diff options
author | Jon Skeet <jonskeet@google.com> | 2015-07-15 13:17:42 +0100 |
---|---|---|
committer | Jon Skeet <jonskeet@google.com> | 2015-07-16 09:36:30 +0100 |
commit | b2ac868493742327b2ebbcacd97947126f8841f7 (patch) | |
tree | d6cac75812d3953e096d6195f6006fb44714b1f9 /src/google | |
parent | 7ea5239792c3c449e7ae36caa3a9de04b90d0bbe (diff) | |
download | protobuf-b2ac868493742327b2ebbcacd97947126f8841f7.tar.gz protobuf-b2ac868493742327b2ebbcacd97947126f8841f7.tar.bz2 protobuf-b2ac868493742327b2ebbcacd97947126f8841f7.zip |
First part of implementing wrapper types. Not ready yet!
Diffstat (limited to 'src/google')
6 files changed, 423 insertions, 16 deletions
diff --git a/src/google/protobuf/compiler/csharp/csharp_field_base.cc b/src/google/protobuf/compiler/csharp/csharp_field_base.cc index 914b972d..f36e6fde 100644 --- a/src/google/protobuf/compiler/csharp/csharp_field_base.cc +++ b/src/google/protobuf/compiler/csharp/csharp_field_base.cc @@ -168,6 +168,18 @@ std::string FieldGeneratorBase::type_name(const FieldDescriptor* descriptor) { return GetClassName(descriptor->enum_type()); case FieldDescriptor::TYPE_MESSAGE: case FieldDescriptor::TYPE_GROUP: + if (IsWrapperType(descriptor)) { + const FieldDescriptor* wrapped_field = descriptor->message_type()->field(0); + string wrapped_field_type_name = type_name(wrapped_field); + // String and ByteString go to the same type; other wrapped types go to the + // nullable equivalent. + if (wrapped_field->type() == FieldDescriptor::TYPE_STRING || + wrapped_field->type() == FieldDescriptor::TYPE_BYTES) { + return wrapped_field_type_name; + } else { + return wrapped_field_type_name + "?"; + } + } return GetClassName(descriptor->message_type()); case FieldDescriptor::TYPE_DOUBLE: return "double"; diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.cc b/src/google/protobuf/compiler/csharp/csharp_helpers.cc index 8ecd1dc2..1c7a24a9 100644 --- a/src/google/protobuf/compiler/csharp/csharp_helpers.cc +++ b/src/google/protobuf/compiler/csharp/csharp_helpers.cc @@ -52,6 +52,7 @@ #include <google/protobuf/compiler/csharp/csharp_repeated_enum_field.h> #include <google/protobuf/compiler/csharp/csharp_repeated_message_field.h> #include <google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h> +#include <google/protobuf/compiler/csharp/csharp_wrapper_field.h> namespace google { namespace protobuf { @@ -366,31 +367,39 @@ FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor, return new RepeatedMessageFieldGenerator(descriptor, fieldOrdinal); } } else { - if (descriptor->containing_oneof()) { - return new MessageOneofFieldGenerator(descriptor, fieldOrdinal); - } else { - return new MessageFieldGenerator(descriptor, fieldOrdinal); - } + if (IsWrapperType(descriptor)) { + if (descriptor->containing_oneof()) { + return new WrapperOneofFieldGenerator(descriptor, fieldOrdinal); + } else { + return new WrapperFieldGenerator(descriptor, fieldOrdinal); + } + } else { + if (descriptor->containing_oneof()) { + return new MessageOneofFieldGenerator(descriptor, fieldOrdinal); + } else { + return new MessageFieldGenerator(descriptor, fieldOrdinal); + } + } } case FieldDescriptor::TYPE_ENUM: if (descriptor->is_repeated()) { return new RepeatedEnumFieldGenerator(descriptor, fieldOrdinal); } else { - if (descriptor->containing_oneof()) { - return new EnumOneofFieldGenerator(descriptor, fieldOrdinal); - } else { - return new EnumFieldGenerator(descriptor, fieldOrdinal); - } + if (descriptor->containing_oneof()) { + return new EnumOneofFieldGenerator(descriptor, fieldOrdinal); + } else { + return new EnumFieldGenerator(descriptor, fieldOrdinal); + } } default: if (descriptor->is_repeated()) { return new RepeatedPrimitiveFieldGenerator(descriptor, fieldOrdinal); } else { - if (descriptor->containing_oneof()) { - return new PrimitiveOneofFieldGenerator(descriptor, fieldOrdinal); - } else { - return new PrimitiveFieldGenerator(descriptor, fieldOrdinal); - } + if (descriptor->containing_oneof()) { + return new PrimitiveOneofFieldGenerator(descriptor, fieldOrdinal); + } else { + return new PrimitiveFieldGenerator(descriptor, fieldOrdinal); + } } } } diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.h b/src/google/protobuf/compiler/csharp/csharp_helpers.h index a7c2395b..b6a75b34 100644 --- a/src/google/protobuf/compiler/csharp/csharp_helpers.h +++ b/src/google/protobuf/compiler/csharp/csharp_helpers.h @@ -120,6 +120,15 @@ inline bool IsDescriptorProto(const FileDescriptor* descriptor) { return descriptor->name() == "google/protobuf/descriptor_proto_file.proto"; } +inline bool IsMapEntry(const Descriptor* descriptor) { + return descriptor->options().map_entry(); +} + +inline bool IsWrapperType(const FieldDescriptor* descriptor) { + return descriptor->type() == FieldDescriptor::TYPE_MESSAGE && + descriptor->message_type()->file()->name() == "google/protobuf/wrappers.proto"; +} + } // namespace csharp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc new file mode 100644 index 00000000..eb3791ec --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc @@ -0,0 +1,210 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <sstream> + +#include <google/protobuf/compiler/code_generator.h> +#include <google/protobuf/compiler/plugin.h> +#include <google/protobuf/descriptor.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/io/zero_copy_stream.h> + +#include <google/protobuf/compiler/csharp/csharp_helpers.h> +#include <google/protobuf/compiler/csharp/csharp_wrapper_field.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +WrapperFieldGenerator::WrapperFieldGenerator(const FieldDescriptor* descriptor, + int fieldOrdinal) + : 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)); +} + +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 $message_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" + " set {\n" + " pb::Freezable.CheckMutable(this);\n" + " $name$_ = value == null ? null : new $message_type_name$ { Value = value$deref$ };\n" + " }\n" + "}\n"); +} + +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" + " }\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... +} + +void WrapperFieldGenerator::GenerateSerializationCode(io::Printer* printer) { + printer->Print( + variables_, + "if ($has_property_check$) {\n" + " output.WriteRawTag($tag_bytes$);\n" + " output.WriteMessage($name$_);\n" + "}\n"); +} + +void WrapperFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) { + printer->Print( + variables_, + "if ($has_property_check$) {\n" + " size += $tag_size$ + pb::CodedOutputStream.ComputeMessageSize($name$_);\n" + "}\n"); +} + +void WrapperFieldGenerator::WriteHash(io::Printer* printer) { + printer->Print( + variables_, + "if ($has_property_check$) hash ^= $property_name$.GetHashCode();\n"); +} + +void WrapperFieldGenerator::WriteEquals(io::Printer* printer) { + printer->Print( + variables_, + "if ($property_name$ != other.$property_name$) return false;\n"); +} + +void WrapperFieldGenerator::WriteToString(io::Printer* printer) { + // TODO: Implement if we ever actually need it... +} + +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)"); +} + +WrapperOneofFieldGenerator::WrapperOneofFieldGenerator(const FieldDescriptor* descriptor, + int fieldOrdinal) + : WrapperFieldGenerator(descriptor, fieldOrdinal) { + SetCommonOneofFieldVariables(&variables_); +} + +WrapperOneofFieldGenerator::~WrapperOneofFieldGenerator() { +} + +void WrapperOneofFieldGenerator::GenerateMembers(io::Printer* printer) { + 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" + " set {\n" + " pb::Freezable.CheckMutable(this);\n" + " $oneof_name$_ = value == null ? null : new $message_type_name$ { Value = value$deref$ };\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"); +} + +void WrapperOneofFieldGenerator::GenerateSerializationCode(io::Printer* printer) { + printer->Print( + variables_, + "if ($has_property_check$) {\n" + " output.WriteRawTag($tag_bytes$);\n" + " output.WriteMessage(($message_type_name$) $oneof_name$_);\n" + "}\n"); +} + +void WrapperOneofFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) { + printer->Print( + variables_, + "if ($has_property_check$) {\n" + " size += $tag_size$ + pb::CodedOutputStream.ComputeMessageSize((($message_type_name$) $oneof_name$_));\n" + "}\n"); +} + +} // namespace csharp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.h b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.h new file mode 100644 index 00000000..5e2c5bc3 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.h @@ -0,0 +1,86 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_WRAPPER_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_CSHARP_WRAPPER_FIELD_H__ + +#include <string> + +#include <google/protobuf/compiler/code_generator.h> +#include <google/protobuf/compiler/csharp/csharp_field_base.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +class WrapperFieldGenerator : public FieldGeneratorBase { + public: + WrapperFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal); + ~WrapperFieldGenerator(); + + virtual void GenerateCodecCode(io::Printer* printer); + virtual void GenerateCloningCode(io::Printer* printer); + virtual void GenerateMembers(io::Printer* printer); + virtual void GenerateMergingCode(io::Printer* printer); + virtual void GenerateParsingCode(io::Printer* printer); + virtual void GenerateSerializationCode(io::Printer* printer); + virtual void GenerateSerializedSizeCode(io::Printer* printer); + + virtual void WriteHash(io::Printer* printer); + virtual void WriteEquals(io::Printer* printer); + virtual void WriteToString(io::Printer* printer); + + private: + bool is_value_type; // True for int32 etc; false for bytes and string + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(WrapperFieldGenerator); +}; + +class WrapperOneofFieldGenerator : public WrapperFieldGenerator { + public: + WrapperOneofFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal); + ~WrapperOneofFieldGenerator(); + + virtual void GenerateMembers(io::Printer* printer); + virtual void GenerateParsingCode(io::Printer* printer); + virtual void GenerateSerializationCode(io::Printer* printer); + virtual void GenerateSerializedSizeCode(io::Printer* printer); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(WrapperOneofFieldGenerator); +}; + +} // namespace csharp +} // namespace compiler +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_WRAPPER_FIELD_H__ + diff --git a/src/google/protobuf/unittest_well_known_types.proto b/src/google/protobuf/unittest_well_known_types.proto index e157260e..4771c094 100644 --- a/src/google/protobuf/unittest_well_known_types.proto +++ b/src/google/protobuf/unittest_well_known_types.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package protobuf_unittest; +option csharp_namespace = "Google.Protobuf.TestProtos"; option java_multiple_files = true; option java_package = "com.google.protobuf.test"; @@ -17,6 +18,8 @@ import "google/protobuf/type.proto"; import "google/protobuf/wrappers.proto"; // Test that we can include all well-known types. +// Each wrapper type is included separately, as languages +// map handle different wrappers in different ways. message TestWellKnownTypes { google.protobuf.Any any_field = 1; google.protobuf.Api api_field = 2; @@ -27,5 +30,83 @@ message TestWellKnownTypes { google.protobuf.Struct struct_field = 7; google.protobuf.Timestamp timestamp_field = 8; google.protobuf.Type type_field = 9; - google.protobuf.Int32Value int32_field = 10; + google.protobuf.DoubleValue double_field = 10; + google.protobuf.FloatValue float_field = 11; + google.protobuf.Int64Value int64_field = 12; + google.protobuf.UInt64Value uint64_field = 13; + google.protobuf.Int32Value int32_field = 14; + google.protobuf.UInt32Value uint32_field = 15; + google.protobuf.BoolValue bool_field = 16; + google.protobuf.StringValue string_field = 17; + google.protobuf.BytesValue bytes_field = 18; +} + +// A repeated field for each well-known type. +message RepeatedWellKnownTypes { + repeated google.protobuf.Any any_field = 1; + repeated google.protobuf.Api api_field = 2; + repeated google.protobuf.Duration duration_field = 3; + repeated google.protobuf.Empty empty_field = 4; + repeated google.protobuf.FieldMask field_mask_field = 5; + repeated google.protobuf.SourceContext source_context_field = 6; + 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; +} + +message OneofWellKnownTypes { + oneof oneof_field { + google.protobuf.Any any_field = 1; + google.protobuf.Api api_field = 2; + google.protobuf.Duration duration_field = 3; + google.protobuf.Empty empty_field = 4; + google.protobuf.FieldMask field_mask_field = 5; + google.protobuf.SourceContext source_context_field = 6; + google.protobuf.Struct struct_field = 7; + google.protobuf.Timestamp timestamp_field = 8; + google.protobuf.Type type_field = 9; + google.protobuf.DoubleValue double_field = 10; + google.protobuf.FloatValue float_field = 11; + google.protobuf.Int64Value int64_field = 12; + google.protobuf.UInt64Value uint64_field = 13; + google.protobuf.Int32Value int32_field = 14; + google.protobuf.UInt32Value uint32_field = 15; + google.protobuf.BoolValue bool_field = 16; + google.protobuf.StringValue string_field = 17; + google.protobuf.BytesValue bytes_field = 18; + } +} + +// A map field for each well-known type. We only +// need to worry about the value part of the map being the +// well-known types, as messages can't be map keys. +message MapWellKnownTypes { + map<int32,google.protobuf.Any> any_field = 1; + map<int32,google.protobuf.Api> api_field = 2; + map<int32,google.protobuf.Duration> duration_field = 3; + map<int32,google.protobuf.Empty> empty_field = 4; + map<int32,google.protobuf.FieldMask> field_mask_field = 5; + map<int32,google.protobuf.SourceContext> source_context_field = 6; + map<int32,google.protobuf.Struct> struct_field = 7; + map<int32,google.protobuf.Timestamp> timestamp_field = 8; + map<int32,google.protobuf.Type> type_field = 9; + map<int32,google.protobuf.DoubleValue> double_field = 10; + map<int32,google.protobuf.FloatValue> float_field = 11; + map<int32,google.protobuf.Int64Value> int64_field = 12; + map<int32,google.protobuf.UInt64Value> uint64_field = 13; + map<int32,google.protobuf.Int32Value> int32_field = 14; + map<int32,google.protobuf.UInt32Value> uint32_field = 15; + map<int32,google.protobuf.BoolValue> bool_field = 16; + map<int32,google.protobuf.StringValue> string_field = 17; + map<int32,google.protobuf.BytesValue> bytes_field = 18; } |