Browse Source

Revamp to reflection.

Changes in brief:
1. Descriptor is now the entry point for all reflection.
2. IReflectedMessage has gone; there's now a Descriptor property in IMessage, which is explicitly implemented (due to the static property).
3. FieldAccessorTable has gone away
4. IFieldAccessor and OneofFieldAccessor still exist; we *could* put the functionality straight into FieldDescriptor and OneofDescriptor... I'm unsure about that.
5. There's a temporary property MessageDescriptor.FieldAccessorsByFieldNumber to make the test changes small - we probably want this to go away
6. Discovery for delegates is now via attributes applied to properties and the Clear method of a oneof

I'm happy with 1-3.
4 I'm unsure about - feedback welcome.
5 will go away
6 I'm unsure about, both in design and implementation. Should we have a ProtobufMessageAttribute too? Should we find all the relevant attributes in MessageDescriptor and pass them down, to avoid an O(N^2) scenario?

Generated code changes coming in the next commit.
Jon Skeet 10 năm trước cách đây
mục cha
commit
53c399a1d6
35 tập tin đã thay đổi với 372 bổ sung278 xóa
  1. 24 23
      csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
  2. 1 0
      csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs
  3. 3 3
      csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs
  4. 3 0
      csharp/src/Google.Protobuf/Collections/MapField.cs
  5. 2 1
      csharp/src/Google.Protobuf/Google.Protobuf.csproj
  6. 8 10
      csharp/src/Google.Protobuf/IMessage.cs
  7. 13 13
      csharp/src/Google.Protobuf/JsonFormatter.cs
  8. 2 3
      csharp/src/Google.Protobuf/Reflection/DescriptorUtil.cs
  9. 9 1
      csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs
  10. 1 6
      csharp/src/Google.Protobuf/Reflection/FieldAccessorBase.cs
  11. 0 97
      csharp/src/Google.Protobuf/Reflection/FieldAccessorTable.cs
  12. 28 0
      csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
  13. 29 7
      csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
  14. 2 1
      csharp/src/Google.Protobuf/Reflection/MapFieldAccessor.cs
  15. 31 3
      csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
  16. 4 6
      csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs
  17. 34 0
      csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs
  18. 58 0
      csharp/src/Google.Protobuf/Reflection/ProtobufFieldAttribute.cs
  19. 52 0
      csharp/src/Google.Protobuf/Reflection/ProtobufOneofAttribute.cs
  20. 20 0
      csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs
  21. 2 1
      csharp/src/Google.Protobuf/Reflection/RepeatedFieldAccessor.cs
  22. 1 4
      csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs
  23. 1 1
      src/google/protobuf/compiler/csharp/csharp_field_base.cc
  24. 2 4
      src/google/protobuf/compiler/csharp/csharp_helpers.h
  25. 1 0
      src/google/protobuf/compiler/csharp/csharp_map_field.cc
  26. 5 79
      src/google/protobuf/compiler/csharp/csharp_message.cc
  27. 0 2
      src/google/protobuf/compiler/csharp/csharp_message.h
  28. 2 0
      src/google/protobuf/compiler/csharp/csharp_message_field.cc
  29. 2 0
      src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
  30. 2 1
      src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc
  31. 1 0
      src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc
  32. 1 0
      src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc
  33. 25 12
      src/google/protobuf/compiler/csharp/csharp_umbrella_class.cc
  34. 1 0
      src/google/protobuf/compiler/csharp/csharp_umbrella_class.h
  35. 2 0
      src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc

+ 24 - 23
csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs

