From 8a0312b20156aa4df092cb6b3c665ef48cb6fa54 Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Thu, 16 Jul 2015 17:03:06 +0100 Subject: First pass at wrapper types. - We do still generate the message types, as otherwise reflection breaks, even though it doesn't actually use those types. - JSON handling hasn't been implemented yet --- .../protobuf/compiler/csharp/csharp_field_base.cc | 41 ++++++----- .../protobuf/compiler/csharp/csharp_field_base.h | 1 + .../protobuf/compiler/csharp/csharp_map_field.cc | 3 +- .../csharp/csharp_repeated_message_field.cc | 15 +++- .../compiler/csharp/csharp_wrapper_field.cc | 83 +++++++++++----------- .../protobuf/unittest_well_known_types.proto | 20 +++--- 6 files changed, 93 insertions(+), 70 deletions(-) (limited to 'src') diff --git a/src/google/protobuf/compiler/csharp/csharp_field_base.cc b/src/google/protobuf/compiler/csharp/csharp_field_base.cc index f36e6fde..1f583e08 100644 --- a/src/google/protobuf/compiler/csharp/csharp_field_base.cc +++ b/src/google/protobuf/compiler/csharp/csharp_field_base.cc @@ -309,14 +309,23 @@ std::string FieldGeneratorBase::GetBytesDefaultValueInternal() { } std::string FieldGeneratorBase::default_value() { - switch (descriptor_->type()) { + return default_value(descriptor_); +} + +std::string FieldGeneratorBase::default_value(const FieldDescriptor* descriptor) { + switch (descriptor->type()) { case FieldDescriptor::TYPE_ENUM: - return type_name() + "." + descriptor_->default_value_enum()->name(); + return type_name() + "." + descriptor->default_value_enum()->name(); case FieldDescriptor::TYPE_MESSAGE: case FieldDescriptor::TYPE_GROUP: - return type_name() + ".DefaultInstance"; + if (IsWrapperType(descriptor)) { + const FieldDescriptor* wrapped_field = descriptor->message_type()->field(0); + return default_value(wrapped_field); + } else { + return "null"; + } case FieldDescriptor::TYPE_DOUBLE: { - double value = descriptor_->default_value_double(); + double value = descriptor->default_value_double(); if (value == numeric_limits::infinity()) { return "double.PositiveInfinity"; } else if (value == -numeric_limits::infinity()) { @@ -327,7 +336,7 @@ std::string FieldGeneratorBase::default_value() { return SimpleDtoa(value) + "D"; } case FieldDescriptor::TYPE_FLOAT: { - float value = descriptor_->default_value_float(); + float value = descriptor->default_value_float(); if (value == numeric_limits::infinity()) { return "float.PositiveInfinity"; } else if (value == -numeric_limits::infinity()) { @@ -338,17 +347,17 @@ std::string FieldGeneratorBase::default_value() { return SimpleFtoa(value) + "F"; } case FieldDescriptor::TYPE_INT64: - return SimpleItoa(descriptor_->default_value_int64()) + "L"; + return SimpleItoa(descriptor->default_value_int64()) + "L"; case FieldDescriptor::TYPE_UINT64: - return SimpleItoa(descriptor_->default_value_uint64()) + "UL"; + return SimpleItoa(descriptor->default_value_uint64()) + "UL"; case FieldDescriptor::TYPE_INT32: - return SimpleItoa(descriptor_->default_value_int32()); + return SimpleItoa(descriptor->default_value_int32()); case FieldDescriptor::TYPE_FIXED64: - return SimpleItoa(descriptor_->default_value_uint64()) + "UL"; + return SimpleItoa(descriptor->default_value_uint64()) + "UL"; case FieldDescriptor::TYPE_FIXED32: - return SimpleItoa(descriptor_->default_value_uint32()); + return SimpleItoa(descriptor->default_value_uint32()); case FieldDescriptor::TYPE_BOOL: - if (descriptor_->default_value_bool()) { + if (descriptor->default_value_bool()) { return "true"; } else { return "false"; @@ -358,15 +367,15 @@ std::string FieldGeneratorBase::default_value() { case FieldDescriptor::TYPE_BYTES: return GetBytesDefaultValueInternal(); case FieldDescriptor::TYPE_UINT32: - return SimpleItoa(descriptor_->default_value_uint32()); + return SimpleItoa(descriptor->default_value_uint32()); case FieldDescriptor::TYPE_SFIXED32: - return SimpleItoa(descriptor_->default_value_int32()); + return SimpleItoa(descriptor->default_value_int32()); case FieldDescriptor::TYPE_SFIXED64: - return SimpleItoa(descriptor_->default_value_int64()) + "L"; + return SimpleItoa(descriptor->default_value_int64()) + "L"; case FieldDescriptor::TYPE_SINT32: - return SimpleItoa(descriptor_->default_value_int32()); + return SimpleItoa(descriptor->default_value_int32()); case FieldDescriptor::TYPE_SINT64: - return SimpleItoa(descriptor_->default_value_int64()) + "L"; + return SimpleItoa(descriptor->default_value_int64()) + "L"; default: GOOGLE_LOG(FATAL)<< "Unknown field type."; return ""; diff --git a/src/google/protobuf/compiler/csharp/csharp_field_base.h b/src/google/protobuf/compiler/csharp/csharp_field_base.h index bffa2062..4761dc49 100644 --- a/src/google/protobuf/compiler/csharp/csharp_field_base.h +++ b/src/google/protobuf/compiler/csharp/csharp_field_base.h @@ -82,6 +82,7 @@ class FieldGeneratorBase : public SourceGeneratorBase { bool has_default_value(); bool is_nullable_type(); std::string default_value(); + std::string default_value(const FieldDescriptor* descriptor); std::string number(); std::string capitalized_type_name(); std::string field_ordinal(); diff --git a/src/google/protobuf/compiler/csharp/csharp_map_field.cc b/src/google/protobuf/compiler/csharp/csharp_map_field.cc index 32c05232..cba24a59 100644 --- a/src/google/protobuf/compiler/csharp/csharp_map_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_map_field.cc @@ -61,6 +61,7 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) { descriptor_->message_type()->FindFieldByName("value"); variables_["key_type_name"] = type_name(key_descriptor); variables_["value_type_name"] = type_name(value_descriptor); + variables_["true_for_wrappers"] = IsWrapperType(value_descriptor) ? "true" : ""; scoped_ptr key_generator(CreateFieldGenerator(key_descriptor, 1)); scoped_ptr value_generator(CreateFieldGenerator(value_descriptor, 2)); @@ -74,7 +75,7 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) { printer->Print( variables_, ", $tag$);\n" - "private readonly pbc::MapField<$key_type_name$, $value_type_name$> $name$_ = new pbc::MapField<$key_type_name$, $value_type_name$>();\n"); + "private readonly pbc::MapField<$key_type_name$, $value_type_name$> $name$_ = new pbc::MapField<$key_type_name$, $value_type_name$>($true_for_wrappers$);\n"); AddDeprecatedFlag(printer); printer->Print( variables_, diff --git a/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc b/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc index 6d4e6984..d939fc79 100644 --- a/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc @@ -39,6 +39,8 @@ #include #include +#include +#include namespace google { namespace protobuf { @@ -58,7 +60,18 @@ void RepeatedMessageFieldGenerator::GenerateMembers(io::Printer* printer) { printer->Print( variables_, "private static readonly pb::FieldCodec<$type_name$> _repeated_$name$_codec\n" - " = pb::FieldCodec.ForMessage($tag$, $type_name$.Parser);\n"); + " = "); + // Don't want to duplicate the codec code here... maybe we should have a + // "create single field generator for this repeated field" + // function, but it doesn't seem worth it for just this. + if (IsWrapperType(descriptor_)) { + scoped_ptr single_generator(new WrapperFieldGenerator(descriptor_, fieldOrdinal_)); + single_generator->GenerateCodecCode(printer); + } else { + scoped_ptr single_generator(new MessageFieldGenerator(descriptor_, fieldOrdinal_)); + single_generator->GenerateCodecCode(printer); + } + printer->Print(";\n"); printer->Print( variables_, "private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n"); diff --git a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc index eb3791ec..75ef5e50 100644 --- a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc @@ -50,35 +50,34 @@ WrapperFieldGenerator::WrapperFieldGenerator(const FieldDescriptor* descriptor, : FieldGeneratorBase(descriptor, fieldOrdinal) { variables_["has_property_check"] = name() + "_ != null"; variables_["has_not_property_check"] = name() + "_ == null"; - variables_["message_type_name"] = GetClassName(descriptor->message_type()); const FieldDescriptor* wrapped_field = descriptor->message_type()->field(0); is_value_type = wrapped_field->type() != FieldDescriptor::TYPE_STRING && wrapped_field->type() != FieldDescriptor::TYPE_BYTES; - variables_["deref"] = is_value_type ? ".Value" : ""; - // This will always be a single byte, because it's always field 1. - variables_["message_tag_bytes"] = SimpleItoa(FixedMakeTag(wrapped_field)); + if (is_value_type) { + variables_["nonnullable_type_name"] = type_name(wrapped_field); + } } WrapperFieldGenerator::~WrapperFieldGenerator() { } void WrapperFieldGenerator::GenerateMembers(io::Printer* printer) { - // Back the underlying property with an underlying message. This isn't efficient, - // but it makes it easier to be compliant with what platforms which don't support wrapper - // types would do. Currently, each time the value is changed, we create a new instance. - // With suitable care to avoid aliasing, we could probably check whether or not we've already - // got an instance, and simply mutate the existing one. + printer->Print( + variables_, + "private static readonly pb::FieldCodec<$type_name$> _single_$name$_codec = "); + GenerateCodecCode(printer); printer->Print( variables_, - "private $message_type_name$ $name$_;\n"); + ";\n" + "private $type_name$ $name$_;\n"); AddDeprecatedFlag(printer); printer->Print( variables_, "$access_level$ $type_name$ $property_name$ {\n" - " get { return $name$_ == null ? ($type_name$) null : $name$_.Value; }\n" + " get { return $name$_; }\n" " set {\n" " pb::Freezable.CheckMutable(this);\n" - " $name$_ = value == null ? null : new $message_type_name$ { Value = value$deref$ };\n" + " $name$_ = value;\n" " }\n" "}\n"); } @@ -87,28 +86,26 @@ void WrapperFieldGenerator::GenerateMergingCode(io::Printer* printer) { printer->Print( variables_, "if (other.$has_property_check$) {\n" - " if ($has_not_property_check$) {\n" - " $name$_ = new $message_type_name$();\n" + " if ($has_not_property_check$ || other.$property_name$ != $default_value$) {\n" + " $property_name$ = other.$property_name$;\n" " }\n" - " $name$_.MergeFrom(other.$name$_);\n" "}\n"); } void WrapperFieldGenerator::GenerateParsingCode(io::Printer* printer) { printer->Print( variables_, - "if ($has_not_property_check$) {\n" - " $name$_ = new $message_type_name$();\n" - "}\n" - "input.ReadMessage($name$_);\n"); // No need to support TYPE_GROUP... + "$type_name$ value = _single_$name$_codec.Read(input);\n" + "if ($has_not_property_check$ || value != $default_value$) {\n" + " $property_name$ = value;\n" + "}\n"); } void WrapperFieldGenerator::GenerateSerializationCode(io::Printer* printer) { printer->Print( variables_, "if ($has_property_check$) {\n" - " output.WriteRawTag($tag_bytes$);\n" - " output.WriteMessage($name$_);\n" + " _single_$name$_codec.WriteTagAndValue(output, $property_name$);\n" "}\n"); } @@ -116,7 +113,7 @@ void WrapperFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) { printer->Print( variables_, "if ($has_property_check$) {\n" - " size += $tag_size$ + pb::CodedOutputStream.ComputeMessageSize($name$_);\n" + " size += _single_$name$_codec.CalculateSizeWithTag($property_name$);\n" "}\n"); } @@ -137,16 +134,20 @@ void WrapperFieldGenerator::WriteToString(io::Printer* printer) { } void WrapperFieldGenerator::GenerateCloningCode(io::Printer* printer) { - // This will effectively perform a deep clone - it will create a new - // underlying message if necessary printer->Print(variables_, "$property_name$ = other.$property_name$;\n"); } void WrapperFieldGenerator::GenerateCodecCode(io::Printer* printer) { - printer->Print( - variables_, - "pb::FieldCodec.ForWrapperType<$type_name$, $message_type_name$>($tag$, $message_type_name$.Parser)"); + if (is_value_type) { + printer->Print( + variables_, + "pb::FieldCodec.ForStructWrapper<$nonnullable_type_name$>($tag$)"); + } else { + printer->Print( + variables_, + "pb::FieldCodec.ForClassWrapper<$type_name$>($tag$)"); + } } WrapperOneofFieldGenerator::WrapperOneofFieldGenerator(const FieldDescriptor* descriptor, @@ -159,48 +160,46 @@ WrapperOneofFieldGenerator::~WrapperOneofFieldGenerator() { } void WrapperOneofFieldGenerator::GenerateMembers(io::Printer* printer) { + // Note: deliberately _oneof_$name$_codec, not _$oneof_name$_codec... we have one codec per field. + printer->Print( + variables_, + "private static readonly pb::FieldCodec<$type_name$> _oneof_$name$_codec = "); + GenerateCodecCode(printer); + printer->Print(";\n"); AddDeprecatedFlag(printer); printer->Print( variables_, "$access_level$ $type_name$ $property_name$ {\n" - " get { return $has_property_check$ ? (($message_type_name$) $oneof_name$_).Value : ($type_name$) null; }\n" + " get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : ($type_name$) null; }\n" " set {\n" " pb::Freezable.CheckMutable(this);\n" - " $oneof_name$_ = value == null ? null : new $message_type_name$ { Value = value$deref$ };\n" + " $oneof_name$_ = value;\n" " $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$property_name$;\n" " }\n" "}\n"); } - - void WrapperOneofFieldGenerator::GenerateParsingCode(io::Printer* printer) { printer->Print( variables_, - "$message_type_name$ subBuilder = new $message_type_name$();\n" - "if ($has_property_check$) {\n" - " subBuilder.MergeFrom(($message_type_name$) $oneof_name$_);\n" - "}\n" - "input.ReadMessage(subBuilder);\n" - // Don't set the property, which would create a new and equivalent message; just set the two fields. - "$oneof_name$Case_ = $oneof_property_name$OneofCase.$property_name$;\n" - "$oneof_name$_ = subBuilder;\n"); + "$property_name$ = _oneof_$name$_codec.Read(input);\n"); } void WrapperOneofFieldGenerator::GenerateSerializationCode(io::Printer* printer) { + // TODO: I suspect this is wrong... printer->Print( variables_, "if ($has_property_check$) {\n" - " output.WriteRawTag($tag_bytes$);\n" - " output.WriteMessage(($message_type_name$) $oneof_name$_);\n" + " _oneof_$name$_codec.WriteTagAndValue(output, ($type_name$) $oneof_name$_);\n" "}\n"); } void WrapperOneofFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) { + // TODO: I suspect this is wrong... printer->Print( variables_, "if ($has_property_check$) {\n" - " size += $tag_size$ + pb::CodedOutputStream.ComputeMessageSize((($message_type_name$) $oneof_name$_));\n" + " size += _oneof_$name$_codec.CalculateSizeWithTag($property_name$);\n" "}\n"); } diff --git a/src/google/protobuf/unittest_well_known_types.proto b/src/google/protobuf/unittest_well_known_types.proto index 4771c094..2cb7775c 100644 --- a/src/google/protobuf/unittest_well_known_types.proto +++ b/src/google/protobuf/unittest_well_known_types.proto @@ -52,16 +52,16 @@ message RepeatedWellKnownTypes { repeated google.protobuf.Struct struct_field = 7; repeated google.protobuf.Timestamp timestamp_field = 8; repeated google.protobuf.Type type_field = 9; - // TODO: Do these even make sense? Should they be prohibited? -// repeated google.protobuf.DoubleValue double_field = 10; -// repeated google.protobuf.FloatValue float_field = 11; -// repeated google.protobuf.Int64Value int64_field = 12; -// repeated google.protobuf.UInt64Value uint64_field = 13; -// repeated google.protobuf.Int32Value int32_field = 14; -// repeated google.protobuf.UInt32Value uint32_field = 15; -// repeated google.protobuf.BoolValue bool_field = 16; -// repeated google.protobuf.StringValue string_field = 17; -// repeated google.protobuf.BytesValue bytes_field = 18; + // These don't actually make a lot of sense, but they're not prohibited... + repeated google.protobuf.DoubleValue double_field = 10; + repeated google.protobuf.FloatValue float_field = 11; + repeated google.protobuf.Int64Value int64_field = 12; + repeated google.protobuf.UInt64Value uint64_field = 13; + repeated google.protobuf.Int32Value int32_field = 14; + repeated google.protobuf.UInt32Value uint32_field = 15; + repeated google.protobuf.BoolValue bool_field = 16; + repeated google.protobuf.StringValue string_field = 17; + repeated google.protobuf.BytesValue bytes_field = 18; } message OneofWellKnownTypes { -- cgit v1.2.3