diff options
author | Feng Xiao <xfxyjwf@gmail.com> | 2014-11-10 17:34:54 -0800 |
---|---|---|
committer | Feng Xiao <xfxyjwf@gmail.com> | 2014-11-10 17:34:54 -0800 |
commit | 6ef984af4b0c63c1c33127a12dcfc8e6359f0c9e (patch) | |
tree | d17c61ff9f3ae28224fbddac6d26bfc59e2cf755 /src/google/protobuf/compiler/cpp | |
parent | baca1a8a1aa180c42de6278d3b8286c4496c6a10 (diff) |
Down-integrate from internal code base.
Diffstat (limited to 'src/google/protobuf/compiler/cpp')
18 files changed, 1813 insertions, 511 deletions
diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.cc b/src/google/protobuf/compiler/cpp/cpp_enum.cc index c31cb5b3..32d5516e 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum.cc @@ -101,6 +101,15 @@ void EnumGenerator::GenerateDefinition(io::Printer* printer) { } } + if (HasPreservingUnknownEnumSemantics(descriptor_->file())) { + // For new enum semantics: generate min and max sentinel values equal to + // INT32_MIN and INT32_MAX + if (descriptor_->value_count() > 0) printer->Print(",\n"); + printer->Print(vars, + "$classname$_$prefix$INT_MIN_SENTINEL_DO_NOT_USE_ = INT32_MIN,\n" + "$classname$_$prefix$INT_MAX_SENTINEL_DO_NOT_USE_ = INT32_MAX"); + } + printer->Outdent(); printer->Print("\n};\n"); diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc index 20b18ade..17926135 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc @@ -87,9 +87,13 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { " // @@protoc_insertion_point(field_get:$full_name$)\n" " return static_cast< $type$ >($name$_);\n" "}\n" - "inline void $classname$::set_$name$($type$ value) {\n" - " assert($type$_IsValid(value));\n" - " set_has_$name$();\n" + "inline void $classname$::set_$name$($type$ value) {\n"); + if (!HasPreservingUnknownEnumSemantics(descriptor_->file())) { + printer->Print(variables_, + " assert($type$_IsValid(value));\n"); + } + printer->Print(variables_, + " $set_hasbit$\n" " $name$_ = value;\n" " // @@protoc_insertion_point(field_set:$full_name$)\n" "}\n"); @@ -121,21 +125,27 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { "int value;\n" "DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<\n" " int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(\n" - " input, &value)));\n" - "if ($type$_IsValid(value)) {\n" - " set_$name$(static_cast< $type$ >(value));\n"); - if (UseUnknownFieldSet(descriptor_->file())) { + " input, &value)));\n"); + if (HasPreservingUnknownEnumSemantics(descriptor_->file())) { printer->Print(variables_, - "} else {\n" - " mutable_unknown_fields()->AddVarint($number$, value);\n"); + "set_$name$(static_cast< $type$ >(value));\n"); } else { - printer->Print( - "} else {\n" - " unknown_fields_stream.WriteVarint32(tag);\n" - " unknown_fields_stream.WriteVarint32(value);\n"); + printer->Print(variables_, + "if ($type$_IsValid(value)) {\n" + " set_$name$(static_cast< $type$ >(value));\n"); + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print(variables_, + "} else {\n" + " mutable_unknown_fields()->AddVarint($number$, value);\n"); + } else { + printer->Print( + "} else {\n" + " unknown_fields_stream.WriteVarint32(tag);\n" + " unknown_fields_stream.WriteVarint32(value);\n"); + } + printer->Print(variables_, + "}\n"); } - printer->Print(variables_, - "}\n"); } void EnumFieldGenerator:: @@ -174,18 +184,24 @@ void EnumOneofFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer) const { printer->Print(variables_, "inline $type$ $classname$::$name$() const {\n" + " // @@protoc_insertion_point(field_get:$full_name$)\n" " if (has_$name$()) {\n" " return static_cast< $type$ >($oneof_prefix$$name$_);\n" " }\n" " return static_cast< $type$ >($default$);\n" "}\n" - "inline void $classname$::set_$name$($type$ value) {\n" - " assert($type$_IsValid(value));\n" + "inline void $classname$::set_$name$($type$ value) {\n"); + if (!HasPreservingUnknownEnumSemantics(descriptor_->file())) { + printer->Print(variables_, + " assert($type$_IsValid(value));\n"); + } + printer->Print(variables_, " if (!has_$name$()) {\n" " clear_$oneof_name$();\n" " set_has_$name$();\n" " }\n" " $oneof_prefix$$name$_ = value;\n" + " // @@protoc_insertion_point(field_set:$full_name$)\n" "}\n"); } @@ -245,13 +261,21 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { " // @@protoc_insertion_point(field_get:$full_name$)\n" " return static_cast< $type$ >($name$_.Get(index));\n" "}\n" - "inline void $classname$::set_$name$(int index, $type$ value) {\n" - " assert($type$_IsValid(value));\n" + "inline void $classname$::set_$name$(int index, $type$ value) {\n"); + if (!HasPreservingUnknownEnumSemantics(descriptor_->file())) { + printer->Print(variables_, + " assert($type$_IsValid(value));\n"); + } + printer->Print(variables_, " $name$_.Set(index, value);\n" " // @@protoc_insertion_point(field_set:$full_name$)\n" "}\n" - "inline void $classname$::add_$name$($type$ value) {\n" - " assert($type$_IsValid(value));\n" + "inline void $classname$::add_$name$($type$ value) {\n"); + if (!HasPreservingUnknownEnumSemantics(descriptor_->file())) { + printer->Print(variables_, + " assert($type$_IsValid(value));\n"); + } + printer->Print(variables_, " $name$_.Add(value);\n" " // @@protoc_insertion_point(field_add:$full_name$)\n" "}\n"); @@ -280,7 +304,7 @@ GenerateMergingCode(io::Printer* printer) const { void RepeatedEnumFieldGenerator:: GenerateSwappingCode(io::Printer* printer) const { - printer->Print(variables_, "$name$_.Swap(&other->$name$_);\n"); + printer->Print(variables_, "$name$_.UnsafeArenaSwap(&other->$name$_);\n"); } void RepeatedEnumFieldGenerator:: @@ -295,20 +319,26 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { "int value;\n" "DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<\n" " int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(\n" - " input, &value)));\n" - "if ($type$_IsValid(value)) {\n" - " add_$name$(static_cast< $type$ >(value));\n"); - if (UseUnknownFieldSet(descriptor_->file())) { + " input, &value)));\n"); + if (HasPreservingUnknownEnumSemantics(descriptor_->file())) { printer->Print(variables_, - "} else {\n" - " mutable_unknown_fields()->AddVarint($number$, value);\n"); + "add_$name$(static_cast< $type$ >(value));\n"); } else { - printer->Print( - "} else {\n" - " unknown_fields_stream.WriteVarint32(tag);\n" - " unknown_fields_stream.WriteVarint32(value);\n"); + printer->Print(variables_, + "if ($type$_IsValid(value)) {\n" + " add_$name$(static_cast< $type$ >(value));\n"); + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print(variables_, + "} else {\n" + " mutable_unknown_fields()->AddVarint($number$, value);\n"); + } else { + printer->Print( + "} else {\n" + " unknown_fields_stream.WriteVarint32(tag);\n" + " unknown_fields_stream.WriteVarint32(value);\n"); + } + printer->Print("}\n"); } - printer->Print("}\n"); } void RepeatedEnumFieldGenerator:: @@ -318,8 +348,15 @@ GenerateMergeFromCodedStreamWithPacking(io::Printer* printer) const { // rarely be executed. printer->Print(variables_, "DO_((::google::protobuf::internal::WireFormatLite::ReadPackedEnumNoInline(\n" - " input,\n" - " &$type$_IsValid,\n" + " input,\n"); + if (HasPreservingUnknownEnumSemantics(descriptor_->file())) { + printer->Print(variables_, + " NULL,\n"); + } else { + printer->Print(variables_, + " &$type$_IsValid,\n"); + } + printer->Print(variables_, " this->mutable_$name$())));\n"); } else { printer->Print(variables_, @@ -331,10 +368,17 @@ GenerateMergeFromCodedStreamWithPacking(io::Printer* printer) const { " int value;\n" " DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<\n" " int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(\n" - " input, &value)));\n" + " input, &value)));\n"); + if (HasPreservingUnknownEnumSemantics(descriptor_->file())) { + printer->Print(variables_, + " add_$name$(static_cast< $type$ >(value));\n"); + } else { + printer->Print(variables_, " if ($type$_IsValid(value)) {\n" " add_$name$(static_cast< $type$ >(value));\n" - " }\n" + " }\n"); + } + printer->Print(variables_, "}\n" "input->PopLimit(limit);\n"); } diff --git a/src/google/protobuf/compiler/cpp/cpp_field.cc b/src/google/protobuf/compiler/cpp/cpp_field.cc index 1e9a40ac..85838ac3 100644 --- a/src/google/protobuf/compiler/cpp/cpp_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_field.cc @@ -34,6 +34,9 @@ #include <google/protobuf/compiler/cpp/cpp_field.h> #include <memory> +#ifndef _SHARED_PTR_H +#include <google/protobuf/stubs/shared_ptr.h> +#endif #include <google/protobuf/compiler/cpp/cpp_helpers.h> #include <google/protobuf/compiler/cpp/cpp_primitive_field.h> @@ -68,6 +71,20 @@ void SetCommonFieldVariables(const FieldDescriptor* descriptor, ? " PROTOBUF_DEPRECATED" : ""; (*variables)["cppget"] = "Get"; + + if (HasFieldPresence(descriptor->file())) { + (*variables)["set_hasbit"] = + "set_has_" + FieldName(descriptor) + "();"; + (*variables)["clear_hasbit"] = + "clear_has_" + FieldName(descriptor) + "();"; + } else { + (*variables)["set_hasbit"] = ""; + (*variables)["clear_hasbit"] = ""; + } + + // By default, empty string, so that generic code used for both oneofs and + // singular fields can be written. + (*variables)["oneof_prefix"] = ""; } void SetCommonOneofFieldVariables(const FieldDescriptor* descriptor, diff --git a/src/google/protobuf/compiler/cpp/cpp_field.h b/src/google/protobuf/compiler/cpp/cpp_field.h index 0342c35e..088e5063 100644 --- a/src/google/protobuf/compiler/cpp/cpp_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_field.h @@ -37,6 +37,9 @@ #include <map> #include <memory> +#ifndef _SHARED_PTR_H +#include <google/protobuf/stubs/shared_ptr.h> +#endif #include <string> #include <google/protobuf/descriptor.h> @@ -124,6 +127,16 @@ class FieldGenerator { // Most field types don't need this, so the default implementation is empty. virtual void GenerateDestructorCode(io::Printer* /*printer*/) const {} + // Generate a manual destructor invocation for use when the message is on an + // arena. The code that this method generates will be executed inside a + // shared-for-the-whole-message-class method registered with OwnDestructor(). + // The method should return |true| if it generated any code that requires a + // call; this allows the message generator to eliminate the OwnDestructor() + // registration if no fields require it. + virtual bool GenerateArenaDestructorCode(io::Printer* printer) const { + return false; + } + // Generate code that allocates the fields's default instance. virtual void GenerateDefaultInstanceAllocator(io::Printer* /*printer*/) const {} diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc index fa194273..dc8bf613 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.cc +++ b/src/google/protobuf/compiler/cpp/cpp_file.cc @@ -34,6 +34,9 @@ #include <google/protobuf/compiler/cpp/cpp_file.h> #include <memory> +#ifndef _SHARED_PTR_H +#include <google/protobuf/stubs/shared_ptr.h> +#endif #include <set> #include <google/protobuf/compiler/cpp/cpp_enum.h> @@ -102,6 +105,7 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { "#define PROTOBUF_$filename_identifier$__INCLUDED\n" "\n" "#include <string>\n" + "#include <stdint.h>\n" // INT32_MIN, INT32_MAX "\n", "filename", file_->name(), "filename_identifier", filename_identifier); @@ -131,7 +135,10 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { // OK, it's now safe to #include other files. printer->Print( - "#include <google/protobuf/generated_message_util.h>\n"); + "#include <google/protobuf/arena.h>\n" + "#include <google/protobuf/arenastring.h>\n" + "#include <google/protobuf/generated_message_util.h>\n" + "#include <google/protobuf/metadata.h>\n"); if (file_->message_type_count() > 0) { if (HasDescriptorMethods(file_)) { printer->Print( @@ -181,6 +188,7 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { "// @@protoc_insertion_point(includes)\n"); + // Open namespace. GenerateNamespaceOpeners(printer); @@ -189,9 +197,10 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { printer->Print( "\n" "// Internal implementation detail -- do not call these.\n" - "void $dllexport_decl$ $adddescriptorsname$();\n", + "void $dllexport_decl$$adddescriptorsname$();\n", "adddescriptorsname", GlobalAddDescriptorsName(file_->name()), - "dllexport_decl", options_.dllexport_decl); + "dllexport_decl", + options_.dllexport_decl.empty() ? "" : options_.dllexport_decl + " "); printer->Print( // Note that we don't put dllexport_decl on these because they are only @@ -295,7 +304,7 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { } printer->Print( "\n" - "} // namespace google\n} // namespace protobuf\n" + "} // namespace protobuf\n} // namespace google\n" "#endif // SWIG\n"); } diff --git a/src/google/protobuf/compiler/cpp/cpp_file.h b/src/google/protobuf/compiler/cpp/cpp_file.h index 4e4d8b6a..13d06f98 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.h +++ b/src/google/protobuf/compiler/cpp/cpp_file.h @@ -36,6 +36,9 @@ #define GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__ #include <memory> +#ifndef _SHARED_PTR_H +#include <google/protobuf/stubs/shared_ptr.h> +#endif #include <string> #include <vector> #include <google/protobuf/stubs/common.h> diff --git a/src/google/protobuf/compiler/cpp/cpp_generator.cc b/src/google/protobuf/compiler/cpp/cpp_generator.cc index 75d558ea..a2fb7162 100644 --- a/src/google/protobuf/compiler/cpp/cpp_generator.cc +++ b/src/google/protobuf/compiler/cpp/cpp_generator.cc @@ -36,6 +36,9 @@ #include <vector> #include <memory> +#ifndef _SHARED_PTR_H +#include <google/protobuf/stubs/shared_ptr.h> +#endif #include <utility> #include <google/protobuf/compiler/cpp/cpp_file.h> diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.cc b/src/google/protobuf/compiler/cpp/cpp_helpers.cc index b7a47acb..63b0265e 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.cc +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.cc @@ -60,16 +60,17 @@ string DotsToColons(const string& name) { } const char* const kKeywordList[] = { - "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break", "case", - "catch", "char", "class", "compl", "const", "const_cast", "continue", - "default", "delete", "do", "double", "dynamic_cast", "else", "enum", - "explicit", "extern", "false", "float", "for", "friend", "goto", "if", - "inline", "int", "long", "mutable", "namespace", "new", "not", "not_eq", - "operator", "or", "or_eq", "private", "protected", "public", "register", - "reinterpret_cast", "return", "short", "signed", "sizeof", "static", - "static_cast", "struct", "switch", "template", "this", "throw", "true", "try", - "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", - "void", "volatile", "wchar_t", "while", "xor", "xor_eq" + "alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", "bitor", + "bool", "break", "case", "catch", "char", "class", "compl", "const", + "constexpr", "const_cast", "continue", "decltype", "default", "delete", "do", + "double", "dynamic_cast", "else", "enum", "explicit", "extern", "false", + "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable", + "namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", + "or_eq", "private", "protected", "public", "register", "reinterpret_cast", + "return", "short", "signed", "sizeof", "static", "static_assert", + "static_cast", "struct", "switch", "template", "this", "thread_local", + "throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", + "using", "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq" }; hash_set<string> MakeKeywordsMap() { diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.h b/src/google/protobuf/compiler/cpp/cpp_helpers.h index 5d30240c..1cff17c8 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.h +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.h @@ -135,8 +135,15 @@ string SafeFunctionName(const Descriptor* descriptor, const FieldDescriptor* field, const string& prefix); -// Do message classes in this file use UnknownFieldSet? -// Otherwise, messages will store unknown fields in a string +// Returns true if unknown fields are preseved after parsing. +inline bool PreserveUnknownFields(const Descriptor* message) { + return message->file()->syntax() != FileDescriptor::SYNTAX_PROTO3; +} + +// If PreserveUnknownFields() is true, determines whether unknown +// fields will be stored in an UnknownFieldSet or a string. +// If PreserveUnknownFields() is false, this method will not be +// used. inline bool UseUnknownFieldSet(const FileDescriptor* file) { return file->options().optimize_for() != FileOptions::LITE_RUNTIME; } @@ -198,6 +205,28 @@ bool IsStringOrMessage(const FieldDescriptor* field); string UnderscoresToCamelCase(const string& input, bool cap_next_letter); +inline bool HasFieldPresence(const FileDescriptor* file) { + return file->syntax() != FileDescriptor::SYNTAX_PROTO3; +} + +// Returns true if 'enum' semantics are such that unknown values are preserved +// in the enum field itself, rather than going to the UnknownFieldSet. +inline bool HasPreservingUnknownEnumSemantics(const FileDescriptor* file) { + return file->syntax() == FileDescriptor::SYNTAX_PROTO3; +} + +inline bool SupportsArenas(const FileDescriptor* file) { + return true; +} + +inline bool SupportsArenas(const Descriptor* desc) { + return SupportsArenas(desc->file()); +} + +inline bool SupportsArenas(const FieldDescriptor* field) { + return SupportsArenas(field->file()); +} + } // namespace cpp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index 3a9d2639..54a92ae4 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -36,6 +36,9 @@ #include <google/protobuf/stubs/hash.h> #include <map> #include <memory> +#ifndef _SHARED_PTR_H +#include <google/protobuf/stubs/shared_ptr.h> +#endif #include <set> #include <utility> #include <vector> @@ -281,8 +284,67 @@ string MessageTypeProtoName(const FieldDescriptor* field) { return field->message_type()->full_name(); } +// Emits an if-statement with a condition that evaluates to true if |field| is +// considered non-default (will be sent over the wire), for message types +// without true field presence. Should only be called if +// !HasFieldPresence(message_descriptor). +bool EmitFieldNonDefaultCondition(io::Printer* printer, + const string& prefix, + const FieldDescriptor* field) { + // Merge and serialize semantics: primitive fields are merged/serialized only + // if non-zero (numeric) or non-empty (string). + if (!field->is_repeated() && !field->containing_oneof()) { + if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) { + printer->Print( + "if ($prefix$$name$().size() > 0) {\n", + "prefix", prefix, + "name", FieldName(field)); + } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + // Message fields still have has_$name$() methods. + printer->Print( + "if ($prefix$has_$name$()) {\n", + "prefix", prefix, + "name", FieldName(field)); + } else { + printer->Print( + "if ($prefix$$name$() != 0) {\n", + "prefix", prefix, + "name", FieldName(field)); + } + printer->Indent(); + return true; + } else if (field->containing_oneof()) { + printer->Print( + "if (has_$name$()) {\n", + "name", FieldName(field)); + printer->Indent(); + return true; + } + return false; +} + +// Does the given field have a has_$name$() method? +bool HasHasMethod(const FieldDescriptor* field) { + if (HasFieldPresence(field->file())) { + // In proto1/proto2, every field has a has_$name$() method. + return true; + } + // For message types without true field presence, only fields with a message + // type have a has_$name$() method. + return field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE; } +// Does the given field have a private (internal helper only) has_$name$() +// method? +bool HasPrivateHasMethod(const FieldDescriptor* field) { + // Only for oneofs in message types with no field presence. has_$name$(), + // based on the oneof case, is still useful internally for generated code. + return (!HasFieldPresence(field->file()) && + field->containing_oneof() != NULL); +} + +} // anonymous namespace + // =================================================================== MessageGenerator::MessageGenerator(const Descriptor* descriptor, @@ -312,6 +374,13 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor, extension_generators_[i].reset( new ExtensionGenerator(descriptor->extension(i), options)); } + + num_required_fields_ = 0; + for (int i = 0; i < descriptor->field_count(); i++) { + if (descriptor->field(i)->is_required()) { + ++num_required_fields_; + } + } } MessageGenerator::~MessageGenerator() {} @@ -360,8 +429,13 @@ GenerateFieldAccessorDeclarations(io::Printer* printer) { if (field->is_repeated()) { printer->Print(vars, "inline int $name$_size() const$deprecation$;\n"); - } else { + } else if (HasHasMethod(field)) { printer->Print(vars, "inline bool has_$name$() const$deprecation$;\n"); + } else if (HasPrivateHasMethod(field)) { + printer->Print(vars, + "private:\n" + "inline bool has_$name$() const$deprecation$;\n" + "public:\n"); } printer->Print(vars, "inline void clear_$name$()$deprecation$;\n"); @@ -410,32 +484,59 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) { "}\n"); } else if (field->containing_oneof()) { // Singular field in a oneof + // N.B.: Without field presence, we do not use has-bits or generate + // has_$name$() methods, but oneofs still have set_has_$name$(). + // Oneofs also have has_$name$() but only as a private helper + // method, so that generated code is slightly cleaner (vs. comparing + // _oneof_case_[index] against a constant everywhere). vars["field_name"] = UnderscoresToCamelCase(field->name(), true); vars["oneof_name"] = field->containing_oneof()->name(); vars["oneof_index"] = SimpleItoa(field->containing_oneof()->index()); printer->Print(vars, "inline bool $classname$::has_$name$() const {\n" " return $oneof_name$_case() == k$field_name$;\n" - "}\n" + "}\n"); + printer->Print(vars, "inline void $classname$::set_has_$name$() {\n" " _oneof_case_[$oneof_index$] = k$field_name$;\n" "}\n"); } else { // Singular field. - char buffer[kFastToBufferSize]; - vars["has_array_index"] = SimpleItoa(field->index() / 32); - vars["has_mask"] = FastHex32ToBuffer(1u << (field->index() % 32), buffer); - printer->Print(vars, - "inline bool $classname$::has_$name$() const {\n" - " return (_has_bits_[$has_array_index$] & 0x$has_mask$u) != 0;\n" - "}\n" - "inline void $classname$::set_has_$name$() {\n" - " _has_bits_[$has_array_index$] |= 0x$has_mask$u;\n" - "}\n" - "inline void $classname$::clear_has_$name$() {\n" - " _has_bits_[$has_array_index$] &= ~0x$has_mask$u;\n" - "}\n" - ); + if (HasFieldPresence(descriptor_->file())) { + // N.B.: without field presence, we do not use has-bits or generate + // has_$name$() methods. + char buffer[kFastToBufferSize]; + vars["has_array_index"] = SimpleItoa(field->index() / 32); + vars["has_mask"] = FastHex32ToBuffer(1u << (field->index() % 32), + buffer); + printer->Print(vars, + "inline bool $classname$::has_$name$() const {\n" + " return (_has_bits_[$has_array_index$] & 0x$has_mask$u) != 0;\n" + "}\n" + "inline void $classname$::set_has_$name$() {\n" + " _has_bits_[$has_array_index$] |= 0x$has_mask$u;\n" + "}\n" + "inline void $classname$::clear_has_$name$() {\n" + " _has_bits_[$has_array_index$] &= ~0x$has_mask$u;\n" + "}\n" + ); + } else { + // Message fields have a has_$name$() method. + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + bool is_lazy = false; + if (is_lazy) { + printer->Print(vars, + "inline bool $classname$::has_$name$() const {\n" + " return !$name$_.IsCleared();\n" + "}\n"); + } else { + printer->Print(vars, + "inline bool $classname$::has_$name$() const {\n" + " return $name$_ != NULL;\n" + "}\n"); + } + } + } } // Generate clear_$name$() @@ -457,9 +558,11 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) { printer->Print("}\n"); } else { field_generators_.get(field).GenerateClearingCode(printer); - if (!field->is_repeated()) { - printer->Print(vars, - "clear_has_$name$();\n"); + if (HasFieldPresence(descriptor_->file())) { + if (!field->is_repeated()) { + printer->Print(vars, + "clear_has_$name$();\n"); + } } } @@ -553,26 +656,41 @@ GenerateClassDefinition(io::Printer* printer) { "}\n" "\n"); - if (UseUnknownFieldSet(descriptor_->file())) { - printer->Print( - "inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {\n" - " return _unknown_fields_;\n" - "}\n" - "\n" - "inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {\n" - " return &_unknown_fields_;\n" - "}\n" - "\n"); - } else { + if (PreserveUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + "inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {\n" + " return _internal_metadata_.unknown_fields();\n" + "}\n" + "\n" + "inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {\n" + " return _internal_metadata_.mutable_unknown_fields();\n" + "}\n" + "\n"); + } else { + printer->Print( + "inline const ::std::string& unknown_fields() const {\n" + " return _unknown_fields_;\n" + "}\n" + "\n" + "inline ::std::string* mutable_unknown_fields() {\n" + " return &_unknown_fields_;\n" + "}\n" + "\n"); + } + } + + // N.B.: We exclude GetArena() when arena support is disabled, falling back on + // MessageLite's implementation which returns NULL rather than generating our + // own method which returns NULL, in order to reduce code size. + if (SupportsArenas(descriptor_)) { + // virtual method version of GetArenaNoVirtual(), required for generic dispatch given a + // MessageLite* (e.g., in RepeatedField::AddAllocated()). printer->Print( - "inline const ::std::string& unknown_fields() const {\n" - " return _unknown_fields_;\n" - "}\n" - "\n" - "inline ::std::string* mutable_unknown_fields() {\n" - " return &_unknown_fields_;\n" - "}\n" - "\n"); + "inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); }\n" + "inline void* GetMaybeArenaPointer() const {\n" + " return MaybeArenaPtr();\n" + "}\n"); } // Only generate this member if it's not disabled. @@ -628,12 +746,18 @@ GenerateClassDefinition(io::Printer* printer) { } + if (SupportsArenas(descriptor_)) { + printer->Print(vars, + "void UnsafeArenaSwap($classname$* other);\n"); + } printer->Print(vars, "void Swap($classname$* other);\n" "\n" "// implements Message ----------------------------------------------\n" "\n" - "$classname$* New() const;\n"); + "inline $classname$* New() const { return New(NULL); }\n" + "\n" + "$classname$* New(::google::protobuf::Arena* arena) const;\n"); if (HasGeneratedMethods(descriptor_->file())) { if (HasDescriptorMethods(descriptor_->file())) { @@ -699,7 +823,41 @@ GenerateClassDefinition(io::Printer* printer) { "void SharedCtor();\n" "void SharedDtor();\n" "void SetCachedSize(int size) const;\n" - "public:\n"); + "void InternalSwap($classname$* other);\n", + "classname", classname_); + if (SupportsArenas(descriptor_)) { + printer->Print( + "protected:\n" + "explicit $classname$(::google::protobuf::Arena* arena);\n" + "private:\n" + "static void ArenaDtor(void* object);\n" + "inline void RegisterArenaDtor(::google::protobuf::Arena* arena);\n", + "classname", classname_); + } + + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + "private:\n" + "inline ::google::protobuf::Arena* GetArenaNoVirtual() const {\n" + " return _internal_metadata_.arena();\n" + "}\n" + "inline void* MaybeArenaPtr() const {\n" + " return _internal_metadata_.raw_arena_ptr();\n" + "}\n" + "public:\n" + "\n"); + } else { + printer->Print( + "private:\n" + "inline ::google::protobuf::Arena* GetArenaNoVirtual() const {\n" + " return _arena_ptr_;\n" + "}\n" + "inline ::google::protobuf::Arena* MaybeArenaPtr() const {\n" + " return _arena_ptr_;\n" + "}\n" + "public:\n" + "\n"); + } if (HasDescriptorMethods(descriptor_->file())) { printer->Print( @@ -759,10 +917,18 @@ GenerateClassDefinition(io::Printer* printer) { for (int i = 0; i < descriptor_->field_count(); i++) { if (!descriptor_->field(i)->is_repeated()) { - printer->Print( - "inline void set_has_$name$();\n", - "name", FieldName(descriptor_->field(i))); - if (!descriptor_->field(i)->containing_oneof()) { + // set_has_***() generated in all proto1/2 code and in oneofs (only) for + // messages without true field presence. + if (HasFieldPresence(descriptor_->file()) || + descriptor_->field(i)->containing_oneof()) { + printer->Print( + "inline void set_has_$name$();\n", + "name", FieldName(descriptor_->field(i))); + } + // clear_has_***() generated only for non-oneof fields + // in proto1/2. + if (!descriptor_->field(i)->containing_oneof() && + HasFieldPresence(descriptor_->file())) { printer->Print( "inline void clear_has_$name$();\n", "name", FieldName(descriptor_->field(i))); @@ -780,6 +946,14 @@ GenerateClassDefinition(io::Printer* printer) { "oneof_name", descriptor_->oneof_decl(i)->name()); } + if (HasGeneratedMethods(descriptor_->file()) && + !descriptor_->options().message_set_wire_format() && + num_required_fields_ > 1) { + printer->Print( + "// helper for ByteSize()\n" + "int RequiredFieldsByteSizeFallback() const;\n\n"); + } + // Prepare decls for _cached_size_ and _has_bits_. Their position in the // output will be determined later. @@ -816,23 +990,32 @@ GenerateClassDefinition(io::Printer* printer) { if (UseUnknownFieldSet(descriptor_->file())) { printer->Print( - "::google::protobuf::UnknownFieldSet _unknown_fields_;\n" - "\n"); + "::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;\n"); } else { printer->Print( "::std::string _unknown_fields_;\n" + "::google::protobuf::Arena* _arena_ptr_;\n" "\n"); } - // _has_bits_ is frequently accessed, so to reduce code size and improve - // speed, it should be close to the start of the object. But, try not to - // waste space:_has_bits_ by itself always makes sense if its size is a - // multiple of 8, but, otherwise, maybe _has_bits_ and cached_size_ together - // will work well. - printer->Print(has_bits_decl.c_str()); - if ((sizeof_has_bits % 8) != 0) { - printer->Print(cached_size_decl.c_str()); - need_to_emit_cached_size = false; + if (SupportsArenas(descriptor_)) { + printer->Print( + "friend class ::google::protobuf::Arena;\n" + "typedef void InternalArenaConstructable_;\n" + "typedef void DestructorSkippable_;\n"); + } + + if (HasFieldPresence(descriptor_->file())) { + // _has_bits_ is frequently accessed, so to reduce code size and improve + // speed, it should be close to the start of the object. But, try not to + // waste space:_has_bits_ by itself always makes sense if its size is a + // multiple of 8, but, otherwise, maybe _has_bits_ and cached_size_ together + // will work well. + printer->Print(has_bits_decl.c_str()); + if ((sizeof_has_bits % 8) != 0) { + printer->Print(cached_size_decl.c_str()); + need_to_emit_cached_size = false; + } } // Field members: @@ -871,7 +1054,10 @@ GenerateClassDefinition(io::Printer* printer) { // For each oneof generate a union for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { printer->Print( - "union $camel_oneof_name$Union {\n", + "union $camel_oneof_name$Union {\n" + // explicit empty constructor is needed when union contains + // ArenaStringPtr members for string fields. + " $camel_oneof_name$Union() {}\n", "camel_oneof_name", UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true)); printer->Indent(); @@ -1029,10 +1215,28 @@ GenerateDescriptorInitializer(io::Printer* printer, int index) { " new ::google::protobuf::internal::GeneratedMessageReflection(\n" " $classname$_descriptor_,\n" " $classname$::default_instance_,\n" - " $classname$_offsets_,\n" - " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, _has_bits_[0]),\n" + " $classname$_offsets_,\n"); + if (!HasFieldPresence(descriptor_->file())) { + // If we don't have field presence, then _has_bits_ does not exist. + printer->Print(vars, + " -1,\n"); + } else { + printer->Print(vars, + " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, _has_bits_[0]),\n"); + } + + // Unknown field offset: either points to the unknown field set if embedded + // directly, or indicates that the unknown field set is stored as part of the + // internal metadata if not. + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print(vars, + " -1,\n"); + } else { + printer->Print(vars, " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" "$classname$, _unknown_fields_),\n"); + } + if (descriptor_->extension_range_count() > 0) { printer->Print(vars, " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" @@ -1054,8 +1258,23 @@ GenerateDescriptorInitializer(io::Printer* printer, int index) { " ::google::protobuf::DescriptorPool::generated_pool(),\n"); printer->Print(vars, " ::google::protobuf::MessageFactory::generated_factory(),\n"); + printer->Print(vars, - " sizeof($classname$));\n"); + " sizeof($classname$),\n"); + + // Arena offset: either an offset to the metadata struct that contains the + // arena pointer and unknown field set (in a space-efficient way) if we use + // that implementation strategy, or an offset directly to the arena pointer if + // not (because e.g. we don't have an unknown field set). + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print(vars, + " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" + "$classname$, _internal_metadata_));\n"); + } else { + printer->Print(vars, + " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" + "$classname$, _arena_));\n"); + } // Handle nested types. for (int i = 0; i < descriptor_->nested_type_count(); i++) { @@ -1100,7 +1319,7 @@ GenerateDefaultInstanceAllocator(io::Printer* printer) { if ((descriptor_->oneof_decl_count() > 0) && HasDescriptorMethods(descriptor_->file())) { printer->Print( - "$classname$_default_oneof_instance_ = new $classname$OneofInstance;\n", + "$classname$_default_oneof_instance_ = new $classname$OneofInstance();\n", "classname", classname_); } @@ -1312,8 +1531,10 @@ GenerateSharedConstructorCode(io::Printer* printer) { } } - printer->Print( - "::memset(_has_bits_, 0, sizeof(_has_bits_));\n"); + if (HasFieldPresence(descriptor_->file())) { + printer->Print( + "::memset(_has_bits_, 0, sizeof(_has_bits_));\n"); + } for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { printer->Print( @@ -1331,6 +1552,14 @@ GenerateSharedDestructorCode(io::Printer* printer) { "void $classname$::SharedDtor() {\n", "classname", classname_); printer->Indent(); + if (SupportsArenas(descriptor_)) { + // Do nothing when the message is allocated in an arena. + printer->Print( + "if (GetArenaNoVirtual() != NULL) {\n" + " return;\n" + "}\n" + "\n"); + } // Write the destructors for each field except oneof members. for (int i = 0; i < descriptor_->field_count(); i++) { if (!descriptor_->field(i)->containing_oneof()) { @@ -1381,19 +1610,107 @@ GenerateSharedDestructorCode(io::Printer* printer) { } void MessageGenerator:: +GenerateArenaDestructorCode(io::Printer* printer) { + // Generate the ArenaDtor() method. Track whether any fields actually produced + // code that needs to be called. + printer->Print( + "void $classname$::ArenaDtor(void* object) {\n", + "classname", classname_); + printer->Indent(); + + // This code is placed inside a static method, rather than an ordinary one, + // since that simplifies Arena's destructor list (ordinary function pointers + // rather than member function pointers). _this is the object being + // destructed. + printer->Print( + "$classname$* _this = reinterpret_cast< $classname$* >(object);\n" + // avoid an "unused variable" warning in case no fields have dtor code. + "(void)_this;\n", + "classname", classname_); + + bool need_registration = false; + for (int i = 0; i < descriptor_->field_count(); i++) { + if (field_generators_.get(descriptor_->field(i)) + .GenerateArenaDestructorCode(printer)) { + need_registration = true; + } + } + printer->Outdent(); + printer->Print( + "}\n"); + + if (need_registration) { + printer->Print( + "inline void $classname$::RegisterArenaDtor(::google::protobuf::Arena* arena) {\n" + " if (arena != NULL) {" + " arena->OwnCustomDestructor(this, &$classname$::ArenaDtor);\n" + " }\n" + "}\n", + "classname", classname_); + } else { + printer->Print( + "void $classname$::RegisterArenaDtor(::google::protobuf::Arena* arena) {\n" + "}\n", + "classname", classname_); + } +} + +void MessageGenerator:: GenerateStructors(io::Printer* printer) { string superclass = SuperClassName(descriptor_); + string initializer_with_arena; + if (UseUnknownFieldSet(descriptor_->file())) { + initializer_with_arena = "_internal_metadata_(arena)"; + } else { + initializer_with_arena = "_arena_ptr_(arena)"; + } + if (descriptor_->extension_range_count() > 0) { + initializer_with_arena = string("\n _extensions_(arena)") + + (!initializer_with_arena.empty() ? ", " : "") + initializer_with_arena; + } else { + initializer_with_arena = "\n " + initializer_with_arena; + } + + // Initialize member variables with arena constructor. + for (int i = 0; i < descriptor_->field_count(); i++) { + bool has_arena_constructor = descriptor_->field(i)->is_repeated(); + if (has_arena_constructor) { + initializer_with_arena += string(",\n ") + + FieldName(descriptor_->field(i)) + string("_(arena)"); + } + } + initializer_with_arena = superclass + "()" + + (!initializer_with_arena.empty() ? "," : " ") + initializer_with_arena; + + string initializer_null; + initializer_null = (UseUnknownFieldSet(descriptor_->file()) ? + ", _internal_metadata_(NULL) " : ", _arena_ptr_(NULL)"); - // Generate the default constructor. printer->Print( - "$classname$::$classname$()\n" - " : $superclass$() {\n" - " SharedCtor();\n" - " // @@protoc_insertion_point(constructor:$full_name$)\n" - "}\n", - "classname", classname_, - "superclass", superclass, - "full_name", descriptor_->full_name()); + "$classname$::$classname$()\n" + " : $superclass$() $initializer$ {\n" + " SharedCtor();\n" + " // @@protoc_insertion_point(constructor:$full_name$)\n" + "}\n", + "classname", classname_, + "superclass", superclass, + "full_name", descriptor_->full_name(), + "initializer", initializer_null); + + if (SupportsArenas(descriptor_)) { + printer->Print( + "\n" + "$classname$::$classname$(::google::protobuf::Arena* arena)\n" + " : $initializer$ {\n" + " SharedCtor();\n" + " RegisterArenaDtor(arena);\n" + " // @@protoc_insertion_point(arena_constructor:$full_name$)\n" + "}\n", + "initializer", initializer_with_arena, + "classname", classname_, + "superclass", superclass, + "full_name", descriptor_->full_name()); + } printer->Print( "\n" @@ -1441,7 +1758,17 @@ GenerateStructors(io::Printer* printer) { // Generate the copy constructor. printer->Print( "$classname$::$classname$(const $classname$& from)\n" - " : $superclass$() {\n" + " : $superclass$()", + "classname", classname_, + "superclass", superclass, + "full_name", descriptor_->full_name()); + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + ",\n _internal_metadata_(NULL) {\n"); + } else if (!UseUnknownFieldSet(descriptor_->file())) { + printer->Print(",\n _arena_ptr_(NULL) {\n"); + } + printer->Print( " SharedCtor();\n" " MergeFrom(from);\n" " // @@protoc_insertion_point(copy_constructor:$full_name$)\n" @@ -1467,6 +1794,11 @@ GenerateStructors(io::Printer* printer) { // Generate the shared destructor code. GenerateSharedDestructorCode(printer); + // Generate the arena-specific destructor code. + if (SupportsArenas(descriptor_)) { + GenerateArenaDestructorCode(printer); + } + // Generate SetCachedSize. printer->Print( "void $classname$::SetCachedSize(int size) const {\n" @@ -1512,11 +1844,23 @@ GenerateStructors(io::Printer* printer) { "\n", "classname", classname_); - printer->Print( - "$classname$* $classname$::New() const {\n" - " return new $classname$;\n" - "}\n", - "classname", classname_); + if (SupportsArenas(descriptor_)) { + printer->Print( + "$classname$* $classname$::New(::google::protobuf::Arena* arena) const {\n" + " return ::google::protobuf::Arena::CreateMessage<$classname$>(arena);\n" + "}\n", + "classname", classname_); + } else { + printer->Print( + "$classname$* $classname$::New(::google::protobuf::Arena* arena) const {\n" + " $classname$* n = new $classname$;\n" + " if (arena != NULL) {\n" + " arena->Own(n);\n" + " }\n" + " return n;\n" + "}\n", + "classname", classname_); + } } @@ -1616,16 +1960,19 @@ GenerateClear(io::Printer* printer) { const string& memsets = memsets_for_chunk[i / 8]; uint32 mask = fields_mask_for_chunk[i / 8]; int count = popcnt(mask); + GOOGLE_DCHECK_GE(count, 1); if (count == 1 || (count <= 4 && count == memset_field_count_for_chunk[i / 8])) { // No "if" here because the chunk is trivial. } else { - printer->Print( - "if (_has_bits_[$index$ / 32] & $mask$) {\n", - "index", SimpleItoa(i / 8 * 8), - "mask", SimpleItoa(mask)); - printer->Indent(); - chunk_block_in_progress = true; + if (HasFieldPresence(descriptor_->file())) { + printer->Print( + "if (_has_bits_[$index$ / 32] & $mask$) {\n", + "index", SimpleItoa(i / 8 * 8), + "mask", SimpleItoa(mask)); + printer->Indent(); + chunk_block_in_progress = true; + } } printer->Print(memsets.c_str()); } @@ -1639,14 +1986,18 @@ GenerateClear(io::Printer* printer) { field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE || field->cpp_type() == FieldDescriptor::CPPTYPE_STRING; - if (should_check_bit) { + bool have_enclosing_if = false; + if (should_check_bit && + // If no field presence, then always clear strings/messages as well. + HasFieldPresence(descriptor_->file())) { printer->Print("if (has_$name$()) {\n", "name", fieldname); printer->Indent(); + have_enclosing_if = true; } field_generators_.get(field).GenerateClearingCode(printer); - if (should_check_bit) { + if (have_enclosing_if) { printer->Outdent(); printer->Print("}\n"); } @@ -1678,16 +2029,22 @@ GenerateClear(io::Printer* printer) { "oneof_name", descriptor_->oneof_decl(i)->name()); } - // Step 5: Everything else. - printer->Print( - "::memset(_has_bits_, 0, sizeof(_has_bits_));\n"); - - if (UseUnknownFieldSet(descriptor_->file())) { + if (HasFieldPresence(descriptor_->file())) { + // Step 5: Everything else. printer->Print( - "mutable_unknown_fields()->Clear();\n"); - } else { - printer->Print( - "mutable_unknown_fields()->clear();\n"); + "::memset(_has_bits_, 0, sizeof(_has_bits_));\n"); + } + + if (PreserveUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + "if (_internal_metadata_.have_unknown_fields()) {\n" + " mutable_unknown_fields()->Clear();\n" + "}\n"); + } else { + printer->Print( + "mutable_unknown_fields()->clear();\n"); + } } printer->Outdent(); @@ -1748,12 +2105,42 @@ GenerateOneofClear(io::Printer* printer) { void MessageGenerator:: GenerateSwap(io::Printer* printer) { - // Generate the Swap member function. - printer->Print("void $classname$::Swap($classname$* other) {\n", + if (SupportsArenas(descriptor_)) { + // Generate the Swap member function. This is a lightweight wrapper around + // UnsafeArenaSwap() / MergeFrom() with temporaries, depending on the memory + // ownership situation: swapping across arenas or between an arena and a + // heap requires copying. + printer->Print( + "void $classname$::Swap($classname$* other) {\n" + " if (other == this) return;\n" + " if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) {\n" + " InternalSwap(other);\n" + " } else {\n" + " $classname$ temp;\n" + " temp.MergeFrom(*this);\n" + " CopyFrom(*other);\n" + " other->CopyFrom(temp);\n" + " }\n" + "}\n" + "void $classname$::UnsafeArenaSwap($classname$* other) {\n" + " if (other == this) return;\n" + " GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual());\n" + " InternalSwap(other);\n" + "}\n", + "classname", classname_); + } else { + printer->Print( + "void $classname$::Swap($classname$* other) {\n" + " if (other == this) return;\n" + " InternalSwap(other);\n" + "}\n", + "classname", classname_); + } + + // Generate the UnsafeArenaSwap member function. + printer->Print("void $classname$::InternalSwap($classname$* other) {\n", "classname", classname_); printer->Indent(); - printer->Print("if (other != this) {\n"); - printer->Indent(); if (HasGeneratedMethods(descriptor_->file())) { for (int i = 0; i < descriptor_->field_count(); i++) { @@ -1769,15 +2156,25 @@ GenerateSwap(io::Printer* printer) { "i", SimpleItoa(i)); } - for (int i = 0; i < (descriptor_->field_count() + 31) / 32; ++i) { - printer->Print("std::swap(_has_bits_[$i$], other->_has_bits_[$i$]);\n", - "i", SimpleItoa(i)); + if (HasFieldPresence(descriptor_->file())) { + for (int i = 0; i < (descriptor_->field_count() + 31) / 32; ++i) { + printer->Print("std::swap(_has_bits_[$i$], other->_has_bits_[$i$]);\n", + "i", SimpleItoa(i)); + } } - if (UseUnknownFieldSet(descriptor_->file())) { - printer->Print("_unknown_fields_.Swap(&other->_unknown_fields_);\n"); + if (PreserveUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + "_internal_metadata_.Swap(&other->_internal_metadata_);\n"); + } else { + printer->Print("_unknown_fields_.swap(other->_unknown_fields_);\n"); + } } else { - printer->Print("_unknown_fields_.swap(other->_unknown_fields_);\n"); + // Still swap internal_metadata as it may contain more than just + // unknown fields. + printer->Print( + "_internal_metadata_.Swap(&other->_internal_metadata_);\n"); } printer->Print("std::swap(_cached_size_, other->_cached_size_);\n"); if (descriptor_->extension_range_count() > 0) { @@ -1789,8 +2186,6 @@ GenerateSwap(io::Printer* printer) { printer->Outdent(); printer->Print("}\n"); - printer->Outdent(); - printer->Print("}\n"); } void MessageGenerator:: @@ -1886,33 +2281,48 @@ GenerateMergeFrom(io::Printer* printer) { const FieldDescriptor* field = descriptor_->field(i); if (!field->is_repeated() && !field->containing_oneof()) { - // See above in GenerateClear for an explanation of this. - if (i / 8 != last_index / 8 || last_index < 0) { - if (last_index >= 0) { - printer->Outdent(); - printer->Print("}\n"); + if (HasFieldPresence(descriptor_->file())) { + // See above in GenerateClear for an explanation of this. + if (i / 8 != last_index / 8 || last_index < 0) { + if (last_index >= 0) { + printer->Outdent(); + printer->Print("}\n"); + } + printer->Print( + "if (from._has_bits_[$index$ / 32] & " + "(0xffu << ($index$ % 32))) {\n", + "index", SimpleItoa(field->index())); + printer->Indent(); } - printer->Print( - "if (from._has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n", - "index", SimpleItoa(field->index())); - printer->Indent(); } last_index = i; - printer->Print( - "if (from.has_$name$()) {\n", - "name", FieldName(field)); - printer->Indent(); + bool have_enclosing_if = false; + if (HasFieldPresence(descriptor_->file())) { + printer->Print( + "if (from.has_$name$()) {\n", + "name", FieldName(field)); + printer->Indent(); + have_enclosing_if = true; + } else { + // Merge semantics without true field presence: primitive fields are + // merged only if non-zero (numeric) or non-empty (string). + have_enclosing_if = EmitFieldNonDefaultCondition( + printer, "from.", field); + } field_generators_.get(field).GenerateMergingCode(printer); - printer->Outdent(); - printer->Print("}\n"); + if (have_enclosing_if) { + printer->Outdent(); + printer->Print("}\n"); + } } } - if (last_index >= 0) { + if (HasFieldPresence(descriptor_->file()) && + last_index >= 0) { printer->Outdent(); printer->Print("}\n"); } @@ -1921,12 +2331,16 @@ GenerateMergeFrom(io::Printer* printer) { printer->Print("_extensions_.MergeFrom(from._extensions_);\n"); } - if (UseUnknownFieldSet(descriptor_->file())) { - printer->Print( - "mutable_unknown_fields()->MergeFrom(from.unknown_fields());\n"); - } else { - printer->Print( - "mutable_unknown_fields()->append(from.unknown_fields());\n"); + if (PreserveUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + "if (from._internal_metadata_.have_unknown_fields()) {\n" + " mutable_unknown_fields()->MergeFrom(from.unknown_fields());\n" + "}\n"); + } else { + printer->Print( + "mutable_unknown_fields()->append(from.unknown_fields());\n"); + } } printer->Outdent(); @@ -2171,24 +2585,33 @@ GenerateMergeFromCodedStream(io::Printer* printer) { } } printer->Print(") {\n"); - if (UseUnknownFieldSet(descriptor_->file())) { - PrintHandlingOptionalStaticInitializers( - descriptor_->file(), printer, - // With static initializers. - " DO_(_extensions_.ParseField(tag, input, default_instance_,\n" - " mutable_unknown_fields()));\n", - // Without. - " DO_(_extensions_.ParseField(tag, input, &default_instance(),\n" - " mutable_unknown_fields()));\n"); + if (PreserveUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_->file())) { + PrintHandlingOptionalStaticInitializers( + descriptor_->file(), printer, + // With static initializers. + " DO_(_extensions_.ParseField(tag, input, default_instance_,\n" + " mutable_unknown_fields()));\n", + // Without. + " DO_(_extensions_.ParseField(tag, input, &default_instance(),\n" + " mutable_unknown_fields()));\n"); + } else { + PrintHandlingOptionalStaticInitializers( + descriptor_->file(), printer, + // With static initializers. + " DO_(_extensions_.ParseField(tag, input, default_instance_,\n" + " &unknown_fields_stream));\n", + // Without. + " DO_(_extensions_.ParseField(tag, input, &default_instance(),\n" + " &unknown_fields_stream));\n"); + } } else { PrintHandlingOptionalStaticInitializers( descriptor_->file(), printer, // With static initializers. - " DO_(_extensions_.ParseField(tag, input, default_instance_,\n" - " &unknown_fields_stream));\n", + " DO_(_extensions_.ParseField(tag, input, default_instance_);\n", // Without. - " DO_(_extensions_.ParseField(tag, input, &default_instance(),\n" - " &unknown_fields_stream));\n"); + " DO_(_extensions_.ParseField(tag, input, &default_instance());\n"); } printer->Print( " continue;\n" @@ -2196,14 +2619,19 @@ GenerateMergeFromCodedStream(io::Printer* printer) { } // We really don't recognize this tag. Skip it. - if (UseUnknownFieldSet(descriptor_->file())) { - printer->Print( - "DO_(::google::protobuf::internal::WireFormat::SkipField(\n" - " input, tag, mutable_unknown_fields()));\n"); + if (PreserveUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + "DO_(::google::protobuf::internal::WireFormat::SkipField(\n" + " input, tag, mutable_unknown_fields()));\n"); + } else { + printer->Print( + "DO_(::google::protobuf::internal::WireFormatLite::SkipField(\n" + " input, tag, &unknown_fields_stream));\n"); + } } else { printer->Print( - "DO_(::google::protobuf::internal::WireFormatLite::SkipField(\n" - " input, tag, &unknown_fields_stream));\n"); + "DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag));\n"); } if (descriptor_->field_count() > 0) { @@ -2232,11 +2660,15 @@ void MessageGenerator::GenerateSerializeOneField( io::Printer* printer, const FieldDescriptor* field, bool to_array) { PrintFieldComment(printer, field); - if (!field->is_repeated()) { + bool have_enclosing_if = false; + if (!field->is_repeated() && HasFieldPresence(descriptor_->file())) { printer->Print( "if (has_$name$()) {\n", "name", FieldName(field)); printer->Indent(); + have_enclosing_if = true; + } else if (!HasFieldPresence(descriptor_->file())) { + have_enclosing_if = EmitFieldNonDefaultCondition(printer, "this->", field); } if (to_array) { @@ -2246,7 +2678,7 @@ void MessageGenerator::GenerateSerializeOneField( field_generators_.get(field).GenerateSerializeWithCachedSizes(printer); } - if (!field->is_repeated()) { + if (have_enclosing_if) { printer->Outdent(); printer->Print("}\n"); } @@ -2386,28 +2818,69 @@ GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) { } } - if (UseUnknownFieldSet(descriptor_->file())) { - printer->Print("if (!unknown_fields().empty()) {\n"); - printer->Indent(); - if (to_array) { + if (PreserveUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print("if (_internal_metadata_.have_unknown_fields()) {\n"); + printer->Indent(); + if (to_array) { + printer->Print( + "target = " + "::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(\n" + " unknown_fields(), target);\n"); + } else { + printer->Print( + "::google::protobuf::internal::WireFormat::SerializeUnknownFields(\n" + " unknown_fields(), output);\n"); + } + printer->Outdent(); + printer->Print( - "target = " - "::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(\n" - " unknown_fields(), target);\n"); + "}\n"); } else { printer->Print( - "::google::protobuf::internal::WireFormat::SerializeUnknownFields(\n" - " unknown_fields(), output);\n"); + "output->WriteRaw(unknown_fields().data(),\n" + " unknown_fields().size());\n"); } - printer->Outdent(); + } +} - printer->Print( - "}\n"); - } else { - printer->Print( - "output->WriteRaw(unknown_fields().data(),\n" - " unknown_fields().size());\n"); +static vector<uint32> RequiredFieldsBitMask(const Descriptor* desc) { + vector<uint32> result; + uint32 mask = 0; + for (int i = 0; i < desc->field_count(); i++) { + if (i > 0 && i % 32 == 0) { + result.push_back(mask); + mask = 0; + } + if (desc->field(i)->is_required()) { + mask |= (1 << (i & 31)); + } + } + if (mask != 0) { + result.push_back(mask); } + return result; +} + +// Create an expression that evaluates to +// "for all i, (_has_bits_[i] & masks[i]) == masks[i]" +// masks is allowed to be shorter than _has_bits_, but at least one element of +// masks must be non-zero. +static string ConditionalToCheckBitmasks(const vector<uint32>& masks) { + vector<string> parts; + for (int i = 0; i < masks.size(); i++) { + if (masks[i] == 0) continue; + char buffer[kFastToBufferSize]; + FastHex32ToBuffer(masks[i], buffer); + string m = StrCat("0x", buffer); + // Each xor evaluates to 0 if the expected bits are present. + parts.push_back(StrCat("((_has_bits_[", i, "] & ", m, ") ^ ", m, ")")); + } + GOOGLE_CHECK(!parts.empty()); + // If we have multiple parts, each expected to be 0, then bitwise-or them. + string result = parts.size() == 1 ? parts[0] : + StrCat("(", Join(parts, "\n | "), ")"); + return result + " == 0"; } void MessageGenerator:: @@ -2420,8 +2893,10 @@ GenerateByteSize(io::Printer* printer) { "classname", classname_); GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file())); printer->Print( + "if (_internal_metadata_.have_unknown_fields()) {\n" " total_size += ::google::protobuf::internal::WireFormat::\n" - " ComputeUnknownMessageSetItemsSize(unknown_fields());\n"); + " ComputeUnknownMessageSetItemsSize(unknown_fields());\n" + "}\n"); printer->Print( " GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();\n" " _cached_size_ = total_size;\n" @@ -2431,6 +2906,33 @@ GenerateByteSize(io::Printer* printer) { return; } + if (num_required_fields_ > 1 && HasFieldPresence(descriptor_->file())) { + // Emit a function (rarely used, we hope) that handles the required fields + // by checking for each one individually. + printer->Print( + "int $classname$::RequiredFieldsByteSizeFallback() const {\n", + "classname", classname_); + printer->Indent(); + printer->Print("int total_size = 0;\n"); + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (field->is_required()) { + printer->Print("\n" + "if (has_$name$()) {\n", + "name", FieldName(field)); + printer->Indent(); + PrintFieldComment(printer, field); + field_generators_.get(field).GenerateByteSize(printer); + printer->Outdent(); + printer->Print("}\n"); + } + } + printer->Print("\n" + "return total_size;\n"); + printer->Outdent(); + printer->Print("}\n"); + } + printer->Print( "int $classname$::ByteSize() const {\n", "classname", classname_); @@ -2439,45 +2941,121 @@ GenerateByteSize(io::Printer* printer) { "int total_size = 0;\n" "\n"); - int last_index = -1; + // Handle required fields (if any). We expect all of them to be + // present, so emit one conditional that checks for that. If they are all + // present then the fast path executes; otherwise the slow path executes. + if (num_required_fields_ > 1 && HasFieldPresence(descriptor_->file())) { + // The fast path works if all required fields are present. + vector<uint32> masks_for_has_bits = RequiredFieldsBitMask(descriptor_); + printer->Print((string("if (") + + ConditionalToCheckBitmasks(masks_for_has_bits) + + ") { // All required fields are present.\n").c_str()); + printer->Indent(); + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (!field->is_required()) continue; + PrintFieldComment(printer, field); + field_generators_.get(field).GenerateByteSize(printer); + printer->Print("\n"); + } + printer->Outdent(); + printer->Print("} else {\n" // the slow path + " total_size += RequiredFieldsByteSizeFallback();\n" + "}\n"); + } else { + // num_required_fields_ <= 1: no need to be tricky + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (!field->is_required()) continue; + PrintFieldComment(printer, field); + printer->Print("if (has_$name$()) {\n", + "name", FieldName(field)); + printer->Indent(); + field_generators_.get(field).GenerateByteSize(printer); + printer->Outdent(); + printer->Print("}\n"); + } + } + // Handle optional fields (worry below about repeateds, oneofs, etc.). + // These are handled in chunks of 8. The first chunk is + // the non-requireds-non-repeateds-non-unions-non-extensions in + // descriptor_->field(0), descriptor_->field(1), ... descriptor_->field(7), + // and the second chunk is the same for + // descriptor_->field(8), descriptor_->field(9), ... descriptor_->field(15), + // etc. + hash_map<int, uint32> fields_mask_for_chunk; for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); + if (!field->is_required() && !field->is_repeated() && + !field->containing_oneof()) { + fields_mask_for_chunk[i / 8] |= static_cast<uint32>(1) << (i % 32); + } + } - if (!field->is_repeated() && !field->containing_oneof()) { + int last_index = -1; + bool chunk_block_in_progress = false; + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (!field->is_required() && !field->is_repeated() && + !field->containing_oneof()) { // See above in GenerateClear for an explanation of this. // TODO(kenton): Share code? Unclear how to do so without // over-engineering. - if ((i / 8) != (last_index / 8) || - last_index < 0) { - if (last_index >= 0) { + if (i / 8 != last_index / 8 || last_index < 0) { + // End previous chunk, if there was one. + if (chunk_block_in_progress) { printer->Outdent(); printer->Print("}\n"); + chunk_block_in_progress = false; + } + // Start chunk. + uint32 mask = fields_mask_for_chunk[i / 8]; + int count = popcnt(mask); + GOOGLE_DCHECK_GE(count, 1); + if (count == 1) { + // No "if" here because the chunk is trivial. + } else { + if (HasFieldPresence(descriptor_->file())) { + printer->Print( + "if (_has_bits_[$index$ / 32] & $mask$) {\n", + "index", SimpleItoa(i), + "mask", SimpleItoa(mask)); + printer->Indent(); + chunk_block_in_progress = true; + } } - printer->Print( - "if (_has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n", - "index", SimpleItoa(field->index())); - printer->Indent(); } last_index = i; PrintFieldComment(printer, field); - printer->Print( - "if (has_$name$()) {\n", - "name", FieldName(field)); - printer->Indent(); + bool have_enclosing_if = false; + if (HasFieldPresence(descriptor_->file())) { + printer->Print( + "if (has_$name$()) {\n", + "name", FieldName(field)); + printer->Indent(); + have_enclosing_if = true; + } else { + // Without field presence: field is serialized only if it has a + // non-default value. + have_enclosing_if = EmitFieldNonDefaultCondition( + printer, "this->", field); + } field_generators_.get(field).GenerateByteSize(printer); - printer->Outdent(); - printer->Print( - "}\n" - "\n"); + if (have_enclosing_if) { + printer->Outdent(); + printer->Print( + "}\n" + "\n"); + } } } - if (last_index >= 0) { + if (chunk_block_in_progress) { printer->Outdent(); printer->Print("}\n"); } @@ -2532,19 +3110,19 @@ GenerateByteSize(io::Printer* printer) { "\n"); } - if (UseUnknownFieldSet(descriptor_->file())) { - printer->Print("if (!unknown_fields().empty()) {\n"); - printer->Indent(); - printer->Print( - "total_size +=\n" - " ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(\n" - " unknown_fields());\n"); - printer->Outdent(); - printer->Print("}\n"); - } else { - printer->Print( - "total_size += unknown_fields().size();\n" - "\n"); + if (PreserveUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + "if (_internal_metadata_.have_unknown_fields()) {\n" + " total_size +=\n" + " ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(\n" + " unknown_fields());\n" + "}\n"); + } else { + printer->Print( + "total_size += unknown_fields().size();\n" + "\n"); + } } // We update _cached_size_ even though this is a const method. In theory, @@ -2569,27 +3147,29 @@ GenerateIsInitialized(io::Printer* printer) { "classname", classname_); printer->Indent(); - // Check that all required fields in this message are set. We can do this - // most efficiently by checking 32 "has bits" at a time. - int has_bits_array_size = (descriptor_->field_count() + 31) / 32; - for (int i = 0; i < has_bits_array_size; i++) { - uint32 mask = 0; - for (int bit = 0; bit < 32; bit++) { - int index = i * 32 + bit; - if (index >= descriptor_->field_count()) break; - const FieldDescriptor* field = descriptor_->field(index); - - if (field->is_required()) { - mask |= 1 << bit; + if (HasFieldPresence(descriptor_->file())) { + // Check that all required fields in this message are set. We can do this + // most efficiently by checking 32 "has bits" at a time. + int has_bits_array_size = (descriptor_->field_count() + 31) / 32; + for (int i = 0; i < has_bits_array_size; i++) { + uint32 mask = 0; + for (int bit = 0; bit < 32; bit++) { + int index = i * 32 + bit; + if (index >= descriptor_->field_count()) break; + const FieldDescriptor* field = descriptor_->field(index); + + if (field->is_required()) { + mask |= 1 << bit; + } } - } - if (mask != 0) { - char buffer[kFastToBufferSize]; - printer->Print( - "if ((_has_bits_[$i$] & 0x$mask$) != 0x$mask$) return false;\n", - "i", SimpleItoa(i), - "mask", FastHex32ToBuffer(mask, buffer)); + if (mask != 0) { + char buffer[kFastToBufferSize]; + printer->Print( + "if ((_has_bits_[$i$] & 0x$mask$) != 0x$mask$) return false;\n", + "i", SimpleItoa(i), + "mask", FastHex32ToBuffer(mask, buffer)); + } } } @@ -2607,7 +3187,7 @@ GenerateIsInitialized(io::Printer* printer) { "name", FieldName(field)); } else { if (field->options().weak()) { - // For weak fields, use the data member (google::protobuf::Message*) instead + // For weak fields, use the data member (::google::protobuf::Message*) instead // of the getter to avoid a link dependency on the weak message type // which is only forward declared. printer->Print( diff --git a/src/google/protobuf/compiler/cpp/cpp_message.h b/src/google/protobuf/compiler/cpp/cpp_message.h index bfd3cec1..a781c234 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.h +++ b/src/google/protobuf/compiler/cpp/cpp_message.h @@ -36,6 +36,9 @@ #define GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__ #include <memory> +#ifndef _SHARED_PTR_H +#include <google/protobuf/stubs/shared_ptr.h> +#endif #include <string> #include <vector> #include <google/protobuf/compiler/cpp/cpp_field.h> @@ -58,8 +61,7 @@ class ExtensionGenerator; // extension.h class MessageGenerator { public: // See generator.cc for the meaning of dllexport_decl. - explicit MessageGenerator(const Descriptor* descriptor, - const Options& options); + MessageGenerator(const Descriptor* descriptor, const Options& options); ~MessageGenerator(); // Header stuff. @@ -130,6 +132,8 @@ class MessageGenerator { void GenerateSharedConstructorCode(io::Printer* printer); // Generate the shared destructor code. void GenerateSharedDestructorCode(io::Printer* printer); + // Generate the arena-specific destructor code. + void GenerateArenaDestructorCode(io::Printer* printer); // Generate standard Message methods. void GenerateClear(io::Printer* printer); @@ -162,6 +166,7 @@ class MessageGenerator { scoped_array<scoped_ptr<MessageGenerator> > nested_generators_; scoped_array<scoped_ptr<EnumGenerator> > enum_generators_; scoped_array<scoped_ptr<ExtensionGenerator> > extension_generators_; + int num_required_fields_; bool uses_string_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator); diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.cc b/src/google/protobuf/compiler/cpp/cpp_message_field.cc index 6ac15a5a..da1ec60b 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.cc @@ -86,6 +86,12 @@ GenerateAccessorDeclarations(io::Printer* printer) const { "inline $type$* mutable_$name$()$deprecation$;\n" "inline $type$* $release_name$()$deprecation$;\n" "inline void set_allocated_$name$($type$* $name$)$deprecation$;\n"); + if (SupportsArenas(descriptor_)) { + printer->Print(variables_, + "inline $type$* unsafe_arena_release_$name$()$deprecation$;\n" + "inline void unsafe_arena_set_allocated_$name$(\n" + " $type$* $name$)$deprecation$;\n"); + } } void MessageFieldGenerator:: @@ -101,36 +107,157 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { // Without. " return $name$_ != NULL ? *$name$_ : *default_instance().$name$_;\n"); - printer->Print(variables_, - "}\n" - "inline $type$* $classname$::mutable_$name$() {\n" - " set_has_$name$();\n" - " if ($name$_ == NULL) $name$_ = new $type$;\n" - " // @@protoc_insertion_point(field_mutable:$full_name$)\n" - " return $name$_;\n" - "}\n" - "inline $type$* $classname$::$release_name$() {\n" - " clear_has_$name$();\n" - " $type$* temp = $name$_;\n" - " $name$_ = NULL;\n" - " return temp;\n" - "}\n" - "inline void $classname$::set_allocated_$name$($type$* $name$) {\n" - " delete $name$_;\n" - " $name$_ = $name$;\n" - " if ($name$) {\n" - " set_has_$name$();\n" - " } else {\n" - " clear_has_$name$();\n" - " }\n" - " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" - "}\n"); + if (SupportsArenas(descriptor_)) { + printer->Print(variables_, + "}\n" + "inline $type$* $classname$::mutable_$name$() {\n" + " $set_hasbit$\n" + " if ($name$_ == NULL) {\n"); + if (SupportsArenas(descriptor_->message_type())) { + printer->Print(variables_, + " $name$_ = ::google::protobuf::Arena::CreateMessage< $type$ >(\n" + " GetArenaNoVirtual());\n"); + } else { + printer->Print(variables_, + " $name$_ = ::google::protobuf::Arena::Create< $type$ >(\n" + " GetArenaNoVirtual());\n"); + } + printer->Print(variables_, " }\n" + " // @@protoc_insertion_point(field_mutable:$full_name$)\n" + " return $name$_;\n" + "}\n" + "inline $type$* $classname$::$release_name$() {\n" + " $clear_hasbit$\n" + " if (GetArenaNoVirtual() != NULL) {\n" + " if ($name$_ == NULL) {\n" + " return NULL;\n" + " } else {\n" + " $type$* temp = new $type$;\n" + " temp->MergeFrom(*$name$_);\n" + " $name$_ = NULL;\n" + " return temp;\n" + " }\n" + " } else {\n" + " $type$* temp = $name$_;\n" + " $name$_ = NULL;\n" + " return temp;\n" + " }\n" + "}\n" + "inline $type$* $classname$::unsafe_arena_release_$name$() {\n" + " $clear_hasbit$\n" + " $type$* temp = $name$_;\n" + " $name$_ = NULL;\n" + " return temp;\n" + "}\n" + "inline void $classname$::set_allocated_$name$($type$* $name$) {\n" + " if (GetArenaNoVirtual() == NULL) {\n" + " delete $name$_;\n" + " }\n" + " if ($name$ != NULL) {\n"); + if (SupportsArenas(descriptor_->message_type())) { + // If we're on an arena and the incoming message is not, simply Own() it + // rather than copy to the arena -- either way we need a heap dealloc, + // so we might as well defer it. Otherwise, if incoming message is on a + // different ownership domain (specific arena, or the heap) than we are, + // copy to our arena (or heap, as the case may be). + printer->Print(variables_, + " if (GetArenaNoVirtual() != NULL && \n" + " ::google::protobuf::Arena::GetArena($name$) == NULL) {\n" + " GetArenaNoVirtual()->Own($name$);\n" + " } else if (GetArenaNoVirtual() !=\n" + " ::google::protobuf::Arena::GetArena($name$)) {\n" + " $type$* new_$name$ = \n" + " ::google::protobuf::Arena::CreateMessage< $type$ >(\n" + " GetArenaNoVirtual());\n" + " new_$name$->CopyFrom(*$name$);\n" + " $name$ = new_$name$;\n" + " }\n"); + } else { + printer->Print(variables_, + " if (GetArenaNoVirtual() != NULL) {\n" + " GetArenaNoVirtual()->Own($name$);\n" + " }\n"); + } + + printer->Print(variables_, + " }\n" + " $name$_ = $name$;\n" + " if ($name$) {\n" + " $set_hasbit$\n" + " } else {\n" + " $clear_hasbit$\n" + " }\n" + " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" + "}\n" + "inline void $classname$::unsafe_arena_set_allocated_$name$(\n" + " $type$* $name$) {\n" + // If we're not on an arena, free whatever we were holding before. + // (If we are on arena, we can just forget the earlier pointer.) + " if (GetArenaNoVirtual() == NULL) {\n" + " delete $name$_;\n" + " }\n" + " $name$_ = $name$;\n" + " if ($name$) {\n" + " $set_hasbit$\n" + " } else {\n" + " $clear_hasbit$\n" + " }\n" + " // @@protoc_insertion_point(field_unsafe_arena_set_allocated" + ":$full_name$)\n" + "}\n"); + } else { + printer->Print(variables_, + "}\n" + "inline $type$* $classname$::mutable_$name$() {\n" + " $set_hasbit$\n" + " if ($name$_ == NULL) {\n" + " $name$_ = new $type$;\n" + " }\n" + " // @@protoc_insertion_point(field_mutable:$full_name$)\n" + " return $name$_;\n" + "}\n" + "inline $type$* $classname$::$release_name$() {\n" + " $clear_hasbit$\n" + " $type$* temp = $name$_;\n" + " $name$_ = NULL;\n" + " return temp;\n" + "}\n" + "inline void $classname$::set_allocated_$name$($type$* $name$) {\n" + " delete $name$_;\n"); + + if (SupportsArenas(descriptor_->message_type())) { + printer->Print(variables_, + " if ($name$ != NULL && $name$->GetArena() != NULL) {\n" + " $type$* new_$name$ = new $type$;\n" + " new_$name$->CopyFrom(*$name$);\n" + " $name$ = new_$name$;\n" + " }\n"); + } + + printer->Print(variables_, + " $name$_ = $name$;\n" + " if ($name$) {\n" + " $set_hasbit$\n" + " } else {\n" + " $clear_hasbit$\n" + " }\n" + " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" + "}\n"); + } } void MessageFieldGenerator:: GenerateClearingCode(io::Printer* printer) const { - printer->Print(variables_, - "if ($name$_ != NULL) $name$_->$type$::Clear();\n"); + if (!HasFieldPresence(descriptor_->file())) { + // If we don't have has-bits, message presence is indicated only by ptr != + // NULL. Thus on clear, we need to delete the object. + printer->Print(variables_, + "if ($name$_ != NULL) delete $name$_;\n" + "$name$_ = NULL;\n"); + } else { + printer->Print(variables_, + "if ($name$_ != NULL) $name$_->$type$::Clear();\n"); + } } void MessageFieldGenerator:: @@ -198,43 +325,165 @@ MessageOneofFieldGenerator::~MessageOneofFieldGenerator() {} void MessageOneofFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer) const { - printer->Print(variables_, - "inline const $type$& $classname$::$name$() const {\n" - " return has_$name$() ? *$oneof_prefix$$name$_\n" - " : $type$::default_instance();\n" - "}\n" - "inline $type$* $classname$::mutable_$name$() {\n" - " if (!has_$name$()) {\n" - " clear_$oneof_name$();\n" - " set_has_$name$();\n" - " $oneof_prefix$$name$_ = new $type$;\n" - " }\n" - " return $oneof_prefix$$name$_;\n" - "}\n" - "inline $type$* $classname$::$release_name$() {\n" - " if (has_$name$()) {\n" - " clear_has_$oneof_name$();\n" - " $type$* temp = $oneof_prefix$$name$_;\n" - " $oneof_prefix$$name$_ = NULL;\n" - " return temp;\n" - " } else {\n" - " return NULL;\n" - " }\n" - "}\n" - "inline void $classname$::set_allocated_$name$($type$* $name$) {\n" - " clear_$oneof_name$();\n" - " if ($name$) {\n" - " set_has_$name$();\n" - " $oneof_prefix$$name$_ = $name$;\n" - " }\n" - "}\n"); + if (SupportsArenas(descriptor_)) { + printer->Print(variables_, + "inline const $type$& $classname$::$name$() const {\n" + " // @@protoc_insertion_point(field_get:$full_name$)\n" + " return has_$name$() ? *$oneof_prefix$$name$_\n" + " : $type$::default_instance();\n" + "}\n" + "inline $type$* $classname$::mutable_$name$() {\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n"); + if (SupportsArenas(descriptor_->message_type())) { + printer->Print(variables_, + " $oneof_prefix$$name$_ = \n" + " ::google::protobuf::Arena::CreateMessage< $type$ >(\n" + " GetArenaNoVirtual());\n"); + } else { + printer->Print(variables_, + " $oneof_prefix$$name$_ = \n" + " ::google::protobuf::Arena::Create< $type$ >(\n" + " GetArenaNoVirtual());\n"); + } + printer->Print(variables_, + " }\n" + " // @@protoc_insertion_point(field_mutable:$full_name$)\n" + " return $oneof_prefix$$name$_;\n" + "}\n" + "inline $type$* $classname$::$release_name$() {\n" + " if (has_$name$()) {\n" + " clear_has_$oneof_name$();\n" + " if (GetArenaNoVirtual() != NULL) {\n" + // N.B.: safe to use the underlying field pointer here because we are sure + // that it is non-NULL (because has_$name$() returned true). + " $type$* temp = new $type$;\n" + " temp->MergeFrom(*$oneof_prefix$$name$_);\n" + " $oneof_prefix$$name$_ = NULL;\n" + " return temp;\n" + " } else {\n" + " $type$* temp = $oneof_prefix$$name$_;\n" + " $oneof_prefix$$name$_ = NULL;\n" + " return temp;\n" + " }\n" + " } else {\n" + " return NULL;\n" + " }\n" + "}\n" + "inline $type$* $classname$::unsafe_arena_release_$name$() {\n" + " if (has_$name$()) {\n" + " clear_has_$oneof_name$();\n" + " $type$* temp = $oneof_prefix$$name$_;\n" + " $oneof_prefix$$name$_ = NULL;\n" + " return temp;\n" + " } else {\n" + " return NULL;\n" + " }\n" + "}\n" + "inline void $classname$::set_allocated_$name$($type$* $name$) {\n" + " clear_$oneof_name$();\n" + " if ($name$) {\n"); + + if (SupportsArenas(descriptor_->message_type())) { + printer->Print(variables_, + // If incoming message is on the heap and we are on an arena, just Own() + // it (see above). If it's on a different arena than we are or one of us + // is on the heap, we make a copy to our arena/heap. + " if (GetArenaNoVirtual() != NULL &&\n" + " ::google::protobuf::Arena::GetArena($name$) == NULL) {\n" + " GetArenaNoVirtual()->Own($name$);\n" + " } else if (GetArenaNoVirtual() !=\n" + " ::google::protobuf::Arena::GetArena($name$)) {\n" + " $type$* new_$name$ = \n" + " ::google::protobuf::Arena::CreateMessage< $type$ >(\n" + " GetArenaNoVirtual());\n" + " new_$name$->CopyFrom(*$name$);\n" + " $name$ = new_$name$;\n" + " }\n"); + } else { + printer->Print(variables_, + " if (GetArenaNoVirtual() != NULL) {\n" + " GetArenaNoVirtual()->Own($name$);\n" + " }\n"); + } + + printer->Print(variables_, + " set_has_$name$();\n" + " $oneof_prefix$$name$_ = $name$;\n" + " }\n" + " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" + "}\n" + "inline void $classname$::unsafe_arena_set_allocated_$name$(" + "$type$* $name$) {\n" + // We rely on the oneof clear method to free the earlier contents of this + // oneof. We can directly use the pointer we're given to set the new + // value. + " clear_$oneof_name$();\n" + " if ($name$) {\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_ = $name$;\n" + " }\n" + " // @@protoc_insertion_point(field_unsafe_arena_set_allocated:" + "$full_name$)\n" + "}\n"); + } else { + printer->Print(variables_, + "inline const $type$& $classname$::$name$() const {\n" + " // @@protoc_insertion_point(field_get:$full_name$)\n" + " return has_$name$() ? *$oneof_prefix$$name$_\n" + " : $type$::default_instance();\n" + "}\n" + "inline $type$* $classname$::mutable_$name$() {\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_ = new $type$;\n" + " }\n" + " // @@protoc_insertion_point(field_mutable:$full_name$)\n" + " return $oneof_prefix$$name$_;\n" + "}\n" + "inline $type$* $classname$::$release_name$() {\n" + " if (has_$name$()) {\n" + " clear_has_$oneof_name$();\n" + " $type$* temp = $oneof_prefix$$name$_;\n" + " $oneof_prefix$$name$_ = NULL;\n" + " return temp;\n" + " } else {\n" + " return NULL;\n" + " }\n" + "}\n" + "inline void $classname$::set_allocated_$name$($type$* $name$) {\n" + " clear_$oneof_name$();\n" + " if ($name$) {\n"); + if (SupportsArenas(descriptor_->message_type())) { + printer->Print(variables_, + " if ($name$->GetArena() != NULL) {\n" + " $type$* new_$name$ = new $type$;\n" + " new_$name$->CopyFrom(*$name$);\n" + " $name$ = new_$name$;\n" + " }\n"); + } + printer->Print(variables_, + " set_has_$name$();\n" + " $oneof_prefix$$name$_ = $name$;\n" + " }\n" + " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" + "}\n"); + } } void MessageOneofFieldGenerator:: GenerateClearingCode(io::Printer* printer) const { - // if it is the active field, it cannot be NULL. - printer->Print(variables_, - "delete $oneof_prefix$$name$_;\n"); + if (SupportsArenas(descriptor_)) { + printer->Print(variables_, + "if (GetArenaNoVirtual() == NULL) {\n" + " delete $oneof_prefix$$name$_;\n" + "}\n"); + } else { + printer->Print(variables_, + "delete $oneof_prefix$$name$_;\n"); + } } void MessageOneofFieldGenerator:: @@ -318,7 +567,7 @@ GenerateMergingCode(io::Printer* printer) const { void RepeatedMessageFieldGenerator:: GenerateSwappingCode(io::Printer* printer) const { - printer->Print(variables_, "$name$_.Swap(&other->$name$_);\n"); + printer->Print(variables_, "$name$_.UnsafeArenaSwap(&other->$name$_);\n"); } void RepeatedMessageFieldGenerator:: diff --git a/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc index 2dbf14ce..26cefb2e 100644 --- a/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc @@ -35,6 +35,9 @@ // worth. #include <memory> +#ifndef _SHARED_PTR_H +#include <google/protobuf/stubs/shared_ptr.h> +#endif #include <google/protobuf/compiler/cpp/cpp_generator.h> #include <google/protobuf/compiler/command_line_interface.h> @@ -69,6 +72,100 @@ class TestGenerator : public CodeGenerator { TryInsert("test.pb.cc", "includes", context); TryInsert("test.pb.cc", "namespace_scope", context); TryInsert("test.pb.cc", "global_scope", context); + + // Check field accessors for an optional int32: + TryInsert("test.pb.h", "field_get:foo.Bar.optInt", context); + TryInsert("test.pb.h", "field_set:foo.Bar.optInt", context); + + // Check field accessors for a repeated int32: + TryInsert("test.pb.h", "field_get:foo.Bar.repeatedInt", context); + TryInsert("test.pb.h", "field_set:foo.Bar.repeatedInt", context); + + // Check field accessors for a required string: + TryInsert("test.pb.h", "field_get:foo.Bar.requiredString", context); + TryInsert("test.pb.h", "field_set:foo.Bar.requiredString", context); + TryInsert("test.pb.h", "field_set_char:foo.Bar.requiredString", context); + TryInsert("test.pb.h", "field_set_pointer:foo.Bar.requiredString", context); + TryInsert("test.pb.h", "field_mutable:foo.Bar.requiredString", context); + TryInsert("test.pb.h", "field_set_allocated:foo.Bar.requiredString", + context); + TryInsert("test.pb.h", "field_set_char:foo.Bar.requiredString", context); + TryInsert("test.pb.h", "field_set_pointer:foo.Bar.requiredString", context); + + // Check field accessors for a repeated string: + TryInsert("test.pb.h", "field_get:foo.Bar.repeatedString", context); + TryInsert("test.pb.h", "field_set:foo.Bar.repeatedString", context); + TryInsert("test.pb.h", "field_set_char:foo.Bar.repeatedString", context); + TryInsert("test.pb.h", "field_set_pointer:foo.Bar.repeatedString", context); + TryInsert("test.pb.h", "field_mutable:foo.Bar.repeatedString", context); + TryInsert("test.pb.h", "field_set_char:foo.Bar.repeatedString", context); + TryInsert("test.pb.h", "field_set_pointer:foo.Bar.repeatedString", context); + + // Check field accessors for an int inside oneof{}: + TryInsert("test.pb.h", "field_get:foo.Bar.oneOfInt", context); + TryInsert("test.pb.h", "field_set:foo.Bar.oneOfInt", context); + + // Check field accessors for a string inside oneof{}: + TryInsert("test.pb.h", "field_get:foo.Bar.oneOfString", context); + TryInsert("test.pb.h", "field_set:foo.Bar.oneOfString", context); + TryInsert("test.pb.h", "field_set_char:foo.Bar.oneOfString", context); + TryInsert("test.pb.h", "field_set_pointer:foo.Bar.oneOfString", context); + TryInsert("test.pb.h", "field_mutable:foo.Bar.oneOfString", context); + TryInsert("test.pb.h", "field_set_allocated:foo.Bar.oneOfString", context); + TryInsert("test.pb.h", "field_set_char:foo.Bar.oneOfString", context); + TryInsert("test.pb.h", "field_set_pointer:foo.Bar.oneOfString", context); + + // Check field accessors for an optional message: + TryInsert("test.pb.h", "field_get:foo.Bar.optMessage", context); + TryInsert("test.pb.h", "field_mutable:foo.Bar.optMessage", context); + TryInsert("test.pb.h", "field_set_allocated:foo.Bar.optMessage", context); + + // Check field accessors for a repeated message: + TryInsert("test.pb.h", "field_add:foo.Bar.repeatedMessage", context); + TryInsert("test.pb.h", "field_get:foo.Bar.repeatedMessage", context); + TryInsert("test.pb.h", "field_list:foo.Bar.repeatedMessage", context); + TryInsert("test.pb.h", "field_mutable:foo.Bar.repeatedMessage", context); + TryInsert("test.pb.h", "field_mutable_list:foo.Bar.repeatedMessage", + context); + + // Check field accessors for a message inside oneof{}: + TryInsert("test.pb.h", "field_get:foo.Bar.oneOfMessage", context); + TryInsert("test.pb.h", "field_mutable:foo.Bar.oneOfMessage", context); + TryInsert("test.pb.h", "field_set_allocated:foo.Bar.oneOfMessage", context); + + // Check field accessors for an optional enum: + TryInsert("test.pb.h", "field_get:foo.Bar.optEnum", context); + TryInsert("test.pb.h", "field_set:foo.Bar.optEnum", context); + + // Check field accessors for a repeated enum: + TryInsert("test.pb.h", "field_get:foo.Bar.repeatedEnum", context); + TryInsert("test.pb.h", "field_set:foo.Bar.repeatedEnum", context); + TryInsert("test.pb.h", "field_add:foo.Bar.repeatedEnum", context); + TryInsert("test.pb.h", "field_list:foo.Bar.repeatedEnum", context); + TryInsert("test.pb.h", "field_mutable_list:foo.Bar.repeatedEnum", context); + + // Check field accessors for an enum inside oneof{}: + TryInsert("test.pb.h", "field_get:foo.Bar.oneOfEnum", context); + TryInsert("test.pb.h", "field_set:foo.Bar.oneOfEnum", context); + + // Check field accessors for a required cord: + TryInsert("test.pb.h", "field_get:foo.Bar.requiredCord", context); + TryInsert("test.pb.h", "field_set:foo.Bar.requiredCord", context); + TryInsert("test.pb.h", "field_mutable:foo.Bar.requiredCord", context); + + // Check field accessors for a repeated cord: + TryInsert("test.pb.h", "field_get:foo.Bar.repeatedCord", context); + TryInsert("test.pb.h", "field_set:foo.Bar.repeatedCord", context); + TryInsert("test.pb.h", "field_add:foo.Bar.repeatedCord", context); + TryInsert("test.pb.h", "field_list:foo.Bar.repeatedCord", context); + TryInsert("test.pb.h", "field_mutable:foo.Bar.repeatedCord", context); + TryInsert("test.pb.h", "field_mutable_list:foo.Bar.repeatedCord", context); + + // Check field accessors for a cord inside oneof{}: + TryInsert("test.pb.h", "field_get:foo.Bar.oneOfCord", context); + TryInsert("test.pb.h", "field_set:foo.Bar.oneOfCord", context); + TryInsert("test.pb.h", "field_mutable:foo.Bar.oneOfCord", context); + return true; } @@ -88,8 +185,39 @@ TEST(CppPluginTest, PluginTest) { GOOGLE_CHECK_OK(File::SetContents(TestTempDir() + "/test.proto", "syntax = \"proto2\";\n" "package foo;\n" + "\n" + "enum Thud { VALUE = 0; }\n" + "\n" "message Bar {\n" " message Baz {}\n" + " optional int32 optInt = 1;\n" + " repeated int32 repeatedInt = 2;\n" + "\n" + " required string requiredString = 3;\n" + " repeated string repeatedString = 4;\n" + "\n" + " optional Baz optMessage = 6;\n" + " repeated Baz repeatedMessage = 7;\n" + "\n" + " optional Thud optEnum = 8;\n" + " repeated Thud repeatedEnum = 9;\n" + "\n" + " required string requiredCord = 10 [\n" + " ctype = CORD\n" + " ];\n" + " repeated string repeatedCord = 11 [\n" + " ctype = CORD\n" + " ];\n" + "\n" + " oneof Qux {\n" + " int64 oneOfInt = 20;\n" + " string oneOfString = 21;\n" + " Baz oneOfMessage = 22;\n" + " Thud oneOfEnum = 23;" + " string oneOfCord = 24 [\n" + " ctype = CORD\n" + " ];\n" + " }\n" "}\n", true)); diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc index 44290a31..9a2c930e 100644 --- a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc @@ -129,7 +129,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { " return $name$_;\n" "}\n" "inline void $classname$::set_$name$($type$ value) {\n" - " set_has_$name$();\n" + " $set_hasbit$\n" " $name$_ = value;\n" " // @@protoc_insertion_point(field_set:$full_name$)\n" "}\n"); @@ -161,7 +161,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { "DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<\n" " $type$, $wire_format_field_type$>(\n" " input, &$name$_)));\n" - "set_has_$name$();\n"); + "$set_hasbit$\n"); } void PrimitiveFieldGenerator:: @@ -207,6 +207,7 @@ void PrimitiveOneofFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer) const { printer->Print(variables_, "inline $type$ $classname$::$name$() const {\n" + " // @@protoc_insertion_point(field_get:$full_name$)\n" " if (has_$name$()) {\n" " return $oneof_prefix$$name$_;\n" " }\n" @@ -218,6 +219,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { " set_has_$name$();\n" " }\n" " $oneof_prefix$$name$_ = value;\n" + " // @@protoc_insertion_point(field_set:$full_name$)\n" "}\n"); } @@ -330,7 +332,7 @@ GenerateMergingCode(io::Printer* printer) const { void RepeatedPrimitiveFieldGenerator:: GenerateSwappingCode(io::Printer* printer) const { - printer->Print(variables_, "$name$_.Swap(&other->$name$_);\n"); + printer->Print(variables_, "$name$_.UnsafeArenaSwap(&other->$name$_);\n"); } void RepeatedPrimitiveFieldGenerator:: diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.cc b/src/google/protobuf/compiler/cpp/cpp_string_field.cc index 180d236b..a2a8c81c 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.cc @@ -52,9 +52,14 @@ void SetStringVariables(const FieldDescriptor* descriptor, (*variables)["default"] = DefaultValue(descriptor); (*variables)["default_length"] = SimpleItoa(descriptor->default_value_string().length()); - (*variables)["default_variable"] = descriptor->default_value_string().empty() - ? "&::google::protobuf::internal::GetEmptyStringAlreadyInited()" - : "_default_" + FieldName(descriptor) + "_"; + string default_variable_string = + descriptor->default_value_string().empty() + ? "&::google::protobuf::internal::GetEmptyStringAlreadyInited()" + : "_default_" + FieldName(descriptor) + "_"; + (*variables)["default_variable"] = default_variable_string; + (*variables)["default_value_init"] = + descriptor->default_value_string().empty() + ? "" : "*" + default_variable_string; (*variables)["pointer_type"] = descriptor->type() == FieldDescriptor::TYPE_BYTES ? "void" : "char"; // NOTE: Escaped here to unblock proto1->proto2 migration. @@ -63,6 +68,8 @@ void SetStringVariables(const FieldDescriptor* descriptor, SafeFunctionName(descriptor->containing_type(), descriptor, "release_"); (*variables)["full_name"] = descriptor->full_name(); + + (*variables)["string_piece"] = "::std::string"; } } // namespace @@ -80,7 +87,19 @@ StringFieldGenerator::~StringFieldGenerator() {} void StringFieldGenerator:: GeneratePrivateMembers(io::Printer* printer) const { - printer->Print(variables_, "::std::string* $name$_;\n"); + // N.B. that we continue to use |ArenaStringPtr| instead of |string*| for + // string fields, even when SupportArenas(descriptor_) == false. Why? + // The simple answer is to avoid unmaintainable complexity. The reflection + // code assumes ArenaStringPtrs. These are *almost* in-memory-compatible with + // string*, except for the pointer tags and related ownership semantics. We + // could modify the runtime code to use string* for the not-supporting-arenas + // case, but this would require a way to detect which type of class was + // generated (adding overhead and complexity to GeneratedMessageReflection) + // and littering the runtime code paths with conditionals. It's simpler to + // stick with this but use lightweight accessors that assume arena == NULL. + // There should be very little overhead anyway because it's just a tagged + // pointer in-memory. + printer->Print(variables_, "::google::protobuf::internal::ArenaStringPtr $name$_;\n"); } void StringFieldGenerator:: @@ -125,6 +144,12 @@ GenerateAccessorDeclarations(io::Printer* printer) const { "inline ::std::string* mutable_$name$()$deprecation$;\n" "inline ::std::string* $release_name$()$deprecation$;\n" "inline void set_allocated_$name$(::std::string* $name$)$deprecation$;\n"); + if (SupportsArenas(descriptor_)) { + printer->Print(variables_, + "inline ::std::string* unsafe_arena_release_$name$()$deprecation$;\n" + "inline void unsafe_arena_set_allocated_$name$(\n" + " ::std::string* $name$)$deprecation$;\n"); + } if (descriptor_->options().ctype() != FieldOptions::STRING) { @@ -136,74 +161,113 @@ GenerateAccessorDeclarations(io::Printer* printer) const { void StringFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer) const { - printer->Print(variables_, - "inline const ::std::string& $classname$::$name$() const {\n" - " // @@protoc_insertion_point(field_get:$full_name$)\n" - " return *$name$_;\n" - "}\n" - "inline void $classname$::set_$name$(const ::std::string& value) {\n" - " set_has_$name$();\n" - " if ($name$_ == $default_variable$) {\n" - " $name$_ = new ::std::string;\n" - " }\n" - " $name$_->assign(value);\n" - " // @@protoc_insertion_point(field_set:$full_name$)\n" - "}\n" - "inline void $classname$::set_$name$(const char* value) {\n" - " set_has_$name$();\n" - " if ($name$_ == $default_variable$) {\n" - " $name$_ = new ::std::string;\n" - " }\n" - " $name$_->assign(value);\n" - " // @@protoc_insertion_point(field_set_char:$full_name$)\n" - "}\n" - "inline " - "void $classname$::set_$name$(const $pointer_type$* value, size_t size) {\n" - " set_has_$name$();\n" - " if ($name$_ == $default_variable$) {\n" - " $name$_ = new ::std::string;\n" - " }\n" - " $name$_->assign(reinterpret_cast<const char*>(value), size);\n" - " // @@protoc_insertion_point(field_set_pointer:$full_name$)\n" - "}\n" - "inline ::std::string* $classname$::mutable_$name$() {\n" - " set_has_$name$();\n" - " if ($name$_ == $default_variable$) {\n"); - if (descriptor_->default_value_string().empty()) { + if (SupportsArenas(descriptor_)) { printer->Print(variables_, - " $name$_ = new ::std::string;\n"); + "inline const ::std::string& $classname$::$name$() const {\n" + " // @@protoc_insertion_point(field_get:$full_name$)\n" + " return $name$_.Get($default_variable$);\n" + "}\n" + "inline void $classname$::set_$name$(const ::std::string& value) {\n" + " $set_hasbit$\n" + " $name$_.Set($default_variable$, value, GetArenaNoVirtual());\n" + " // @@protoc_insertion_point(field_set:$full_name$)\n" + "}\n" + "inline void $classname$::set_$name$(const char* value) {\n" + " $set_hasbit$\n" + " $name$_.Set($default_variable$, $string_piece$(value),\n" + " GetArenaNoVirtual());\n" + " // @@protoc_insertion_point(field_set_char:$full_name$)\n" + "}\n" + "inline " + "void $classname$::set_$name$(const $pointer_type$* value,\n" + " size_t size) {\n" + " $set_hasbit$\n" + " $name$_.Set($default_variable$, $string_piece$(\n" + " reinterpret_cast<const char*>(value), size), GetArenaNoVirtual());\n" + " // @@protoc_insertion_point(field_set_pointer:$full_name$)\n" + "}\n" + "inline ::std::string* $classname$::mutable_$name$() {\n" + " $set_hasbit$\n" + " // @@protoc_insertion_point(field_mutable:$full_name$)\n" + " return $name$_.Mutable($default_variable$, GetArenaNoVirtual());\n" + "}\n" + "inline ::std::string* $classname$::$release_name$() {\n" + " $clear_hasbit$\n" + " return $name$_.Release($default_variable$, GetArenaNoVirtual());\n" + "}\n" + "inline ::std::string* $classname$::unsafe_arena_release_$name$() {\n" + " GOOGLE_DCHECK(GetArenaNoVirtual() != NULL);\n" + " $clear_hasbit$\n" + " return $name$_.UnsafeArenaRelease($default_variable$,\n" + " GetArenaNoVirtual());\n" + "}\n" + "inline void $classname$::set_allocated_$name$(::std::string* $name$) {\n" + " if ($name$ != NULL) {\n" + " $set_hasbit$\n" + " } else {\n" + " $clear_hasbit$\n" + " }\n" + " $name$_.SetAllocated($default_variable$, $name$,\n" + " GetArenaNoVirtual());\n" + " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" + "}\n" + "inline void $classname$::unsafe_arena_set_allocated_$name$(\n" + " ::std::string* $name$) {\n" + " GOOGLE_DCHECK(GetArenaNoVirtual() != NULL);\n" + " if ($name$ != NULL) {\n" + " $set_hasbit$\n" + " } else {\n" + " $clear_hasbit$\n" + " }\n" + " $set_hasbit$\n" + " $name$_.UnsafeArenaSetAllocated($default_variable$,\n" + " $name$, GetArenaNoVirtual());\n" + " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" + "}\n"); } else { + // No-arena case. printer->Print(variables_, - " $name$_ = new ::std::string(*$default_variable$);\n"); + "inline const ::std::string& $classname$::$name$() const {\n" + " // @@protoc_insertion_point(field_get:$full_name$)\n" + " return $name$_.GetNoArena($default_variable$);\n" + "}\n" + "inline void $classname$::set_$name$(const ::std::string& value) {\n" + " $set_hasbit$\n" + " $name$_.SetNoArena($default_variable$, value);\n" + " // @@protoc_insertion_point(field_set:$full_name$)\n" + "}\n" + "inline void $classname$::set_$name$(const char* value) {\n" + " $set_hasbit$\n" + " $name$_.SetNoArena($default_variable$, $string_piece$(value));\n" + " // @@protoc_insertion_point(field_set_char:$full_name$)\n" + "}\n" + "inline " + "void $classname$::set_$name$(const $pointer_type$* value, " + "size_t size) {\n" + " $set_hasbit$\n" + " $name$_.SetNoArena($default_variable$,\n" + " $string_piece$(reinterpret_cast<const char*>(value), size));\n" + " // @@protoc_insertion_point(field_set_pointer:$full_name$)\n" + "}\n" + "inline ::std::string* $classname$::mutable_$name$() {\n" + " $set_hasbit$\n" + " // @@protoc_insertion_point(field_mutable:$full_name$)\n" + " return $name$_.MutableNoArena($default_variable$);\n" + "}\n" + "inline ::std::string* $classname$::$release_name$() {\n" + " $clear_hasbit$\n" + " return $name$_.ReleaseNoArena($default_variable$);\n" + "}\n" + "inline void $classname$::set_allocated_$name$(::std::string* $name$) {\n" + " if ($name$ != NULL) {\n" + " $set_hasbit$\n" + " } else {\n" + " $clear_hasbit$\n" + " }\n" + " $name$_.SetAllocatedNoArena($default_variable$, $name$);\n" + " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" + "}\n"); } - printer->Print(variables_, - " }\n" - " // @@protoc_insertion_point(field_mutable:$full_name$)\n" - " return $name$_;\n" - "}\n" - "inline ::std::string* $classname$::$release_name$() {\n" - " clear_has_$name$();\n" - " if ($name$_ == $default_variable$) {\n" - " return NULL;\n" - " } else {\n" - " ::std::string* temp = $name$_;\n" - " $name$_ = const_cast< ::std::string*>($default_variable$);\n" - " return temp;\n" - " }\n" - "}\n" - "inline void $classname$::set_allocated_$name$(::std::string* $name$) {\n" - " if ($name$_ != $default_variable$) {\n" - " delete $name$_;\n" - " }\n" - " if ($name$) {\n" - " set_has_$name$();\n" - " $name$_ = $name$;\n" - " } else {\n" - " clear_has_$name$();\n" - " $name$_ = const_cast< ::std::string*>($default_variable$);\n" - " }\n" - " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" - "}\n"); } void StringFieldGenerator:: @@ -217,16 +281,26 @@ GenerateNonInlineAccessorDefinitions(io::Printer* printer) const { void StringFieldGenerator:: GenerateClearingCode(io::Printer* printer) const { - if (descriptor_->default_value_string().empty()) { - printer->Print(variables_, - "if ($name$_ != $default_variable$) {\n" - " $name$_->clear();\n" - "}\n"); + // Two-dimension specialization here: supporting arenas or not, and default + // value is the empty string or not. Complexity here ensures the minimal + // number of branches / amount of extraneous code at runtime (given that the + // below methods are inlined one-liners)! + if (SupportsArenas(descriptor_)) { + if (descriptor_->default_value_string().empty()) { + printer->Print(variables_, + "$name$_.ClearToEmpty($default_variable$, GetArenaNoVirtual());\n"); + } else { + printer->Print(variables_, + "$name$_.ClearToDefault($default_variable$, GetArenaNoVirtual());\n"); + } } else { - printer->Print(variables_, - "if ($name$_ != $default_variable$) {\n" - " $name$_->assign(*$default_variable$);\n" - "}\n"); + if (descriptor_->default_value_string().empty()) { + printer->Print(variables_, + "$name$_.ClearToEmptyNoArena($default_variable$);\n"); + } else { + printer->Print(variables_, + "$name$_.ClearToDefaultNoArena($default_variable$);\n"); + } } } @@ -237,21 +311,24 @@ GenerateMergingCode(io::Printer* printer) const { void StringFieldGenerator:: GenerateSwappingCode(io::Printer* printer) const { - printer->Print(variables_, "std::swap($name$_, other->$name$_);\n"); + printer->Print(variables_, "$name$_.Swap(&other->$name$_);\n"); } void StringFieldGenerator:: GenerateConstructorCode(io::Printer* printer) const { printer->Print(variables_, - "$name$_ = const_cast< ::std::string*>($default_variable$);\n"); + "$name$_.UnsafeSetDefault($default_variable$);\n"); } void StringFieldGenerator:: GenerateDestructorCode(io::Printer* printer) const { - printer->Print(variables_, - "if ($name$_ != $default_variable$) {\n" - " delete $name$_;\n" - "}\n"); + if (SupportsArenas(descriptor_)) { + printer->Print(variables_, + "$name$_.Destroy($default_variable$, GetArenaNoVirtual());\n"); + } else { + printer->Print(variables_, + "$name$_.DestroyNoArena($default_variable$);\n"); + } } void StringFieldGenerator:: @@ -276,13 +353,14 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { printer->Print(variables_, "DO_(::google::protobuf::internal::WireFormatLite::Read$declared_type$(\n" " input, this->mutable_$name$()));\n"); + if (HasUtf8Verification(descriptor_->file()) && descriptor_->type() == FieldDescriptor::TYPE_STRING) { printer->Print(variables_, "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" " this->$name$().data(), this->$name$().length(),\n" " ::google::protobuf::internal::WireFormat::PARSE,\n" - " \"$name$\");\n"); + " \"$full_name$\");\n"); } } @@ -294,7 +372,7 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" " this->$name$().data(), this->$name$().length(),\n" " ::google::protobuf::internal::WireFormat::SERIALIZE,\n" - " \"$name$\");\n"); + " \"$full_name$\");\n"); } printer->Print(variables_, "::google::protobuf::internal::WireFormatLite::Write$declared_type$MaybeAliased(\n" @@ -309,7 +387,7 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" " this->$name$().data(), this->$name$().length(),\n" " ::google::protobuf::internal::WireFormat::SERIALIZE,\n" - " \"$name$\");\n"); + " \"$full_name$\");\n"); } printer->Print(variables_, "target =\n" @@ -338,84 +416,186 @@ StringOneofFieldGenerator::~StringOneofFieldGenerator() {} void StringOneofFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer) const { - printer->Print(variables_, - "inline const ::std::string& $classname$::$name$() const {\n" - " if (has_$name$()) {\n" - " return *$oneof_prefix$$name$_;\n" - " }\n"); - if (descriptor_->default_value_string().empty()) { - printer->Print(variables_, - " return ::google::protobuf::internal::GetEmptyStringAlreadyInited();\n"); - } else { - printer->Print(variables_, - " return *$default_variable$;\n"); - } - printer->Print(variables_, - "}\n" - "inline void $classname$::set_$name$(const ::std::string& value) {\n" - " if (!has_$name$()) {\n" - " clear_$oneof_name$();\n" - " set_has_$name$();\n" - " $oneof_prefix$$name$_ = new ::std::string;\n" - " }\n" - " $oneof_prefix$$name$_->assign(value);\n" - "}\n" - "inline void $classname$::set_$name$(const char* value) {\n" - " if (!has_$name$()) {\n" - " clear_$oneof_name$();\n" - " set_has_$name$();\n" - " $oneof_prefix$$name$_ = new ::std::string;\n" - " }\n" - " $oneof_prefix$$name$_->assign(value);\n" - "}\n" - "inline " - "void $classname$::set_$name$(const $pointer_type$* value, size_t size) {\n" - " if (!has_$name$()) {\n" - " clear_$oneof_name$();\n" - " set_has_$name$();\n" - " $oneof_prefix$$name$_ = new ::std::string;\n" - " }\n" - " $oneof_prefix$$name$_->assign(\n" - " reinterpret_cast<const char*>(value), size);\n" - "}\n" - "inline ::std::string* $classname$::mutable_$name$() {\n" - " if (!has_$name$()) {\n" - " clear_$oneof_name$();\n" - " set_has_$name$();\n"); - if (descriptor_->default_value_string().empty()) { + if (SupportsArenas(descriptor_)) { printer->Print(variables_, - " $oneof_prefix$$name$_ = new ::std::string;\n"); + "inline const ::std::string& $classname$::$name$() const {\n" + " // @@protoc_insertion_point(field_get:$full_name$)\n" + " if (has_$name$()) {\n" + " return $oneof_prefix$$name$_.Get($default_variable$);\n" + " }\n" + " return *$default_variable$;\n" + "}\n" + "inline void $classname$::set_$name$(const ::std::string& value) {\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_.UnsafeSetDefault($default_variable$);\n" + " }\n" + " $oneof_prefix$$name$_.Set($default_variable$, value,\n" + " GetArenaNoVirtual());\n" + " // @@protoc_insertion_point(field_set:$full_name$)\n" + "}\n" + "inline void $classname$::set_$name$(const char* value) {\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_.UnsafeSetDefault($default_variable$);\n" + " }\n" + " $oneof_prefix$$name$_.Set($default_variable$,\n" + " $string_piece$(value), GetArenaNoVirtual());\n" + " // @@protoc_insertion_point(field_set_char:$full_name$)\n" + "}\n" + "inline " + "void $classname$::set_$name$(const $pointer_type$* value,\n" + " size_t size) {\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_.UnsafeSetDefault($default_variable$);\n" + " }\n" + " $oneof_prefix$$name$_.Set($default_variable$, $string_piece$(\n" + " reinterpret_cast<const char*>(value), size),\n" + " GetArenaNoVirtual());\n" + " // @@protoc_insertion_point(field_set_pointer:$full_name$)\n" + "}\n" + "inline ::std::string* $classname$::mutable_$name$() {\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_.UnsafeSetDefault($default_variable$);\n" + " }\n" + " return $oneof_prefix$$name$_.Mutable($default_variable$,\n" + " GetArenaNoVirtual());\n" + " // @@protoc_insertion_point(field_mutable:$full_name$)\n" + "}\n" + "inline ::std::string* $classname$::$release_name$() {\n" + " if (has_$name$()) {\n" + " clear_has_$oneof_name$();\n" + " return $oneof_prefix$$name$_.Release($default_variable$,\n" + " GetArenaNoVirtual());\n" + " } else {\n" + " return NULL;\n" + " }\n" + "}\n" + "inline ::std::string* $classname$::unsafe_arena_release_$name$() {\n" + " GOOGLE_DCHECK(GetArenaNoVirtual() != NULL);\n" + " if (has_$name$()) {\n" + " clear_has_$oneof_name$();\n" + " return $oneof_prefix$$name$_.UnsafeArenaRelease(\n" + " $default_variable$, GetArenaNoVirtual());\n" + " } else {\n" + " return NULL;\n" + " }\n" + "}\n" + "inline void $classname$::set_allocated_$name$(::std::string* $name$) {\n" + " if (!has_$name$()) {\n" + " $oneof_prefix$$name$_.UnsafeSetDefault($default_variable$);\n" + " }\n" + " clear_$oneof_name$();\n" + " if ($name$ != NULL) {\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_.SetAllocated($default_variable$, $name$,\n" + " GetArenaNoVirtual());\n" + " }\n" + " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" + "}\n" + "inline void $classname$::unsafe_arena_set_allocated_$name$(" + "::std::string* $name$) {\n" + " GOOGLE_DCHECK(GetArenaNoVirtual() != NULL);\n" + " if (!has_$name$()) {\n" + " $oneof_prefix$$name$_.UnsafeSetDefault($default_variable$);\n" + " }\n" + " clear_$oneof_name$();\n" + " if ($name$) {\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_.UnsafeArenaSetAllocated($default_variable$, " + "$name$, GetArenaNoVirtual());\n" + " }\n" + " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" + "}\n"); } else { + // No-arena case. printer->Print(variables_, - " $oneof_prefix$$name$_ = new ::std::string(*$default_variable$);\n"); + "inline const ::std::string& $classname$::$name$() const {\n" + " // @@protoc_insertion_point(field_get:$full_name$)\n" + " if (has_$name$()) {\n" + " return $oneof_prefix$$name$_.GetNoArena($default_variable$);\n" + " }\n" + " return *$default_variable$;\n" + "}\n" + "inline void $classname$::set_$name$(const ::std::string& value) {\n" + " // @@protoc_insertion_point(field_set:$full_name$)\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_.UnsafeSetDefault($default_variable$);\n" + " }\n" + " $oneof_prefix$$name$_.SetNoArena($default_variable$, value);\n" + " // @@protoc_insertion_point(field_set:$full_name$)\n" + "}\n" + "inline void $classname$::set_$name$(const char* value) {\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_.UnsafeSetDefault($default_variable$);\n" + " }\n" + " $oneof_prefix$$name$_.SetNoArena($default_variable$,\n" + " $string_piece$(value));\n" + " // @@protoc_insertion_point(field_set_char:$full_name$)\n" + "}\n" + "inline " + "void $classname$::set_$name$(const $pointer_type$* value, size_t size) {\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_.UnsafeSetDefault($default_variable$);\n" + " }\n" + " $oneof_prefix$$name$_.SetNoArena($default_variable$, $string_piece$(\n" + " reinterpret_cast<const char*>(value), size));\n" + " // @@protoc_insertion_point(field_set_pointer:$full_name$)\n" + "}\n" + "inline ::std::string* $classname$::mutable_$name$() {\n" + " if (!has_$name$()) {\n" + " clear_$oneof_name$();\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_.UnsafeSetDefault($default_variable$);\n" + " }\n" + " // @@protoc_insertion_point(field_mutable:$full_name$)\n" + " return $oneof_prefix$$name$_.MutableNoArena($default_variable$);\n" + "}\n" + "inline ::std::string* $classname$::$release_name$() {\n" + " if (has_$name$()) {\n" + " clear_has_$oneof_name$();\n" + " return $oneof_prefix$$name$_.ReleaseNoArena($default_variable$);\n" + " } else {\n" + " return NULL;\n" + " }\n" + "}\n" + "inline void $classname$::set_allocated_$name$(::std::string* $name$) {\n" + " if (!has_$name$()) {\n" + " $oneof_prefix$$name$_.UnsafeSetDefault($default_variable$);\n" + " }\n" + " clear_$oneof_name$();\n" + " if ($name$ != NULL) {\n" + " set_has_$name$();\n" + " $oneof_prefix$$name$_.SetAllocatedNoArena($default_variable$,\n" + " $name$);\n" + " }\n" + " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" + "}\n"); } - printer->Print(variables_, - " }\n" - " return $oneof_prefix$$name$_;\n" - "}\n" - "inline ::std::string* $classname$::$release_name$() {\n" - " if (has_$name$()) {\n" - " clear_has_$oneof_name$();\n" - " ::std::string* temp = $oneof_prefix$$name$_;\n" - " $oneof_prefix$$name$_ = NULL;\n" - " return temp;\n" - " } else {\n" - " return NULL;\n" - " }\n" - "}\n" - "inline void $classname$::set_allocated_$name$(::std::string* $name$) {\n" - " clear_$oneof_name$();\n" - " if ($name$) {\n" - " set_has_$name$();\n" - " $oneof_prefix$$name$_ = $name$;\n" - " }\n" - "}\n"); } void StringOneofFieldGenerator:: GenerateClearingCode(io::Printer* printer) const { + if (SupportsArenas(descriptor_)) { printer->Print(variables_, - "delete $oneof_prefix$$name$_;\n"); + "$oneof_prefix$$name$_.Destroy($default_variable$,\n" + " GetArenaNoVirtual());\n"); + } else { + printer->Print(variables_, + "$oneof_prefix$$name$_.DestroyNoArena($default_variable$);\n"); + } } void StringOneofFieldGenerator:: @@ -425,25 +605,45 @@ GenerateSwappingCode(io::Printer* printer) const { void StringOneofFieldGenerator:: GenerateConstructorCode(io::Printer* printer) const { - if (!descriptor_->default_value_string().empty()) { + printer->Print(variables_, + " const_cast< ::google::protobuf::internal::ArenaStringPtr*>(" + "&$classname$_default_oneof_instance_->$name$_)->UnsafeSetDefault(" + "$default_variable$);\n"); +} + +void StringOneofFieldGenerator:: +GenerateDestructorCode(io::Printer* printer) const { + if (SupportsArenas(descriptor_)) { printer->Print(variables_, - " $classname$_default_oneof_instance_->$name$_ = " - "$classname$::$default_variable$;\n"); + "if (has_$name$()) {\n" + " $oneof_prefix$$name$_.Destroy($default_variable$,\n" + " GetArenaNoVirtual());\n" + "}\n"); } else { printer->Print(variables_, - " $classname$_default_oneof_instance_->$name$_ = " - "$default_variable$;\n"); + "if (has_$name$()) {\n" + " $oneof_prefix$$name$_.DestroyNoArena($default_variable$);\n" + "}\n"); } } void StringOneofFieldGenerator:: -GenerateDestructorCode(io::Printer* printer) const { - printer->Print(variables_, - "if (has_$name$()) {\n" - " delete $oneof_prefix$$name$_;\n" - "}\n"); +GenerateMergeFromCodedStream(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormatLite::Read$declared_type$(\n" + " input, this->mutable_$name$()));\n"); + + if (HasUtf8Verification(descriptor_->file()) && + descriptor_->type() == FieldDescriptor::TYPE_STRING) { + printer->Print(variables_, + "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" + " this->$name$().data(), this->$name$().length(),\n" + " ::google::protobuf::internal::WireFormat::PARSE,\n" + " \"$full_name$\");\n"); + } } + // =================================================================== RepeatedStringFieldGenerator:: @@ -566,7 +766,7 @@ GenerateMergingCode(io::Printer* printer) const { void RepeatedStringFieldGenerator:: GenerateSwappingCode(io::Printer* printer) const { - printer->Print(variables_, "$name$_.Swap(&other->$name$_);\n"); + printer->Print(variables_, "$name$_.UnsafeArenaSwap(&other->$name$_);\n"); } void RepeatedStringFieldGenerator:: @@ -586,7 +786,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { " this->$name$(this->$name$_size() - 1).data(),\n" " this->$name$(this->$name$_size() - 1).length(),\n" " ::google::protobuf::internal::WireFormat::PARSE,\n" - " \"$name$\");\n"); + " \"$full_name$\");\n"); } } @@ -600,7 +800,7 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" " this->$name$(i).data(), this->$name$(i).length(),\n" " ::google::protobuf::internal::WireFormat::SERIALIZE,\n" - " \"$name$\");\n"); + " \"$full_name$\");\n"); } printer->Print(variables_, " ::google::protobuf::internal::WireFormatLite::Write$declared_type$(\n" @@ -618,7 +818,7 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { " ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" " this->$name$(i).data(), this->$name$(i).length(),\n" " ::google::protobuf::internal::WireFormat::SERIALIZE,\n" - " \"$name$\");\n"); + " \"$full_name$\");\n"); } printer->Print(variables_, " target = ::google::protobuf::internal::WireFormatLite::\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.h b/src/google/protobuf/compiler/cpp/cpp_string_field.h index 86da38f2..0a5ca440 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.h @@ -88,6 +88,7 @@ class StringOneofFieldGenerator : public StringFieldGenerator { void GenerateSwappingCode(io::Printer* printer) const; void GenerateConstructorCode(io::Printer* printer) const; void GenerateDestructorCode(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringOneofFieldGenerator); diff --git a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto index 6b7f8308..4fa3c144 100644 --- a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto +++ b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto @@ -35,6 +35,7 @@ // This file tests that various identifiers work as field and type names even // though the same identifiers are used internally by the C++ code generator. +syntax = "proto2"; // Some generic_services option(s) added automatically. // See: http://go/proto2-generic-services-default @@ -58,7 +59,7 @@ message TestConflictingSymbolNames { optional int32 total_size = 6; optional int32 tag = 7; - enum TestEnum { FOO = 1; } + enum TestEnum { FOO = 0; } message Data1 { repeated int32 data = 1; } message Data2 { repeated TestEnum data = 1; } message Data3 { repeated string data = 1; } @@ -99,6 +100,8 @@ message TestConflictingSymbolNames { optional uint32 int = 30; optional uint32 friend = 31; optional uint32 class = 37; + optional uint32 typedecl = 39; + optional uint32 auto = 40; // The generator used to #define a macro called "DO" inside the .cc file. message DO {} @@ -116,15 +119,18 @@ message TestConflictingSymbolNames { // names. optional DO release_do = 36; - extensions 1000 to max; -} + // For clashing local variables in Serialize and ByteSize calculation. + optional string target = 38; -message TestConflictingSymbolNamesExtension { - extend TestConflictingSymbolNames { - repeated int32 repeated_int32_ext = 20423638 [packed=true]; - } + extensions 1000 to max; // NO_PROTO3 } +message TestConflictingSymbolNamesExtension { // NO_PROTO3 + extend TestConflictingSymbolNames { // NO_PROTO3 + repeated int32 repeated_int32_ext = 20423638 [packed=true]; // NO_PROTO3 + } // NO_PROTO3 +} // NO_PROTO3 + message DummyMessage {} service TestConflictingMethodNames { diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_unittest.cc index 93e1c3f1..c509a6a9 100644 --- a/src/google/protobuf/compiler/cpp/cpp_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_unittest.cc @@ -47,6 +47,9 @@ #include <google/protobuf/compiler/cpp/cpp_unittest.h> #include <memory> +#ifndef _SHARED_PTR_H +#include <google/protobuf/stubs/shared_ptr.h> +#endif #include <vector> #include <google/protobuf/unittest.pb.h> |