@@ -604,7 +604,7 @@ namespace Google.Protobuf
         public void Reflection_GetValue()
         {
             var message = SampleMessages.CreateFullTestAllTypes();
-            var fields = ((IReflectedMessage) message).Fields;
+            var fields = TestAllTypes.Descriptor.FieldAccessorsByFieldNumber;
             Assert.AreEqual(message.SingleBool, fields[TestAllTypes.SingleBoolFieldNumber].GetValue(message));
             Assert.AreEqual(message.SingleBytes, fields[TestAllTypes.SingleBytesFieldNumber].GetValue(message));
             Assert.AreEqual(message.SingleDouble, fields[TestAllTypes.SingleDoubleFieldNumber].GetValue(message));
@@ -639,7 +639,7 @@ namespace Google.Protobuf
 
             // Just a single map field, for the same reason
             var mapMessage = new TestMap { MapStringString = { { "key1", "value1" }, { "key2", "value2" } } };
-            fields = ((IReflectedMessage) mapMessage).Fields;
+            fields = TestMap.Descriptor.FieldAccessorsByFieldNumber;
             var dictionary = (IDictionary) fields[TestMap.MapStringStringFieldNumber].GetValue(mapMessage);
             Assert.AreEqual(mapMessage.MapStringString, dictionary);
             Assert.AreEqual("value1", dictionary["key1"]);
@@ -648,8 +648,8 @@ namespace Google.Protobuf
         [Test]
         public void Reflection_Clear()
         {
-            IReflectedMessage message = SampleMessages.CreateFullTestAllTypes();
-            var fields = message.Fields;
+            var message = SampleMessages.CreateFullTestAllTypes();
+            var fields = TestAllTypes.Descriptor.FieldAccessorsByFieldNumber;
             fields[TestAllTypes.SingleBoolFieldNumber].Clear(message);
             fields[TestAllTypes.SingleInt32FieldNumber].Clear(message);
             fields[TestAllTypes.SingleStringFieldNumber].Clear(message);
@@ -673,7 +673,7 @@ namespace Google.Protobuf
 
             // Separately, maps.
             var mapMessage = new TestMap { MapStringString = { { "key1", "value1" }, { "key2", "value2" } } };
-            fields = ((IReflectedMessage) mapMessage).Fields;
+            fields = TestMap.Descriptor.FieldAccessorsByFieldNumber;
             fields[TestMap.MapStringStringFieldNumber].Clear(mapMessage);
             Assert.AreEqual(0, mapMessage.MapStringString.Count);
         }
@@ -682,8 +682,8 @@ namespace Google.Protobuf
         public void Reflection_SetValue_SingleFields()
         {
             // Just a sample (primitives, messages, enums, strings, byte strings)
-            IReflectedMessage message = SampleMessages.CreateFullTestAllTypes();
-            var fields = message.Fields;
+            var message = SampleMessages.CreateFullTestAllTypes();
+            var fields = TestAllTypes.Descriptor.FieldAccessorsByFieldNumber;
             fields[TestAllTypes.SingleBoolFieldNumber].SetValue(message, false);
             fields[TestAllTypes.SingleInt32FieldNumber].SetValue(message, 500);
             fields[TestAllTypes.SingleStringFieldNumber].SetValue(message, "It's a string");
@@ -709,51 +709,52 @@ namespace Google.Protobuf
         [Test]
         public void Reflection_SetValue_SingleFields_WrongType()
         {
-            IReflectedMessage message = SampleMessages.CreateFullTestAllTypes();
-            var fields = message.Fields;
+            IMessage message = SampleMessages.CreateFullTestAllTypes();
+            var fields = message.Descriptor.FieldAccessorsByFieldNumber;
             Assert.Throws<InvalidCastException>(() => fields[TestAllTypes.SingleBoolFieldNumber].SetValue(message, "This isn't a bool"));
         }
 
         [Test]
         public void Reflection_SetValue_MapFields()
         {
-            IReflectedMessage message = new TestMap();
-            var fields = message.Fields;
+            IMessage message = new TestMap();
+            var fields = message.Descriptor.FieldAccessorsByFieldNumber;
             Assert.Throws<InvalidOperationException>(() => fields[TestMap.MapStringStringFieldNumber].SetValue(message, new Dictionary<string, string>()));
         }
 
         [Test]
         public void Reflection_SetValue_RepeatedFields()
         {
-            IReflectedMessage message = SampleMessages.CreateFullTestAllTypes();
-            var fields = message.Fields;
+            IMessage message = SampleMessages.CreateFullTestAllTypes();
+            var fields = message.Descriptor.FieldAccessorsByFieldNumber;
             Assert.Throws<InvalidOperationException>(() => fields[TestAllTypes.RepeatedDoubleFieldNumber].SetValue(message, new double[10]));
         }
 
         [Test]
         public void Reflection_GetValue_IncorrectType()
         {
-            IReflectedMessage message = SampleMessages.CreateFullTestAllTypes();
-            Assert.Throws<InvalidCastException>(() => message.Fields[TestAllTypes.SingleBoolFieldNumber].GetValue(new TestMap()));
+            IMessage message = SampleMessages.CreateFullTestAllTypes();
+            var fields = message.Descriptor.FieldAccessorsByFieldNumber;
+            Assert.Throws<InvalidCastException>(() => fields[TestAllTypes.SingleBoolFieldNumber].GetValue(new TestMap()));
         }
 
         [Test]
         public void Reflection_Oneof()
         {
             var message = new TestAllTypes();
-            var fields = ((IReflectedMessage) message).Fields;
-            Assert.AreEqual(1, fields.Oneofs.Count);
-            var oneof = fields.Oneofs[0];
-            Assert.AreEqual("oneof_field", oneof.Descriptor.Name);
-            Assert.IsNull(oneof.GetCaseFieldDescriptor(message));
+            var descriptor = TestAllTypes.Descriptor;
+            Assert.AreEqual(1, descriptor.Oneofs.Count);
+            var oneof = descriptor.Oneofs[0];
+            Assert.AreEqual("oneof_field", oneof.Name);
+            Assert.IsNull(oneof.Accessor.GetCaseFieldDescriptor(message));
 
             message.OneofString = "foo";
-            Assert.AreSame(fields[TestAllTypes.OneofStringFieldNumber].Descriptor, oneof.GetCaseFieldDescriptor(message));
+            Assert.AreSame(descriptor.FieldAccessorsByFieldNumber[TestAllTypes.OneofStringFieldNumber].Descriptor, oneof.Accessor.GetCaseFieldDescriptor(message));
 
             message.OneofUint32 = 10;
-            Assert.AreSame(fields[TestAllTypes.OneofUint32FieldNumber].Descriptor, oneof.GetCaseFieldDescriptor(message));
+            Assert.AreSame(descriptor.FieldAccessorsByFieldNumber[TestAllTypes.OneofUint32FieldNumber].Descriptor, oneof.Accessor.GetCaseFieldDescriptor(message));
 
-            oneof.Clear(message);
+            oneof.Accessor.Clear(message);
             Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
         }
     }

+ 1 - 0
csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs

@@ -62,6 +62,7 @@ namespace Google.Protobuf.Reflection
             Assert.AreEqual(UnittestImportProto3.Descriptor, file.Dependencies[0]);
 
             MessageDescriptor messageType = TestAllTypes.Descriptor;
+            Assert.AreSame(typeof(TestAllTypes), messageType.GeneratedType);
             Assert.AreEqual(messageType, file.MessageTypes[0]);
             Assert.AreEqual(messageType, file.FindTypeByName<MessageDescriptor>("TestAllTypes"));
             Assert.Null(file.FindTypeByName<MessageDescriptor>("NoSuchType"));

+ 3 - 3
csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs

@@ -192,7 +192,7 @@ namespace Google.Protobuf.WellKnownTypes
                 Uint32Field = 3,
                 Uint64Field = 4
             };
-            var fields = ((IReflectedMessage) message).Fields;
+            var fields = TestWellKnownTypes.Descriptor.FieldAccessorsByFieldNumber;
 
             Assert.AreEqual("x", fields[TestWellKnownTypes.StringFieldFieldNumber].GetValue(message));
             Assert.AreEqual(ByteString.CopyFrom(1, 2, 3), fields[TestWellKnownTypes.BytesFieldFieldNumber].GetValue(message));
@@ -216,7 +216,7 @@ namespace Google.Protobuf.WellKnownTypes
         {
             // Just a single example... note that we can't have a null value here
             var message = new RepeatedWellKnownTypes { Int32Field = { 1, 2 } };
-            var fields = ((IReflectedMessage) message).Fields;
+            var fields = RepeatedWellKnownTypes.Descriptor.FieldAccessorsByFieldNumber;
             var list = (IList) fields[RepeatedWellKnownTypes.Int32FieldFieldNumber].GetValue(message);
             CollectionAssert.AreEqual(new[] { 1, 2 }, list);
         }
@@ -226,7 +226,7 @@ namespace Google.Protobuf.WellKnownTypes
         {
             // Just a single example... note that we can't have a null value here
             var message = new MapWellKnownTypes { Int32Field = { { 1, 2 }, { 3, null } } };
-            var fields = ((IReflectedMessage) message).Fields;
+            var fields = MapWellKnownTypes.Descriptor.FieldAccessorsByFieldNumber;
             var dictionary = (IDictionary) fields[MapWellKnownTypes.Int32FieldFieldNumber].GetValue(message);
             Assert.AreEqual(2, dictionary[1]);
             Assert.IsNull(dictionary[3]);

+ 3 - 0
csharp/src/Google.Protobuf/Collections/MapField.cs

@@ -30,6 +30,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 
+using Google.Protobuf.Reflection;
 using System;
 using System.Collections;
 using System.Collections.Generic;
@@ -559,6 +560,8 @@ namespace Google.Protobuf.Collections
                 {
                     return codec.keyCodec.CalculateSizeWithTag(Key) + codec.valueCodec.CalculateSizeWithTag(Value);
                 }
+
+                MessageDescriptor IMessage.Descriptor { get { return null; } }
             }
         }
     }

+ 2 - 1
csharp/src/Google.Protobuf/Google.Protobuf.csproj

@@ -78,7 +78,6 @@
     <Compile Include="Reflection\EnumDescriptor.cs" />
     <Compile Include="Reflection\EnumValueDescriptor.cs" />
     <Compile Include="Reflection\FieldAccessorBase.cs" />
-    <Compile Include="Reflection\FieldAccessorTable.cs" />
     <Compile Include="Reflection\FieldDescriptor.cs" />
     <Compile Include="Reflection\FieldType.cs" />
     <Compile Include="Reflection\FileDescriptor.cs" />
@@ -91,6 +90,8 @@
     <Compile Include="Reflection\OneofDescriptor.cs" />
     <Compile Include="Reflection\PackageDescriptor.cs" />
     <Compile Include="Reflection\PartialClasses.cs" />
