From 047575f20cbdee8fb2f88d78a317ffe6133b93e6 Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Mon, 16 Jan 2017 11:23:32 +0000 Subject: Support custom options in C# This consists of: - Changing the codegen for the fixed set of options protos, to parse unknown fields instead of skipping them - Add a new CustomOptions type in the C# support library - Expose CustomOptions properties from the immutable proto wrappers in the support library Only single-value options are currently supported, and fetching options values requires getting the type right and knowing the field number. Both of these can be addressed at a later time. Fixes #2143, at least as a first pass. --- .../protobuf/compiler/csharp/csharp_helpers.h | 16 ++++++++++++++++ .../protobuf/compiler/csharp/csharp_message.cc | 22 ++++++++++++++++++---- 2 files changed, 34 insertions(+), 4 deletions(-) (limited to 'src/google') diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.h b/src/google/protobuf/compiler/csharp/csharp_helpers.h index 1563ca7e..c317ad0e 100644 --- a/src/google/protobuf/compiler/csharp/csharp_helpers.h +++ b/src/google/protobuf/compiler/csharp/csharp_helpers.h @@ -120,6 +120,22 @@ inline bool IsDescriptorProto(const FileDescriptor* descriptor) { return descriptor->name() == "google/protobuf/descriptor.proto"; } +// Determines whether the given message is an options message within descriptor.proto. +inline bool IsDescriptorOptionMessage(const Descriptor* descriptor) { + if (!IsDescriptorProto(descriptor->file())) { + return false; + } + const string name = descriptor->full_name(); + return name == "google.protobuf.FileOptions" || + name == "google.protobuf.MessageOptions" || + name == "google.protobuf.FieldOptions" || + name == "google.protobuf.OneofOptions" || + name == "google.protobuf.EnumOptions" || + name == "google.protobuf.EnumValueOptions" || + name == "google.protobuf.ServiceOptions" || + name == "google.protobuf.MethodOptions"; +} + inline bool IsWrapperType(const FieldDescriptor* descriptor) { return descriptor->type() == FieldDescriptor::TYPE_MESSAGE && descriptor->message_type()->file()->name() == "google/protobuf/wrappers.proto"; diff --git a/src/google/protobuf/compiler/csharp/csharp_message.cc b/src/google/protobuf/compiler/csharp/csharp_message.cc index b0cc6694..0f00a438 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message.cc +++ b/src/google/protobuf/compiler/csharp/csharp_message.cc @@ -151,6 +151,12 @@ void MessageGenerator::Generate(io::Printer* printer) { " get { return Descriptor; }\n" "}\n" "\n"); + // CustomOptions property, only for options messages + if (IsDescriptorOptionMessage(descriptor_)) { + printer->Print( + "internal CustomOptions CustomOptions{ get; private set; } = CustomOptions.Empty;\n" + "\n"); + } // Parameterless constructor and partial OnConstruction method. WriteGeneratedCodeAttributes(printer); @@ -475,10 +481,18 @@ void MessageGenerator::GenerateMergingMethods(io::Printer* printer) { " switch(tag) {\n"); printer->Indent(); printer->Indent(); - printer->Print( - "default:\n" - " input.SkipLastField();\n" // We're not storing the data, but we still need to consume it. - " break;\n"); + // Option messages need to store unknown fields so that options can be parsed later. + if (IsDescriptorOptionMessage(descriptor_)) { + printer->Print( + "default:\n" + " CustomOptions = CustomOptions.ReadOrSkipUnknownField(input);\n" + " break;\n"); + } else { + printer->Print( + "default:\n" + " input.SkipLastField();\n" // We're not storing the data, but we still need to consume it. + " break;\n"); + } for (int i = 0; i < fields_by_number().size(); i++) { const FieldDescriptor* field = fields_by_number()[i]; internal::WireFormatLite::WireType wt = -- cgit v1.2.3