瀏覽代碼

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 年之前
父節點
當前提交
53c399a1d6
共有 35 個文件被更改,包括 372 次插入278 次删除
  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"