+    <Compile Include="Reflection\ProtobufOneofAttribute.cs" />
+    <Compile Include="Reflection\ProtobufFieldAttribute.cs" />
     <Compile Include="Reflection\ReflectionUtil.cs" />
     <Compile Include="Reflection\RepeatedFieldAccessor.cs" />
     <Compile Include="Reflection\ServiceDescriptor.cs" />

+ 8 - 10
csharp/src/Google.Protobuf/IMessage.cs

@@ -39,15 +39,6 @@ namespace Google.Protobuf
     // TODO(jonskeet): Do we want a "weak" (non-generic) version of IReflectedMessage?
     // TODO(jonskeet): Split these interfaces into separate files when we're happy with them.
 
-    /// <summary>
-    /// Reflection support for accessing field values.
-    /// </summary>
-    public interface IReflectedMessage : IMessage
-    {
-        FieldAccessorTable Fields { get; }
-        // TODO(jonskeet): Descriptor? Or a single property which has "all you need for reflection"?
-    }
-
     /// <summary>
     /// Interface for a Protocol Buffers message, supporting
     /// basic operations required for serialization.
@@ -73,6 +64,13 @@ namespace Google.Protobuf
         /// <returns>The number of bytes required to write this message
         /// to a coded output stream.</returns>
         int CalculateSize();
+
+        /// <summary>
+        /// Descriptor for this message. All instances are expected to return the same descriptor,
+        /// and for generated types this will be an explicitly-implemented member, returning the
+        /// same value as the static property declared on the type.
+        /// </summary>
+        MessageDescriptor Descriptor { get; }
     }
 
     /// <summary>
@@ -81,7 +79,7 @@ namespace Google.Protobuf
     /// the implementation class.
     /// </summary>
     /// <typeparam name="T">The message type.</typeparam>
-    public interface IMessage<T> : IReflectedMessage, IEquatable<T>, IDeepCloneable<T>, IFreezable where T : IMessage<T>
+    public interface IMessage<T> : IMessage, IEquatable<T>, IDeepCloneable<T>, IFreezable where T : IMessage<T>
     {
         /// <summary>
         /// Merges the given message into this one.

+ 13 - 13
csharp/src/Google.Protobuf/JsonFormatter.cs

@@ -118,7 +118,7 @@ namespace Google.Protobuf
             this.settings = settings;
         }
 
-        public string Format(IReflectedMessage message)
+        public string Format(IMessage message)
         {
             ThrowHelper.ThrowIfNull(message, "message");
             StringBuilder builder = new StringBuilder();
@@ -129,7 +129,7 @@ namespace Google.Protobuf
             return builder.ToString();
         }
 
-        private void WriteMessage(StringBuilder builder, IReflectedMessage message)
+        private void WriteMessage(StringBuilder builder, IMessage message)
         {
             if (message == null)
             {
@@ -137,15 +137,15 @@ namespace Google.Protobuf
                 return;
             }
             builder.Append("{ ");
-            var fields = message.Fields;
+            var fields = message.Descriptor.Fields;
             bool first = true;
             // First non-oneof fields
-            foreach (var accessor in fields.Accessors)
+            foreach (var field in fields)
             {
-                var descriptor = accessor.Descriptor;
+                var accessor = field.Accessor;
                 // Oneofs are written later
                 // TODO: Change to write out fields in order, interleaving oneofs appropriately (as per binary format)
-                if (descriptor.ContainingOneof != null)
+                if (field.ContainingOneof != null)
                 {
                     continue;
                 }
@@ -156,7 +156,7 @@ namespace Google.Protobuf
                     continue;
                 }
                 // Omit awkward (single) values such as unknown enum values
-                if (!descriptor.IsRepeated && !descriptor.IsMap && !CanWriteSingleValue(accessor.Descriptor, value))
+                if (!field.IsRepeated && !field.IsMap && !CanWriteSingleValue(accessor.Descriptor, value))
                 {
                     continue;
                 }
@@ -173,15 +173,15 @@ namespace Google.Protobuf
             }
 
             // Now oneofs
-            foreach (var accessor in fields.Oneofs)
+            foreach (var oneof in message.Descriptor.Oneofs)
             {
+                var accessor = oneof.Accessor;
                 var fieldDescriptor = accessor.GetCaseFieldDescriptor(message);
                 if (fieldDescriptor == null)
                 {
                     continue;
                 }
-                var fieldAccessor = fields[fieldDescriptor.FieldNumber];
-                object value = fieldAccessor.GetValue(message);
+                object value = fieldDescriptor.Accessor.GetValue(message);
                 // Omit awkward (single) values such as unknown enum values
                 if (!fieldDescriptor.IsRepeated && !fieldDescriptor.IsMap && !CanWriteSingleValue(fieldDescriptor, value))
                 {
@@ -194,7 +194,7 @@ namespace Google.Protobuf
                 }
                 WriteString(builder, ToCamelCase(fieldDescriptor.Name));
                 builder.Append(": ");
-                WriteValue(builder, fieldAccessor, value);
+                WriteValue(builder, fieldDescriptor.Accessor, value);
                 first = false;
             }
             builder.Append(first ? "}" : " }");
@@ -385,7 +385,7 @@ namespace Google.Protobuf
                     }
                     else
                     {
-                        WriteMessage(builder, (IReflectedMessage) value);
+                        WriteMessage(builder, (IMessage) value);
                     }
                     break;
                 default:
@@ -406,7 +406,7 @@ namespace Google.Protobuf
                 WriteSingleValue(builder, descriptor.MessageType.FindFieldByNumber(1), value);
                 return;
             }
-            WriteMessage(builder, (IReflectedMessage) value);
+            WriteMessage(builder, (IMessage) value);
         }
 
         private void WriteList(StringBuilder builder, IFieldAccessor accessor, IList list)

+ 2 - 3
csharp/src/Google.Protobuf/Reflection/DescriptorUtil.cs

@@ -50,9 +50,8 @@ namespace Google.Protobuf.Reflection
         /// Converts the given array into a read-only list, applying the specified conversion to
         /// each input element.
         /// </summary>
-        internal static IList<TOutput> ConvertAndMakeReadOnly<TInput, TOutput>(IList<TInput> input,
-                                                                               IndexedConverter<TInput, TOutput>
-                                                                                   converter)
+        internal static IList<TOutput> ConvertAndMakeReadOnly<TInput, TOutput>
+            (IList<TInput> input, IndexedConverter<TInput, TOutput> converter)
         {
             TOutput[] array = new TOutput[input.Count];
             for (int i = 0; i < array.Length; i++)

+ 9 - 1
csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs

@@ -30,6 +30,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 
+using System;
 using System.Collections.Generic;
 
 namespace Google.Protobuf.Reflection
@@ -42,11 +43,13 @@ namespace Google.Protobuf.Reflection
         private readonly EnumDescriptorProto proto;
         private readonly MessageDescriptor containingType;
         private readonly IList<EnumValueDescriptor> values;
+        private readonly Type generatedType;
 
-        internal EnumDescriptor(EnumDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index)
+        internal EnumDescriptor(EnumDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index, Type generatedType)
             : base(file, file.ComputeFullName(parent, proto.Name), index)
         {
             this.proto = proto;
+            this.generatedType = generatedType;
             containingType = parent;
 
             if (proto.Value.Count == 0)
@@ -69,6 +72,11 @@ namespace Google.Protobuf.Reflection
         /// </summary>
         public override string Name { get { return proto.Name; } }
 
+        /// <summary>
+        /// The generated type for this enum, or <c>null</c> if the descriptor does not represent a generated type.
+        /// </summary>
+        public Type GeneratedType { get { return generatedType; } }
+
         /// <value>
         /// If this is a nested type, get the outer descriptor, otherwise null.
         /// </value>

+ 1 - 6
csharp/src/Google.Protobuf/Reflection/FieldAccessorBase.cs

@@ -43,13 +43,8 @@ namespace Google.Protobuf.Reflection
         private readonly Func<object, object> getValueDelegate;
         private readonly FieldDescriptor descriptor;
 
-        internal FieldAccessorBase(Type type, string propertyName, FieldDescriptor descriptor)
+        internal FieldAccessorBase(PropertyInfo property, FieldDescriptor descriptor)
         {
-            PropertyInfo property = type.GetProperty(propertyName);
-            if (property == null || !property.CanRead)
-            {
-                throw new ArgumentException("Not all required properties/methods available");
-            }
             this.descriptor = descriptor;
             getValueDelegate = ReflectionUtil.CreateFuncObjectObject(property.GetGetMethod());
         }

+ 0 - 97
csharp/src/Google.Protobuf/Reflection/FieldAccessorTable.cs

@@ -1,97 +0,0 @@
-#region Copyright notice and license
-// 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.
-#endregion
-
-using System;
-using System.Collections.ObjectModel;
-
-namespace Google.Protobuf.Reflection
-{
-    /// <summary>
-    /// Provides access to fields in generated messages via reflection.
-    /// </summary>
-    public sealed class FieldAccessorTable
-    {
-        private readonly ReadOnlyCollection<IFieldAccessor> accessors;
-        private readonly ReadOnlyCollection<OneofAccessor> oneofs;
-        private readonly MessageDescriptor descriptor;
-
-        /// <summary>
-        /// Constructs a FieldAccessorTable for a particular message class.
-        /// Only one FieldAccessorTable should be constructed per class.
-        /// </summary>
-        /// <param name="type">The CLR type for the message.</param>
-        /// <param name="descriptor">The type's descriptor</param>
-        /// <param name="propertyNames">The Pascal-case names of all the field-based properties in the message.</param>
-        public FieldAccessorTable(Type type, MessageDescriptor descriptor, string[] propertyNames, string[] oneofPropertyNames)
-        {
-            this.descriptor = descriptor;
-            var accessorsArray = new IFieldAccessor[descriptor.Fields.Count];
-            for (int i = 0; i < accessorsArray.Length; i++)
-            {
-                var field = descriptor.Fields[i];
-                var name = propertyNames[i];
-                accessorsArray[i] =
-                    field.IsMap ? new MapFieldAccessor(type, name, field)
-                    : field.IsRepeated ? new RepeatedFieldAccessor(type, name, field)
-                    : (IFieldAccessor) new SingleFieldAccessor(type, name, field);
-            }
-            accessors = new ReadOnlyCollection<IFieldAccessor>(accessorsArray);
-            var oneofsArray = new OneofAccessor[descriptor.Oneofs.Count];
-            for (int i = 0; i < oneofsArray.Length; i++)
-            {
-                var oneof = descriptor.Oneofs[i];
-                oneofsArray[i] = new OneofAccessor(type, oneofPropertyNames[i], oneof);
-            }
-            oneofs = new ReadOnlyCollection<OneofAccessor>(oneofsArray);
-        }
-
-        // TODO: Validate the name here... should possibly make this type a more "general reflection access" type,
-        // bearing in mind the oneof parts to come as well.
-        /// <summary>
-        /// Returns all of the field accessors for the message type.
-        /// </summary>
-        public ReadOnlyCollection<IFieldAccessor> Accessors { get { return accessors; } }
-
-        public ReadOnlyCollection<OneofAccessor> Oneofs { get { return oneofs; } }
-
-        // TODO: Review this, as it's easy to get confused between FieldNumber and Index.
-        // Currently only used to get an accessor related to a oneof... maybe just make that simpler?
-        public IFieldAccessor this[int fieldNumber]
-        {
-            get
-            {
-                FieldDescriptor field = descriptor.FindFieldByNumber(fieldNumber);
-                return accessors[field.Index];
-            }
-        }
-    }
-}

+ 28 - 0
csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs

@@ -31,6 +31,7 @@
 #endregion
 
 using System;
+using System.Linq;
 
 namespace Google.Protobuf.Reflection
 {
@@ -45,6 +46,7 @@ namespace Google.Protobuf.Reflection
         private readonly MessageDescriptor containingType;
         private readonly OneofDescriptor containingOneof;
         private FieldType fieldType;
+        private IFieldAccessor accessor;
 
         internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file,
                                  MessageDescriptor parent, int index)
@@ -82,6 +84,8 @@ namespace Google.Protobuf.Reflection
         public override string Name { get { return proto.Name; } }
 
         internal FieldDescriptorProto Proto { get { return proto; } }
+
+        public IFieldAccessor Accessor { get { return accessor; } }
         
         /// <summary>
         /// Maps a field type as included in the .proto file to a FieldType.
@@ -287,6 +291,30 @@ namespace Google.Protobuf.Reflection
             {
                 throw new DescriptorValidationException(this, "MessageSet format is not supported.");
             }
+
+            accessor = CreateAccessor();
+        }
+
+        private IFieldAccessor CreateAccessor()
+        {
+            // TODO: Check the performance of this with some large protos. Each message is O(N^2) in the number of fields,
+            // which isn't great...
+            if (containingType.GeneratedType == null)
+            {
+                return null;
+            }
+            var property = containingType
+                .GeneratedType
+                .GetProperties()
+                .FirstOrDefault(p => p.IsDefined(typeof(ProtobufFieldAttribute), false) &&
+                                     p.GetCustomAttributes(typeof(ProtobufFieldAttribute), false).Cast<ProtobufFieldAttribute>().Single().Number == FieldNumber);
+            if (property == null)
+            {
+                return null;
+            }
+            return IsMap ? new MapFieldAccessor(property, this)
+                : IsRepeated ? new RepeatedFieldAccessor(property, this)
+                : (IFieldAccessor) new SingleFieldAccessor(property, this);
         }
     }
 }

+ 29 - 7
csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs

@@ -62,11 +62,12 @@ namespace Google.Protobuf.Reflection
             get { return proto.Syntax == "proto3" ? ProtoSyntax.Proto3 : ProtoSyntax.Proto2; }
         }
 
-        private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies)
+        private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies, Type[] generatedTypes)
         {
             this.pool = pool;
             this.proto = proto;
             this.dependencies = new ReadOnlyCollection<FileDescriptor>((FileDescriptor[]) dependencies.Clone());
+            IEnumerator<Type> generatedTypeIterator = generatedTypes == null ? null : ((IEnumerable<Type>)generatedTypes).GetEnumerator();
 
             publicDependencies = DeterminePublicDependencies(this, proto, dependencies, allowUnknownDependencies);
 
@@ -74,15 +75,21 @@ namespace Google.Protobuf.Reflection
 
             messageTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.MessageType,
                                                                  (message, index) =>
-                                                                 new MessageDescriptor(message, this, null, index));
+                                                                 new MessageDescriptor(message, this, null, index, generatedTypeIterator));
 
             enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType,
                                                               (enumType, index) =>
-                                                              new EnumDescriptor(enumType, this, null, index));
+                                                              new EnumDescriptor(enumType, this, null, index, ReflectionUtil.GetNextType(generatedTypeIterator)));
 
             services = DescriptorUtil.ConvertAndMakeReadOnly(proto.Service,
                                                              (service, index) =>
                                                              new ServiceDescriptor(service, this, index));
+
+            // We should now have consumed all the generated types.
+            if (generatedTypeIterator != null && generatedTypeIterator.MoveNext())
+            {
+                throw new ArgumentException("More generated types left over after consuming all expected ones", "generatedTypes");
+            }
         }
 
         /// <summary>
@@ -265,7 +272,7 @@ namespace Google.Protobuf.Reflection
         /// <exception cref="DescriptorValidationException">If <paramref name="proto"/> is not
         /// a valid descriptor. This can occur for a number of reasons, such as a field
         /// having an undefined type or because two messages were defined with the same name.</exception>
-        private static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies)
+        private static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies, Type[] generatedTypes)
         {
             // Building descriptors involves two steps: translating and linking.
             // In the translation step (implemented by FileDescriptor's
@@ -282,7 +289,7 @@ namespace Google.Protobuf.Reflection
             }
 
             DescriptorPool pool = new DescriptorPool(dependencies);
-            FileDescriptor result = new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies);
+            FileDescriptor result = new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies, generatedTypes);
 
             // TODO(jonskeet): Reinstate these checks, or get rid of them entirely. They aren't in the Java code,
             // and fail for the CustomOptions test right now. (We get "descriptor.proto" vs "google/protobuf/descriptor.proto".)
@@ -319,8 +326,23 @@ namespace Google.Protobuf.Reflection
             }
         }
 
+        /// <summary>
+        /// Creates an instance for generated code.
+        /// </summary>
+        /// <remarks>
+        /// The <paramref name="generatedTypes"/> parameter should be null for descriptors which don't correspond to
+        /// generated types. Otherwise, the array should be represent all the generated types in the file: messages then
+        /// enums. Within each message, there can be nested messages and enums, which must be specified "inline" in the array:
+        /// containing message, nested messages, nested enums - and of course each nested message may contain *more* nested messages,
+        /// etc. All messages within the descriptor should be represented, even if they do not have a generated type - any
+        /// type without a corresponding generated type (such as map entries) should respond to a null element.
+        /// For example, a file with a messages OuterMessage and InnerMessage, and enums OuterEnum and InnerEnum (where
+        /// InnerMessage and InnerEnum are nested within InnerMessage) would result in an array of
+        /// OuterMessage, InnerMessage, InnerEnum, OuterEnum.
+        /// </remarks>
         public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData,
-                                                                    FileDescriptor[] dependencies)
+                                                                    FileDescriptor[] dependencies,
+                                                                    Type[] generatedTypes)
         {
             FileDescriptorProto proto;
             try
@@ -336,7 +358,7 @@ namespace Google.Protobuf.Reflection
             {
                 // When building descriptors for generated code, we allow unknown
                 // dependencies by default.
-                return BuildFrom(proto, dependencies, true);
+                return BuildFrom(proto, dependencies, true, generatedTypes);
             }
             catch (DescriptorValidationException e)
             {

+ 2 - 1
csharp/src/Google.Protobuf/Reflection/MapFieldAccessor.cs

@@ -32,6 +32,7 @@
 
 using System;
 using System.Collections;
+using System.Reflection;
 
 namespace Google.Protobuf.Reflection
 {
@@ -40,7 +41,7 @@ namespace Google.Protobuf.Reflection
     /// </summary>
     internal sealed class MapFieldAccessor : FieldAccessorBase
     {
-        internal MapFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor)
+        internal MapFieldAccessor(PropertyInfo property, FieldDescriptor descriptor) : base(property, descriptor)
         {
         }
 

+ 31 - 3
csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs

@@ -30,8 +30,10 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 
+using Google.Protobuf.Collections;
 using System;
 using System.Collections.Generic;
+using System.Linq;
 
 namespace Google.Protobuf.Reflection
 {
@@ -60,11 +62,15 @@ namespace Google.Protobuf.Reflection
         private readonly IList<EnumDescriptor> enumTypes;
         private readonly IList<FieldDescriptor> fields;
         private readonly IList<OneofDescriptor> oneofs;
+        // CLR representation of the type described by this descriptor, if any.
+        private readonly Type generatedType;
+        private IDictionary<int, IFieldAccessor> fieldAccessorsByFieldNumber;
         
-        internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex)
+        internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex, IEnumerator<Type> generatedTypeIterator)
             : base(file, file.ComputeFullName(parent, proto.Name), typeIndex)
         {
             this.proto = proto;
+            generatedType = ReflectionUtil.GetNextType(generatedTypeIterator);
             containingType = parent;
 
             oneofs = DescriptorUtil.ConvertAndMakeReadOnly(proto.OneofDecl,
@@ -73,11 +79,11 @@ namespace Google.Protobuf.Reflection
 
             nestedTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.NestedType,
                                                                 (type, index) =>
-                                                                new MessageDescriptor(type, file, this, index));
+                                                                new MessageDescriptor(type, file, this, index, generatedTypeIterator));
 
             enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType,
                                                               (type, index) =>
-                                                              new EnumDescriptor(type, file, this, index));
+                                                              new EnumDescriptor(type, file, this, index, ReflectionUtil.GetNextType(generatedTypeIterator)));
 
             // TODO(jonskeet): Sort fields first?
             fields = DescriptorUtil.ConvertAndMakeReadOnly(proto.Field,
@@ -85,6 +91,14 @@ namespace Google.Protobuf.Reflection
                                                            new FieldDescriptor(field, file, this, index));
             file.DescriptorPool.AddSymbol(this);
         }
+                
+        /// <summary>
+        /// Returns the total number of nested types and enums, recursively.
+        /// </summary>
+        private int CountTotalGeneratedTypes()
+        {
+            return nestedTypes.Sum(nested => nested.CountTotalGeneratedTypes()) + enumTypes.Count;
+        }
 
         /// <summary>
         /// The brief name of the descriptor's target.
@@ -93,6 +107,11 @@ namespace Google.Protobuf.Reflection
 
         internal DescriptorProto Proto { get { return proto; } }
 
+        /// <summary>
+        /// The generated type for this message, or <c>null</c> if the descriptor does not represent a generated type.
+        /// </summary>
+        public Type GeneratedType { get { return generatedType; } }
+
         /// <summary>
         /// Returns whether this message is one of the "well known types" which may have runtime/protoc support.
         /// </summary>
@@ -141,6 +160,13 @@ namespace Google.Protobuf.Reflection
             get { return oneofs; }
         }
 
+        /// <summary>
+        /// Returns a map from field number to accessor.
+        /// TODO: Revisit this. It's mostly in place to make the transition from FieldAccessorTable
+        /// to descriptor-based reflection simple in terms of tests. Work out what we really want.
+        /// </summary>
+        public IDictionary<int, IFieldAccessor> FieldAccessorsByFieldNumber { get { return fieldAccessorsByFieldNumber; } }
+
         /// <summary>
         /// Finds a field by field name.
         /// </summary>
@@ -192,6 +218,8 @@ namespace Google.Protobuf.Reflection
             {
                 oneof.CrossLink();
             }
+
+            fieldAccessorsByFieldNumber = new ReadOnlyDictionary<int, IFieldAccessor>(fields.ToDictionary(field => field.FieldNumber, field => field.Accessor));
         }        
     }
 }

+ 4 - 6
csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs

@@ -44,18 +44,16 @@ namespace Google.Protobuf.Reflection
         private readonly Action<object> clearDelegate;
         private OneofDescriptor descriptor;
 
-        internal OneofAccessor(Type type, string propertyName, OneofDescriptor descriptor) 
+        internal OneofAccessor(PropertyInfo caseProperty, MethodInfo clearMethod, OneofDescriptor descriptor) 
         {
-            PropertyInfo property = type.GetProperty(propertyName + "Case");
-            if (property == null || !property.CanRead)
+            if (!caseProperty.CanRead)
             {
-                throw new ArgumentException("Not all required properties/methods available");
+                throw new ArgumentException("Cannot read from property");
             }
             this.descriptor = descriptor;
-            caseDelegate = ReflectionUtil.CreateFuncObjectT<int>(property.GetGetMethod());
+            caseDelegate = ReflectionUtil.CreateFuncObjectT<int>(caseProperty.GetGetMethod());
 
             this.descriptor = descriptor;
-            MethodInfo clearMethod = type.GetMethod("Clear" + propertyName);
             clearDelegate = ReflectionUtil.CreateActionObject(clearMethod);
         }
 

+ 34 - 0
csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs

@@ -32,6 +32,7 @@
 
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Linq;
 
 namespace Google.Protobuf.Reflection
 {
@@ -40,6 +41,7 @@ namespace Google.Protobuf.Reflection
         private readonly OneofDescriptorProto proto;
         private MessageDescriptor containingType;
         private IList<FieldDescriptor> fields;
+        private OneofAccessor accessor;
 
         internal OneofDescriptor(OneofDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index)
             : base(file, file.ComputeFullName(parent, proto.Name), index)
@@ -62,6 +64,8 @@ namespace Google.Protobuf.Reflection
 
         public IList<FieldDescriptor> Fields { get { return fields; } }
 
+        public OneofAccessor Accessor { get { return accessor; } }
+
         internal void CrossLink()
         {
             List<FieldDescriptor> fieldCollection = new List<FieldDescriptor>();
@@ -73,6 +77,36 @@ namespace Google.Protobuf.Reflection
                 }
             }
             fields = new ReadOnlyCollection<FieldDescriptor>(fieldCollection);
+            accessor = CreateAccessor();
+        }
+
+        private OneofAccessor CreateAccessor()
+        {
+            if (containingType.GeneratedType == null)
+            {
+                return null;
+            }
+            var caseProperty = containingType
+                .GeneratedType
+                .GetProperties()
+                .FirstOrDefault(p => p.IsDefined(typeof(ProtobufOneofAttribute), false) &&
+                                     p.GetCustomAttributes(typeof(ProtobufOneofAttribute), false).Cast<ProtobufOneofAttribute>().Single().Name == Name);
+            if (caseProperty == null)
+            {
+                return null;
+            }
+
+            var clearMethod = containingType
+                 .GeneratedType
+                 .GetMethods()
+                 .FirstOrDefault(p => p.IsDefined(typeof(ProtobufOneofAttribute), false) &&
+                                      p.GetCustomAttributes(typeof(ProtobufOneofAttribute), false).Cast<ProtobufOneofAttribute>().Single().Name == Name);
+            if (clearMethod == null)
+            {
+                return null;
+            }
+
+            return new OneofAccessor(caseProperty, clearMethod, this);
         }
     }
 }

+ 58 - 0
csharp/src/Google.Protobuf/Reflection/ProtobufFieldAttribute.cs

@@ -0,0 +1,58 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 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.
+#endregion
+using System;
+
+namespace Google.Protobuf.Reflection
+{
+    /// <summary>
+    /// Attribute applied to a generated property corresponding to a field in a .proto file.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
+    public sealed class ProtobufFieldAttribute : Attribute
+    {
+        /// <summary>
+        /// The field number in the original .proto file.
+        /// </summary>
+        public int Number { get; set; }
+        
+        /// <summary>
+        /// The field name in the original .proto file.
+        /// </summary>
+        public string Name { get; set; }
+
+        public ProtobufFieldAttribute(int number, string name)
+        {
+            this.Number = number;
+            this.Name = name;
+        }
+    }
+}

+ 52 - 0
csharp/src/Google.Protobuf/Reflection/ProtobufOneofAttribute.cs

@@ -0,0 +1,52 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 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.
+#endregion
+using System;
+
+namespace Google.Protobuf.Reflection
+{
+    /// <summary>
+    /// Attribute applied to the "case" property or "clear" method corresponding to a oneof in a .proto file.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = false)]
+    public sealed class ProtobufOneofAttribute : Attribute
+    {
+        /// <summary>
+        /// The oneof name in the original .proto file.
+        /// </summary>
+        public string Name { get; set; }
+
+        public ProtobufOneofAttribute(string name)
+        {
+            this.Name = name;
+        }
+    }
+}

+ 20 - 0
csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs

@@ -31,6 +31,7 @@
 #endregion
 
 using System;
+using System.Collections.Generic;
 using System.Linq.Expressions;
 using System.Reflection;
 
@@ -102,5 +103,24 @@ namespace Google.Protobuf.Reflection
             Expression call = Expression.Call(castTarget, method);
             return Expression.Lambda<Action<object>>(call, targetParameter).Compile();
         }
+
+        /// <summary>
+        /// Returns the next type from an iterator of types, unless the iterator is a null reference,
+        /// in which case null is returned.
+        /// </summary>
+        internal static Type GetNextType(IEnumerator<Type> generatedTypeIterator)
+        {
+            if (generatedTypeIterator == null)
+            {
+                return null;
+            }
+            if (!generatedTypeIterator.MoveNext())
+            {
+                // This parameter name corresponds to any public method supplying the generated types to start with.
+                throw new ArgumentException("More generated types left over after consuming all expected ones", "generatedTypes");
+            }
+            return generatedTypeIterator.Current;
+        }
+
     }
 }

+ 2 - 1
csharp/src/Google.Protobuf/Reflection/RepeatedFieldAccessor.cs

@@ -32,6 +32,7 @@
 
 using System;
 using System.Collections;
+using System.Reflection;
 
 namespace Google.Protobuf.Reflection
 {
@@ -40,7 +41,7 @@ namespace Google.Protobuf.Reflection
     /// </summary>
     internal sealed class RepeatedFieldAccessor : FieldAccessorBase
     {
-        internal RepeatedFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor)
+        internal RepeatedFieldAccessor(PropertyInfo property, FieldDescriptor descriptor) : base(property, descriptor)
         {
         }
 

+ 1 - 4
csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs

@@ -48,11 +48,8 @@ namespace Google.Protobuf.Reflection
         private readonly Action<object, object> setValueDelegate;
         private readonly Action<object> clearDelegate;
 
-        internal SingleFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor)
+        internal SingleFieldAccessor(PropertyInfo property, FieldDescriptor descriptor) : base(property, descriptor)
         {
-            PropertyInfo property = type.GetProperty(propertyName);
-            // We know there *is* such a property, or the base class constructor would have thrown. We should be able to write
-            // to it though.
             if (!property.CanWrite)
             {
                 throw new ArgumentException("Not all required properties/methods available");

+ 1 - 1
src/google/protobuf/compiler/csharp/csharp_field_base.cc

@@ -74,6 +74,7 @@ void FieldGeneratorBase::SetCommonFieldVariables(
 
   (*variables)["property_name"] = property_name();
   (*variables)["type_name"] = type_name();
+  (*variables)["original_name"] = descriptor_->name();
   (*variables)["name"] = name();
   (*variables)["descriptor_name"] = descriptor_->name();
   (*variables)["default_value"] = default_value();
@@ -85,7 +86,6 @@ void FieldGeneratorBase::SetCommonFieldVariables(
   }
   (*variables)["capitalized_type_name"] = capitalized_type_name();
   (*variables)["number"] = number();
-  (*variables)["field_ordinal"] = field_ordinal();
   (*variables)["has_property_check"] =
     (*variables)["property_name"] + " != " + (*variables)["default_value"];
   (*variables)["other_has_property_check"] = "other." +

+ 2 - 4
src/google/protobuf/compiler/csharp/csharp_helpers.h

@@ -77,6 +77,8 @@ std::string GetFullUmbrellaClassName(const FileDescriptor* descriptor);
 
 std::string GetQualifiedUmbrellaClassName(const FileDescriptor* descriptor);
 
+std::string GetClassName(const Descriptor* descriptor);
+
 std::string GetClassName(const EnumDescriptor* descriptor);
 
 std::string GetFieldName(const FieldDescriptor* descriptor);
@@ -119,10 +121,6 @@ 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";

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_map_field.cc

@@ -79,6 +79,7 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
+    "[pbr::ProtobufField($number$, \"$original_name$\")]\n"
     "$access_level$ pbc::MapField<$key_type_name$, $value_type_name$> $property_name$ {\n"
     "  get { return $name$_; }\n"
     "}\n");

+ 5 - 79
src/google/protobuf/compiler/csharp/csharp_message.cc

@@ -96,88 +96,11 @@ const std::vector<const FieldDescriptor*>& MessageGenerator::fields_by_number()
   return fields_by_number_;
 }
 
-/// Get an identifier that uniquely identifies this type within the file.
-/// This is used to declare static variables related to this type at the
-/// outermost file scope.
-std::string GetUniqueFileScopeIdentifier(const Descriptor* descriptor) {
-  std::string result = descriptor->full_name();
-  std::replace(result.begin(), result.end(), '.', '_');
-  return "static_" + result;
-}
-
-void MessageGenerator::GenerateStaticVariables(io::Printer* printer) {
-  // Because descriptor.proto (Google.Protobuf.DescriptorProtos) is
-  // used in the construction of descriptors, we have a tricky bootstrapping
-  // problem.  To help control static initialization order, we make sure all
-  // descriptors and other static data that depends on them are members of
-  // the proto-descriptor class.  This way, they will be initialized in
-  // a deterministic order.
-
-  std::string identifier = GetUniqueFileScopeIdentifier(descriptor_);
-
-  // The descriptor for this type.
-  printer->Print(
-      "internal static pbr::FieldAccessorTable internal__$identifier$__FieldAccessorTable;\n",
-      "identifier", GetUniqueFileScopeIdentifier(descriptor_),
-      "full_class_name", full_class_name());
-
-  for (int i = 0; i < descriptor_->nested_type_count(); i++) {
-    // Don't generate accessor table fields for maps...
-    if (!IsMapEntryMessage(descriptor_->nested_type(i))) {
-      MessageGenerator messageGenerator(descriptor_->nested_type(i));
-      messageGenerator.GenerateStaticVariables(printer);
-    }
-  }
-}
-
-void MessageGenerator::GenerateStaticVariableInitializers(io::Printer* printer) {
-  map<string, string> vars;
-  vars["identifier"] = GetUniqueFileScopeIdentifier(descriptor_);
-  vars["full_class_name"] = full_class_name();
-
-  // Work out how to get to the message descriptor (which may be multiply nested) from the file
-  // descriptor.
-  string descriptor_chain;
-  const Descriptor* current_descriptor = descriptor_;
-  while (current_descriptor->containing_type()) {
-    descriptor_chain = ".NestedTypes[" + SimpleItoa(current_descriptor->index()) + "]" + descriptor_chain;
-    current_descriptor = current_descriptor->containing_type();
-  }
-  descriptor_chain = "descriptor.MessageTypes[" + SimpleItoa(current_descriptor->index()) + "]" + descriptor_chain;
-  vars["descriptor_chain"] = descriptor_chain;
-
-  printer->Print(
-    vars,
-    "internal__$identifier$__FieldAccessorTable = \n"
-    "    new pbr::FieldAccessorTable(typeof($full_class_name$), $descriptor_chain$,\n");
-  printer->Print("        new string[] { ");
-  for (int i = 0; i < descriptor_->field_count(); i++) {
-    printer->Print("\"$property_name$\", ",
-                   "property_name", GetPropertyName(descriptor_->field(i)));
-  }
-  printer->Print("}, new string[] { ");
-  for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
-    printer->Print("\"$oneof_name$\", ",
-                   "oneof_name",
-                   UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true));
-  }
-  printer->Print("});\n");
-
-  // Generate static member initializers for all non-map-entry nested types.
-  for (int i = 0; i < descriptor_->nested_type_count(); i++) {
-    if (!IsMapEntryMessage(descriptor_->nested_type(i))) {
-      MessageGenerator messageGenerator(descriptor_->nested_type(i));
-      messageGenerator.GenerateStaticVariableInitializers(printer);
-    }
-  }
-}
-
 void MessageGenerator::Generate(io::Printer* printer) {
   map<string, string> vars;
   vars["class_name"] = class_name();
   vars["access_level"] = class_access_level();
   vars["umbrella_class_name"] = GetFullUmbrellaClassName(descriptor_->file());
-  vars["identifier"] = GetUniqueFileScopeIdentifier(descriptor_);
 
   printer->Print(
     "[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n");
@@ -221,8 +144,8 @@ void MessageGenerator::Generate(io::Printer* printer) {
     "  get { return $descriptor_accessor$; }\n"
     "}\n"
     "\n"
-    "pbr::FieldAccessorTable pb::IReflectedMessage.Fields {\n"
-    "  get { return $umbrella_class_name$.internal__$identifier$__FieldAccessorTable; }\n"
+    "pbr::MessageDescriptor pb::IMessage.Descriptor {\n"
+    "  get { return Descriptor; }\n"
     "}\n"
     "\n"
     "private bool _frozen = false;\n"
@@ -258,6 +181,7 @@ void MessageGenerator::Generate(io::Printer* printer) {
   for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
     vars["name"] = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false);
     vars["property_name"] = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true);
+    vars["original_name"] = descriptor_->oneof_decl(i)->name();
     printer->Print(
       vars,
       "private object $name$_;\n"
@@ -275,9 +199,11 @@ void MessageGenerator::Generate(io::Printer* printer) {
     printer->Print(
       vars,
       "private $property_name$OneofCase $name$Case_ = $property_name$OneofCase.None;\n"
+      "[pbr::ProtobufOneof(\"$original_name$\")]\n"
       "public $property_name$OneofCase $property_name$Case {\n"
       "  get { return $name$Case_; }\n"
       "}\n\n"
+      "[pbr::ProtobufOneof(\"$original_name$\")]\n"
       "public void Clear$property_name$() {\n"
       "  pb::Freezable.CheckMutable(this);\n"
       "  $name$Case_ = $property_name$OneofCase.None;\n"

+ 0 - 2
src/google/protobuf/compiler/csharp/csharp_message.h

@@ -53,8 +53,6 @@ class MessageGenerator : public SourceGeneratorBase {
   void GenerateCloningCode(io::Printer* printer);
   void GenerateFreezingCode(io::Printer* printer);
   void GenerateFrameworkMethods(io::Printer* printer);
-  void GenerateStaticVariables(io::Printer* printer);
-  void GenerateStaticVariableInitializers(io::Printer* printer);
   void Generate(io::Printer* printer);
 
  private:

+ 2 - 0
src/google/protobuf/compiler/csharp/csharp_message_field.cc

@@ -64,6 +64,7 @@ void MessageFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
+    "[pbr::ProtobufField($number$, \"$original_name$\")]\n"
     "$access_level$ $type_name$ $property_name$ {\n"
     "  get { return $name$_; }\n"
     "  set {\n"
@@ -158,6 +159,7 @@ void MessageOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
+    "[pbr::ProtobufField($number$, \"$original_name$\")]\n"
     "$access_level$ $type_name$ $property_name$ {\n"
     "  get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : null; }\n"
     "  set {\n"

+ 2 - 0
src/google/protobuf/compiler/csharp/csharp_primitive_field.cc

@@ -71,6 +71,7 @@ void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
+    "[pbr::ProtobufField($number$, \"$original_name$\")]\n"
     "$access_level$ $type_name$ $property_name$ {\n"
     "  get { return $name$_; }\n"
     "  set {\n"
@@ -174,6 +175,7 @@ void PrimitiveOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
+    "[pbr::ProtobufField($number$, \"$original_name$\")]\n"
     "$access_level$ $type_name$ $property_name$ {\n"
     "  get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : $default_value$; }\n"
     "  set {\n"

+ 2 - 1
src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc

@@ -59,12 +59,13 @@ void RepeatedEnumFieldGenerator::GenerateMembers(io::Printer* printer) {
   printer->Print(
     variables_,
     "private static readonly pb::FieldCodec<$type_name$> _repeated_$name$_codec\n"
-    "    = pb::FieldCodec.ForEnum($tag$, x => (int) x, x => ($type_name$) x);");
+    "    = pb::FieldCodec.ForEnum($tag$, x => (int) x, x => ($type_name$) x);\n");
   printer->Print(variables_,
     "private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n");
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
+    "[pbr::ProtobufField($number$, \"$original_name$\")]\n"
     "$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
     "  get { return $name$_; }\n"
     "}\n");

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc

@@ -78,6 +78,7 @@ void RepeatedMessageFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
+    "[pbr::ProtobufField($number$, \"$original_name$\")]\n"
     "$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
     "  get { return $name$_; }\n"
     "}\n");

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc

@@ -65,6 +65,7 @@ void RepeatedPrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
+    "[pbr::ProtobufField($number$, \"$original_name$\")]\n"
     "$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
     "  get { return $name$_; }\n"
     "}\n");

+ 25 - 12
src/google/protobuf/compiler/csharp/csharp_umbrella_class.cc

@@ -63,12 +63,6 @@ UmbrellaClassGenerator::~UmbrellaClassGenerator() {
 void UmbrellaClassGenerator::Generate(io::Printer* printer) {
   WriteIntroduction(printer);
 
-  printer->Print("#region Static variables\n");
-  for (int i = 0; i < file_->message_type_count(); i++) {
-    MessageGenerator messageGenerator(file_->message_type(i));
-    messageGenerator.GenerateStaticVariables(printer);
-  }
-  printer->Print("#endregion\n");
   WriteDescriptor(printer);
   // Close the class declaration.
   printer->Outdent();
@@ -183,24 +177,43 @@ void UmbrellaClassGenerator::WriteDescriptor(io::Printer* printer) {
   // Invoke InternalBuildGeneratedFileFrom() to build the file.
   printer->Print(
       "descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,\n");
-  printer->Print("    new pbr::FileDescriptor[] {\n");
+  printer->Print("    new pbr::FileDescriptor[] { ");
   for (int i = 0; i < file_->dependency_count(); i++) {
     printer->Print(
-      "    $full_umbrella_class_name$.Descriptor, \n",
+      "$full_umbrella_class_name$.Descriptor, ",
       "full_umbrella_class_name",
       GetFullUmbrellaClassName(file_->dependency(i)));
   }
-  printer->Print("    });\n");
-  // Then invoke any other static variable initializers, e.g. field accessors.
+  // Specify all the generated types (messages and enums), recursively, as an array. 
+  printer->Print("},\n"
+    "    new global::System.Type[] { ");
   for (int i = 0; i < file_->message_type_count(); i++) {
-      MessageGenerator messageGenerator(file_->message_type(i));
-      messageGenerator.GenerateStaticVariableInitializers(printer);
+    WriteTypeLiterals(file_->message_type(i), printer);
   }
+  for (int i = 0; i < file_->enum_type_count(); i++) {
+    printer->Print("typeof($type_name$), ", "type_name", GetClassName(file_->enum_type(i)));
+  }
+  printer->Print("});\n");
+
   printer->Outdent();
   printer->Print("}\n");
   printer->Print("#endregion\n\n");
 }
 
+void UmbrellaClassGenerator::WriteTypeLiterals(const Descriptor* descriptor, io::Printer* printer) {
+    if (IsMapEntryMessage(descriptor)) {
+        printer->Print("null, ");
+        return;
+    }
+    printer->Print("typeof($type_name$), ", "type_name", GetClassName(descriptor));
+    for (int i = 0; i < descriptor->nested_type_count(); i++) {
+        WriteTypeLiterals(descriptor->nested_type(i), printer);
+    }
+    for (int i = 0; i < descriptor->enum_type_count(); i++) {
+        printer->Print("typeof($type_name$), ", "type_name", GetClassName(descriptor->enum_type(i)));
+    }
+}
+
 }  // namespace csharp
 }  // namespace compiler
 }  // namespace protobuf

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_umbrella_class.h

@@ -57,6 +57,7 @@ class UmbrellaClassGenerator : public SourceGeneratorBase {
 
   void WriteIntroduction(io::Printer* printer);
   void WriteDescriptor(io::Printer* printer);
+  void WriteTypeLiterals(const Descriptor* descriptor, io::Printer* printer);
 
   GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UmbrellaClassGenerator);
 };

+ 2 - 0
src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc

@@ -73,6 +73,7 @@ void WrapperFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
+    "[pbr::ProtobufField($number$, \"$original_name$\")]\n"
     "$access_level$ $type_name$ $property_name$ {\n"
     "  get { return $name$_; }\n"
     "  set {\n"
@@ -169,6 +170,7 @@ void WrapperOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
+    "[pbr::ProtobufField($number$, \"$original_name$\")]\n"
     "$access_level$ $type_name$ $property_name$ {\n"
     "  get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : ($type_name$) null; }\n"
     "  set {\n"