Merge pull request #566 from jskeet/csharp-reflection

Improve C# reflection support
This commit is contained in:
Jon Skeet 2015-07-10 09:08:44 +01:00
commit 94878b3080
52 changed files with 2703 additions and 1964 deletions

View File

@ -13,12 +13,9 @@ namespace Google.Protobuf.Examples.AddressBook {
public static partial class Addressbook {
#region Static variables
internal static pbd::MessageDescriptor internal__static_tutorial_Person__Descriptor;
internal static pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.Examples.AddressBook.Person> internal__static_tutorial_Person__FieldAccessorTable;
internal static pbd::MessageDescriptor internal__static_tutorial_Person_PhoneNumber__Descriptor;
internal static pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber> internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable;
internal static pbd::MessageDescriptor internal__static_tutorial_AddressBook__Descriptor;
internal static pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.Examples.AddressBook.AddressBook> internal__static_tutorial_AddressBook__FieldAccessorTable;
internal static pb::FieldAccess.FieldAccessorTable internal__static_tutorial_Person__FieldAccessorTable;
internal static pb::FieldAccess.FieldAccessorTable internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable;
internal static pb::FieldAccess.FieldAccessorTable internal__static_tutorial_AddressBook__FieldAccessorTable;
#endregion
#region Descriptor
public static pbd::FileDescriptor Descriptor {
@ -29,33 +26,27 @@ namespace Google.Protobuf.Examples.AddressBook {
static Addressbook() {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"ChFhZGRyZXNzYm9vay5wcm90bxIIdHV0b3JpYWwi2gEKBlBlcnNvbhIMCgRu",
"YW1lGAEgAigJEgoKAmlkGAIgAigFEg0KBWVtYWlsGAMgASgJEisKBXBob25l",
"GAQgAygLMhwudHV0b3JpYWwuUGVyc29uLlBob25lTnVtYmVyGk0KC1Bob25l",
"TnVtYmVyEg4KBm51bWJlchgBIAIoCRIuCgR0eXBlGAIgASgOMhoudHV0b3Jp",
"YWwuUGVyc29uLlBob25lVHlwZToESE9NRSIrCglQaG9uZVR5cGUSCgoGTU9C",
"SUxFEAASCAoESE9NRRABEggKBFdPUksQAiIvCgtBZGRyZXNzQm9vaxIgCgZw",
"ZXJzb24YASADKAsyEC50dXRvcmlhbC5QZXJzb25CUAoUY29tLmV4YW1wbGUu",
"dHV0b3JpYWxCEUFkZHJlc3NCb29rUHJvdG9zqgIkR29vZ2xlLlByb3RvYnVm",
"LkV4YW1wbGVzLkFkZHJlc3NCb29r"));
pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) {
descriptor = root;
internal__static_tutorial_Person__Descriptor = Descriptor.MessageTypes[0];
internal__static_tutorial_Person__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.Examples.AddressBook.Person>(internal__static_tutorial_Person__Descriptor,
new string[] { "Name", "Id", "Email", "Phone", });
internal__static_tutorial_Person_PhoneNumber__Descriptor = internal__static_tutorial_Person__Descriptor.NestedTypes[0];
internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber>(internal__static_tutorial_Person_PhoneNumber__Descriptor,
new string[] { "Number", "Type", });
internal__static_tutorial_AddressBook__Descriptor = Descriptor.MessageTypes[1];
internal__static_tutorial_AddressBook__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.Examples.AddressBook.AddressBook>(internal__static_tutorial_AddressBook__Descriptor,
new string[] { "Person", });
};
pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
"ChFhZGRyZXNzYm9vay5wcm90bxIIdHV0b3JpYWwi1AEKBlBlcnNvbhIMCgRu",
"YW1lGAEgASgJEgoKAmlkGAIgASgFEg0KBWVtYWlsGAMgASgJEisKBXBob25l",
"GAQgAygLMhwudHV0b3JpYWwuUGVyc29uLlBob25lTnVtYmVyGkcKC1Bob25l",
"TnVtYmVyEg4KBm51bWJlchgBIAEoCRIoCgR0eXBlGAIgASgOMhoudHV0b3Jp",
"YWwuUGVyc29uLlBob25lVHlwZSIrCglQaG9uZVR5cGUSCgoGTU9CSUxFEAAS",
"CAoESE9NRRABEggKBFdPUksQAiIvCgtBZGRyZXNzQm9vaxIgCgZwZXJzb24Y",
"ASADKAsyEC50dXRvcmlhbC5QZXJzb25CUAoUY29tLmV4YW1wbGUudHV0b3Jp",
"YWxCEUFkZHJlc3NCb29rUHJvdG9zqgIkR29vZ2xlLlByb3RvYnVmLkV4YW1w",
"bGVzLkFkZHJlc3NCb29rYgZwcm90bzM="));
descriptor = pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
new pbd::FileDescriptor[] {
}, assigner);
});
internal__static_tutorial_Person__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.Examples.AddressBook.Person), descriptor.MessageTypes[0],
new string[] { "Name", "Id", "Email", "Phone", });
internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber), descriptor.MessageTypes[0].NestedTypes[0],
new string[] { "Number", "Type", });
internal__static_tutorial_AddressBook__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.Examples.AddressBook.AddressBook), descriptor.MessageTypes[1],
new string[] { "Person", });
}
#endregion
@ -69,19 +60,23 @@ namespace Google.Protobuf.Examples.AddressBook {
private static readonly string[] _fieldNames = new string[] { "email", "id", "name", "phone" };
private static readonly uint[] _fieldTags = new uint[] { 26, 16, 10, 34 };
public static pbd::MessageDescriptor Descriptor {
get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.internal__static_tutorial_Person__Descriptor; }
get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.Descriptor.MessageTypes[0]; }
}
public pb::FieldAccess.FieldAccessorTable<Person> Fields {
public pb::FieldAccess.FieldAccessorTable Fields {
get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.internal__static_tutorial_Person__FieldAccessorTable; }
}
private bool _frozen = false;
public bool IsFrozen { get { return _frozen; } }
public Person() { }
public Person() {
OnConstruction();
}
public Person(Person other) {
partial void OnConstruction();
public Person(Person other) : this() {
name_ = other.name_;
id_ = other.id_;
email_ = other.email_;
@ -260,19 +255,23 @@ namespace Google.Protobuf.Examples.AddressBook {
private static readonly string[] _fieldNames = new string[] { "number", "type" };
private static readonly uint[] _fieldTags = new uint[] { 10, 16 };
public static pbd::MessageDescriptor Descriptor {
get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.internal__static_tutorial_Person_PhoneNumber__Descriptor; }
get { return global::Google.Protobuf.Examples.AddressBook.Person.Descriptor.NestedTypes[0]; }
}
public pb::FieldAccess.FieldAccessorTable<PhoneNumber> Fields {
public pb::FieldAccess.FieldAccessorTable Fields {
get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable; }
}
private bool _frozen = false;
public bool IsFrozen { get { return _frozen; } }
public PhoneNumber() { }
public PhoneNumber() {
OnConstruction();
}
public PhoneNumber(PhoneNumber other) {
partial void OnConstruction();
public PhoneNumber(PhoneNumber other) : this() {
number_ = other.number_;
type_ = other.type_;
}
@ -299,7 +298,7 @@ namespace Google.Protobuf.Examples.AddressBook {
}
public const int TypeFieldNumber = 2;
private global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType type_ = global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.HOME;
private global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType type_ = global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.MOBILE;
public global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType Type {
get { return type_; }
set {
@ -327,7 +326,7 @@ namespace Google.Protobuf.Examples.AddressBook {
public override int GetHashCode() {
int hash = 1;
if (Number.Length != 0) hash ^= Number.GetHashCode();
if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.HOME) hash ^= Type.GetHashCode();
if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.MOBILE) hash ^= Type.GetHashCode();
return hash;
}
@ -336,7 +335,7 @@ namespace Google.Protobuf.Examples.AddressBook {
output.WriteRawTag(10);
output.WriteString(Number);
}
if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.HOME) {
if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.MOBILE) {
output.WriteRawTag(16);
output.WriteEnum((int) Type);
}
@ -347,7 +346,7 @@ namespace Google.Protobuf.Examples.AddressBook {
if (Number.Length != 0) {
size += 1 + pb::CodedOutputStream.ComputeStringSize(Number);
}
if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.HOME) {
if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.MOBILE) {
size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Type);
}
return size;
@ -360,7 +359,7 @@ namespace Google.Protobuf.Examples.AddressBook {
if (other.Number.Length != 0) {
Number = other.Number;
}
if (other.Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.HOME) {
if (other.Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.MOBILE) {
Type = other.Type;
}
}
@ -403,19 +402,23 @@ namespace Google.Protobuf.Examples.AddressBook {
private static readonly string[] _fieldNames = new string[] { "person" };
private static readonly uint[] _fieldTags = new uint[] { 10 };
public static pbd::MessageDescriptor Descriptor {
get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.internal__static_tutorial_AddressBook__Descriptor; }
get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.Descriptor.MessageTypes[1]; }
}
public pb::FieldAccess.FieldAccessorTable<AddressBook> Fields {
public pb::FieldAccess.FieldAccessorTable Fields {
get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.internal__static_tutorial_AddressBook__FieldAccessorTable; }
}
private bool _frozen = false;
public bool IsFrozen { get { return _frozen; } }
public AddressBook() { }
public AddressBook() {
OnConstruction();
}
public AddressBook(AddressBook other) {
partial void OnConstruction();
public AddressBook(AddressBook other) : this() {
person_ = other.person_.Clone();
}

View File

@ -34,6 +34,8 @@ using System;
using System.Collections.Generic;
using Google.Protobuf.TestProtos;
using NUnit.Framework;
using System.Collections;
using System.Linq;
namespace Google.Protobuf.Collections
{
@ -53,6 +55,18 @@ namespace Google.Protobuf.Collections
Assert.IsTrue(message.IsFrozen);
}
[Test]
public void Freeze_Idempotent()
{
var message = new ForeignMessage { C = 20 };
var map = new MapField<string, ForeignMessage> { { "x", message } };
Assert.IsFalse(map.IsFrozen);
map.Freeze();
Assert.IsTrue(message.IsFrozen);
map.Freeze();
Assert.IsTrue(message.IsFrozen);
}
[Test]
public void Freeze_PreventsMutation()
{
@ -187,6 +201,15 @@ namespace Google.Protobuf.Collections
EqualityTester.AssertInequality(map1, map2);
}
[Test]
public void Equality_Simple()
{
var map = new MapField<string, string>();
EqualityTester.AssertEquality(map, map);
EqualityTester.AssertInequality(map, null);
Assert.IsFalse(map.Equals(new object()));
}
[Test]
public void EqualityIsValueSensitive()
{
@ -287,7 +310,8 @@ namespace Google.Protobuf.Collections
Assert.IsFalse(map.Remove("missing"));
Assert.AreEqual(1, map.Count);
Assert.IsTrue(map.Remove("foo"));
Assert.AreEqual(0, map.Count);
Assert.AreEqual(0, map.Count);
Assert.Throws<ArgumentNullException>(() => map.Remove(null));
}
[Test]
@ -346,6 +370,164 @@ namespace Google.Protobuf.Collections
Assert.AreEqual("z", map["x"]);
}
[Test]
public void GetEnumerator_NonGeneric()
{
IEnumerable map = new MapField<string, string> { { "x", "y" } };
CollectionAssert.AreEqual(new[] { new KeyValuePair<string, string>("x", "y") },
map.Cast<object>().ToList());
}
// Test for the explicitly-implemented non-generic IDictionary interface
[Test]
public void IDictionary_GetEnumerator()
{
IDictionary map = new MapField<string, string> { { "x", "y" } };
var enumerator = map.GetEnumerator();
// Commented assertions show an ideal situation - it looks like
// the LinkedList enumerator doesn't throw when you ask for the current entry
// at an inappropriate time; fixing this would be more work than it's worth.
// Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode());
Assert.IsTrue(enumerator.MoveNext());
Assert.AreEqual("x", enumerator.Key);
Assert.AreEqual("y", enumerator.Value);
Assert.AreEqual(new DictionaryEntry("x", "y"), enumerator.Current);
Assert.AreEqual(new DictionaryEntry("x", "y"), enumerator.Entry);
Assert.IsFalse(enumerator.MoveNext());
// Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode());
enumerator.Reset();
// Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode());
Assert.IsTrue(enumerator.MoveNext());
Assert.AreEqual("x", enumerator.Key); // Assume the rest are okay
}
[Test]
public void IDictionary_Add()
{
var map = new MapField<string, string> { { "x", "y" } };
IDictionary dictionary = map;
dictionary.Add("a", "b");
Assert.AreEqual("b", map["a"]);
Assert.Throws<ArgumentException>(() => dictionary.Add("a", "duplicate"));
Assert.Throws<InvalidCastException>(() => dictionary.Add(new object(), "key is bad"));
Assert.Throws<InvalidCastException>(() => dictionary.Add("value is bad", new object()));
}
[Test]
public void IDictionary_Contains()
{
var map = new MapField<string, string> { { "x", "y" } };
IDictionary dictionary = map;
Assert.IsFalse(dictionary.Contains("a"));
Assert.IsFalse(dictionary.Contains(5));
// Surprising, but IDictionary.Contains is only about keys.
Assert.IsFalse(dictionary.Contains(new DictionaryEntry("x", "y")));
Assert.IsTrue(dictionary.Contains("x"));
}
[Test]
public void IDictionary_Remove()
{
var map = new MapField<string, string> { { "x", "y" } };
IDictionary dictionary = map;
dictionary.Remove("a");
Assert.AreEqual(1, dictionary.Count);
dictionary.Remove(5);
Assert.AreEqual(1, dictionary.Count);
dictionary.Remove(new DictionaryEntry("x", "y"));
Assert.AreEqual(1, dictionary.Count);
dictionary.Remove("x");
Assert.AreEqual(0, dictionary.Count);
Assert.Throws<ArgumentNullException>(() => dictionary.Remove(null));
map.Freeze();
// Call should fail even though it clearly doesn't contain 5 as a key.
Assert.Throws<InvalidOperationException>(() => dictionary.Remove(5));
}
[Test]
public void IDictionary_CopyTo()
{
var map = new MapField<string, string> { { "x", "y" } };
IDictionary dictionary = map;
var array = new DictionaryEntry[3];
dictionary.CopyTo(array, 1);
CollectionAssert.AreEqual(new[] { default(DictionaryEntry), new DictionaryEntry("x", "y"), default(DictionaryEntry) },
array);
var objectArray = new object[3];
dictionary.CopyTo(objectArray, 1);
CollectionAssert.AreEqual(new object[] { null, new DictionaryEntry("x", "y"), null },
objectArray);
}
[Test]
public void IDictionary_IsFixedSize()
{
var map = new MapField<string, string> { { "x", "y" } };
IDictionary dictionary = map;
Assert.IsFalse(dictionary.IsFixedSize);
map.Freeze();
Assert.IsTrue(dictionary.IsFixedSize);
}
[Test]
public void IDictionary_Keys()
{
IDictionary dictionary = new MapField<string, string> { { "x", "y" } };
CollectionAssert.AreEqual(new[] { "x" }, dictionary.Keys);
}
[Test]
public void IDictionary_Values()
{
IDictionary dictionary = new MapField<string, string> { { "x", "y" } };
CollectionAssert.AreEqual(new[] { "y" }, dictionary.Values);
}
[Test]
public void IDictionary_IsSynchronized()
{
IDictionary dictionary = new MapField<string, string> { { "x", "y" } };
Assert.IsFalse(dictionary.IsSynchronized);
}
[Test]
public void IDictionary_SyncRoot()
{
IDictionary dictionary = new MapField<string, string> { { "x", "y" } };
Assert.AreSame(dictionary, dictionary.SyncRoot);
}
[Test]
public void IDictionary_Indexer_Get()
{
IDictionary dictionary = new MapField<string, string> { { "x", "y" } };
Assert.AreEqual("y", dictionary["x"]);
Assert.IsNull(dictionary["a"]);
Assert.IsNull(dictionary[5]);
Assert.Throws<ArgumentNullException>(() => dictionary[null].GetHashCode());
}
[Test]
public void IDictionary_Indexer_Set()
{
var map = new MapField<string, string> { { "x", "y" } };
IDictionary dictionary = map;
map["a"] = "b";
Assert.AreEqual("b", map["a"]);
map["a"] = "c";
Assert.AreEqual("c", map["a"]);
Assert.Throws<InvalidCastException>(() => dictionary[5] = "x");
Assert.Throws<InvalidCastException>(() => dictionary["x"] = 5);
Assert.Throws<ArgumentNullException>(() => dictionary[null] = "z");
Assert.Throws<ArgumentNullException>(() => dictionary["x"] = null);
map.Freeze();
// Note: Not InvalidOperationException.
Assert.Throws<NotSupportedException>(() => dictionary["a"] = "c");
}
private static KeyValuePair<TKey, TValue> NewKeyValuePair<TKey, TValue>(TKey key, TValue value)
{
return new KeyValuePair<TKey, TValue>(key, value);

View File

@ -31,9 +31,11 @@
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Google.Protobuf.TestProtos;
using NUnit.Framework;
@ -82,6 +84,115 @@ namespace Google.Protobuf.Collections
Assert.AreEqual("bar", list[2]);
}
[Test]
public void RemoveAt_Valid()
{
var list = new RepeatedField<string> { "first", "second", "third" };
list.RemoveAt(1);
CollectionAssert.AreEqual(new[] { "first", "third" }, list);
// Just check that these don't throw...
list.RemoveAt(list.Count - 1); // Now the count will be 1...
list.RemoveAt(0);
Assert.AreEqual(0, list.Count);
}
[Test]
public void RemoveAt_Invalid()
{
var list = new RepeatedField<string> { "first", "second", "third" };
Assert.Throws<ArgumentOutOfRangeException>(() => list.RemoveAt(-1));
Assert.Throws<ArgumentOutOfRangeException>(() => list.RemoveAt(3));
}
[Test]
public void Insert_Valid()
{
var list = new RepeatedField<string> { "first", "second" };
list.Insert(1, "middle");
CollectionAssert.AreEqual(new[] { "first", "middle", "second" }, list);
list.Insert(3, "end");
CollectionAssert.AreEqual(new[] { "first", "middle", "second", "end" }, list);
list.Insert(0, "start");
CollectionAssert.AreEqual(new[] { "start", "first", "middle", "second", "end" }, list);
}
[Test]
public void Insert_Invalid()
{
var list = new RepeatedField<string> { "first", "second" };
Assert.Throws<ArgumentOutOfRangeException>(() => list.Insert(-1, "foo"));
Assert.Throws<ArgumentOutOfRangeException>(() => list.Insert(3, "foo"));
Assert.Throws<ArgumentNullException>(() => list.Insert(0, null));
}
[Test]
public void Equals_RepeatedField()
{
var list = new RepeatedField<string> { "first", "second" };
Assert.IsFalse(list.Equals((RepeatedField<string>) null));
Assert.IsTrue(list.Equals(list));
Assert.IsFalse(list.Equals(new RepeatedField<string> { "first", "third" }));
Assert.IsFalse(list.Equals(new RepeatedField<string> { "first" }));
Assert.IsTrue(list.Equals(new RepeatedField<string> { "first", "second" }));
}
[Test]
public void Equals_Object()
{
var list = new RepeatedField<string> { "first", "second" };
Assert.IsFalse(list.Equals((object) null));
Assert.IsTrue(list.Equals((object) list));
Assert.IsFalse(list.Equals((object) new RepeatedField<string> { "first", "third" }));
Assert.IsFalse(list.Equals((object) new RepeatedField<string> { "first" }));
Assert.IsTrue(list.Equals((object) new RepeatedField<string> { "first", "second" }));
Assert.IsFalse(list.Equals(new object()));
}
[Test]
public void GetEnumerator_GenericInterface()
{
IEnumerable<string> list = new RepeatedField<string> { "first", "second" };
// Select gets rid of the optimizations in ToList...
CollectionAssert.AreEqual(new[] { "first", "second" }, list.Select(x => x).ToList());
}
[Test]
public void GetEnumerator_NonGenericInterface()
{
IEnumerable list = new RepeatedField<string> { "first", "second" };
CollectionAssert.AreEqual(new[] { "first", "second" }, list.Cast<object>().ToList());
}
[Test]
public void CopyTo()
{
var list = new RepeatedField<string> { "first", "second" };
string[] stringArray = new string[4];
list.CopyTo(stringArray, 1);
CollectionAssert.AreEqual(new[] { null, "first", "second", null }, stringArray);
}
[Test]
public void Indexer_Get()
{
var list = new RepeatedField<string> { "first", "second" };
Assert.AreEqual("first", list[0]);
Assert.AreEqual("second", list[1]);
Assert.Throws<ArgumentOutOfRangeException>(() => list[-1].GetHashCode());
Assert.Throws<ArgumentOutOfRangeException>(() => list[2].GetHashCode());
}
[Test]
public void Indexer_Set()
{
var list = new RepeatedField<string> { "first", "second" };
list[0] = "changed";
Assert.AreEqual("changed", list[0]);
Assert.Throws<ArgumentNullException>(() => list[0] = null);
Assert.Throws<ArgumentOutOfRangeException>(() => list[-1] = "bad");
Assert.Throws<ArgumentOutOfRangeException>(() => list[2] = "bad");
}
[Test]
public void Freeze_FreezesElements()
{
@ -124,6 +235,27 @@ namespace Google.Protobuf.Collections
clone[0] = 1;
}
[Test]
public void Enumerator()
{
var list = new RepeatedField<string> { "first", "second" };
using (var enumerator = list.GetEnumerator())
{
Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode());
Assert.IsTrue(enumerator.MoveNext());
Assert.AreEqual("first", enumerator.Current);
Assert.IsTrue(enumerator.MoveNext());
Assert.AreEqual("second", enumerator.Current);
Assert.IsFalse(enumerator.MoveNext());
Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode());
Assert.IsFalse(enumerator.MoveNext());
enumerator.Reset();
Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode());
Assert.IsTrue(enumerator.MoveNext());
Assert.AreEqual("first", enumerator.Current);
}
}
[Test]
public void AddEntriesFrom_PackedInt32()
{
@ -309,6 +441,42 @@ namespace Google.Protobuf.Collections
Assert.IsTrue(input.IsAtEnd);
}
[Test]
public void CalculateSize_VariableSizeNonPacked()
{
var list = new RepeatedField<int> { 1, 500, 1 };
var tag = WireFormat.MakeTag(1, WireFormat.WireType.Varint);
// 2 bytes for the first entry, 3 bytes for the second, 2 bytes for the third
Assert.AreEqual(7, list.CalculateSize(FieldCodec.ForInt32(tag)));
}
[Test]
public void CalculateSize_FixedSizeNonPacked()
{
var list = new RepeatedField<int> { 1, 500, 1 };
var tag = WireFormat.MakeTag(1, WireFormat.WireType.Fixed32);
// 5 bytes for the each entry
Assert.AreEqual(15, list.CalculateSize(FieldCodec.ForSFixed32(tag)));
}
[Test]
public void CalculateSize_VariableSizePacked()
{
var list = new RepeatedField<int> { 1, 500, 1};
var tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
// 1 byte for the tag, 1 byte for the length,
// 1 byte for the first entry, 2 bytes for the second, 1 byte for the third
Assert.AreEqual(6, list.CalculateSize(FieldCodec.ForInt32(tag)));
}
[Test]
public void CalculateSize_FixedSizePacked()
{
var list = new RepeatedField<int> { 1, 500, 1 };
var tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
// 1 byte for the tag, 1 byte for the length, 4 bytes per entry
Assert.AreEqual(14, list.CalculateSize(FieldCodec.ForSFixed32(tag)));
}
[Test]
public void TestNegativeEnumArray()
@ -378,5 +546,100 @@ namespace Google.Protobuf.Collections
Assert.AreEqual(((SampleEnum)(-4)), values[4]);
Assert.AreEqual(((SampleEnum)(-5)), values[5]);
}
// Fairly perfunctory tests for the non-generic IList implementation
[Test]
public void IList_Indexer()
{
var field = new RepeatedField<string> { "first", "second" };
IList list = field;
Assert.AreEqual("first", list[0]);
list[1] = "changed";
Assert.AreEqual("changed", field[1]);
}
[Test]
public void IList_Contains()
{
IList list = new RepeatedField<string> { "first", "second" };
Assert.IsTrue(list.Contains("second"));
Assert.IsFalse(list.Contains("third"));
Assert.IsFalse(list.Contains(new object()));
}
[Test]
public void IList_Add()
{
IList list = new RepeatedField<string> { "first", "second" };
list.Add("third");
CollectionAssert.AreEqual(new[] { "first", "second", "third" }, list);
}
[Test]
public void IList_Remove()
{
IList list = new RepeatedField<string> { "first", "second" };
list.Remove("third"); // No-op, no exception
list.Remove(new object()); // No-op, no exception
list.Remove("first");
CollectionAssert.AreEqual(new[] { "second" }, list);
}
[Test]
public void IList_IsFixedSize()
{
var field = new RepeatedField<string> { "first", "second" };
IList list = field;
Assert.IsFalse(list.IsFixedSize);
field.Freeze();
Assert.IsTrue(list.IsFixedSize);
}
[Test]
public void IList_IndexOf()
{
IList list = new RepeatedField<string> { "first", "second" };
Assert.AreEqual(1, list.IndexOf("second"));
Assert.AreEqual(-1, list.IndexOf("third"));
Assert.AreEqual(-1, list.IndexOf(new object()));
}
[Test]
public void IList_SyncRoot()
{
IList list = new RepeatedField<string> { "first", "second" };
Assert.AreSame(list, list.SyncRoot);
}
[Test]
public void IList_CopyTo()
{
IList list = new RepeatedField<string> { "first", "second" };
string[] stringArray = new string[4];
list.CopyTo(stringArray, 1);
CollectionAssert.AreEqual(new[] { null, "first", "second", null }, stringArray);
object[] objectArray = new object[4];
list.CopyTo(objectArray, 1);
CollectionAssert.AreEqual(new[] { null, "first", "second", null }, objectArray);
Assert.Throws<ArrayTypeMismatchException>(() => list.CopyTo(new StringBuilder[4], 1));
Assert.Throws<ArrayTypeMismatchException>(() => list.CopyTo(new int[4], 1));
}
[Test]
public void IList_IsSynchronized()
{
IList list = new RepeatedField<string> { "first", "second" };
Assert.IsFalse(list.IsSynchronized);
}
[Test]
public void IList_Insert()
{
IList list = new RepeatedField<string> { "first", "second" };
list.Insert(1, "middle");
CollectionAssert.AreEqual(new[] { "first", "middle", "second" }, list);
}
}
}

View File

@ -30,6 +30,8 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System.Linq;
using Google.Protobuf.DescriptorProtos;
using Google.Protobuf.Descriptors;
using Google.Protobuf.TestProtos;
using NUnit.Framework;
@ -50,7 +52,7 @@ namespace Google.Protobuf
Assert.AreEqual("google/protobuf/unittest_proto3.proto", file.Name);
Assert.AreEqual("protobuf_unittest", file.Package);
Assert.AreEqual("UnittestProto", file.Options.JavaOuterClassname);
Assert.AreEqual("UnittestProto", file.Proto.Options.JavaOuterClassname);
Assert.AreEqual("google/protobuf/unittest_proto3.proto", file.Proto.Name);
// unittest.proto doesn't have any public imports, but unittest_import.proto does.
@ -92,7 +94,7 @@ namespace Google.Protobuf
Assert.AreEqual("protobuf_unittest.TestAllTypes", messageType.FullName);
Assert.AreEqual(UnittestProto3.Descriptor, messageType.File);
Assert.IsNull(messageType.ContainingType);
Assert.IsNull(messageType.Options);
Assert.IsNull(messageType.Proto.Options);
Assert.AreEqual("TestAllTypes", messageType.Name);
@ -143,7 +145,7 @@ namespace Google.Protobuf
Assert.AreEqual(messageType, primitiveField.ContainingType);
Assert.AreEqual(UnittestProto3.Descriptor, primitiveField.File);
Assert.AreEqual(FieldType.Int32, primitiveField.FieldType);
Assert.IsNull(primitiveField.Options);
Assert.IsNull(primitiveField.Proto.Options);
Assert.AreEqual("single_nested_enum", enumField.Name);
Assert.AreEqual(FieldType.Enum, enumField.FieldType);
@ -177,7 +179,7 @@ namespace Google.Protobuf
Assert.AreEqual("protobuf_unittest.ForeignEnum", enumType.FullName);
Assert.AreEqual(UnittestProto3.Descriptor, enumType.File);
Assert.Null(enumType.ContainingType);
Assert.Null(enumType.Options);
Assert.Null(enumType.Proto.Options);
Assert.AreEqual("NestedEnum", nestedType.Name);
Assert.AreEqual("protobuf_unittest.TestAllTypes.NestedEnum",
@ -197,5 +199,27 @@ namespace Google.Protobuf
Assert.AreEqual(i, enumType.Values[i].Index);
}
}
[Test]
public void OneofDescriptor()
{
OneofDescriptor descriptor = TestAllTypes.Descriptor.FindDescriptor<OneofDescriptor>("oneof_field");
Assert.AreEqual("oneof_field", descriptor.Name);
Assert.AreEqual("protobuf_unittest.TestAllTypes.oneof_field", descriptor.FullName);
var expectedFields = new[] {
TestAllTypes.OneofBytesFieldNumber,
TestAllTypes.OneofNestedMessageFieldNumber,
TestAllTypes.OneofStringFieldNumber,
TestAllTypes.OneofUint32FieldNumber }
.Select(fieldNumber => TestAllTypes.Descriptor.FindFieldByNumber(fieldNumber))
.ToList();
foreach (var field in expectedFields)
{
Assert.AreSame(descriptor, field.ContainingOneof);
}
CollectionAssert.AreEquivalent(expectedFields, descriptor.Fields);
}
}
}

View File

@ -45,15 +45,20 @@ namespace Google.Protobuf
public static void AssertEquality<T>(T first, T second) where T : IEquatable<T>
{
Assert.IsTrue(first.Equals(second));
Assert.IsTrue(first.Equals((object) second));
Assert.AreEqual(first.GetHashCode(), second.GetHashCode());
}
public static void AssertInequality<T>(T first, T second) where T : IEquatable<T>
{
Assert.IsFalse(first.Equals(second));
Assert.IsFalse(first.Equals((object) second));
// While this isn't a requirement, the chances of this test failing due to
// coincidence rather than a bug are very small.
Assert.AreNotEqual(first.GetHashCode(), second.GetHashCode());
if (first != null && second != null)
{
Assert.AreNotEqual(first.GetHashCode(), second.GetHashCode());
}
}
}
}

View File

@ -86,12 +86,22 @@ namespace Google.Protobuf
codec.TestDefaultValue();
}
[Test, TestCaseSource("Codecs")]
public void FixedSize(ICodecTestData codec)
{
codec.TestFixedSize();
}
// This is ugly, but it means we can have a non-generic interface.
// It feels like NUnit should support this better, but I don't know
// of any better ways right now.
public interface ICodecTestData
{
void TestRoundTripRaw();
void TestRoundTripWithTag();
void TestCalculateSizeWithTag();
void TestDefaultValue();
void TestFixedSize();
}
public class FieldCodecTestData<T> : ICodecTestData
@ -169,6 +179,11 @@ namespace Google.Protobuf
}
}
public void TestFixedSize()
{
Assert.AreEqual(name.Contains("Fixed"), codec.FixedSize != 0);
}
public override string ToString()
{
return name;

View File

@ -29,11 +29,13 @@
// (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.IO;
using Google.Protobuf.TestProtos;
using NUnit.Framework;
using System.Collections;
using System.Collections.Generic;
namespace Google.Protobuf
{
@ -595,5 +597,142 @@ namespace Google.Protobuf
Assert.AreEqual(message, message2);
Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
}
// TODO: Consider moving these tests to a separate reflection test - although they do require generated messages.
[Test]
public void Reflection_GetValue()
{
var message = SampleMessages.CreateFullTestAllTypes();
var fields = message.Fields;
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));
Assert.AreEqual(message.SingleFixed32, fields[TestAllTypes.SingleFixed32FieldNumber].GetValue(message));
Assert.AreEqual(message.SingleFixed64, fields[TestAllTypes.SingleFixed64FieldNumber].GetValue(message));
Assert.AreEqual(message.SingleFloat, fields[TestAllTypes.SingleFloatFieldNumber].GetValue(message));
Assert.AreEqual(message.SingleForeignEnum, fields[TestAllTypes.SingleForeignEnumFieldNumber].GetValue(message));
Assert.AreEqual(message.SingleForeignMessage, fields[TestAllTypes.SingleForeignMessageFieldNumber].GetValue(message));
Assert.AreEqual(message.SingleImportEnum, fields[TestAllTypes.SingleImportEnumFieldNumber].GetValue(message));
Assert.AreEqual(message.SingleImportMessage, fields[TestAllTypes.SingleImportMessageFieldNumber].GetValue(message));
Assert.AreEqual(message.SingleInt32, fields[TestAllTypes.SingleInt32FieldNumber].GetValue(message));
Assert.AreEqual(message.SingleInt64, fields[TestAllTypes.SingleInt64FieldNumber].GetValue(message));
Assert.AreEqual(message.SingleNestedEnum, fields[TestAllTypes.SingleNestedEnumFieldNumber].GetValue(message));
Assert.AreEqual(message.SingleNestedMessage, fields[TestAllTypes.SingleNestedMessageFieldNumber].GetValue(message));
Assert.AreEqual(message.SinglePublicImportMessage, fields[TestAllTypes.SinglePublicImportMessageFieldNumber].GetValue(message));
Assert.AreEqual(message.SingleSint32, fields[TestAllTypes.SingleSint32FieldNumber].GetValue(message));
Assert.AreEqual(message.SingleSint64, fields[TestAllTypes.SingleSint64FieldNumber].GetValue(message));
Assert.AreEqual(message.SingleString, fields[TestAllTypes.SingleStringFieldNumber].GetValue(message));
Assert.AreEqual(message.SingleSfixed32, fields[TestAllTypes.SingleSfixed32FieldNumber].GetValue(message));
Assert.AreEqual(message.SingleSfixed64, fields[TestAllTypes.SingleSfixed64FieldNumber].GetValue(message));
Assert.AreEqual(message.SingleUint32, fields[TestAllTypes.SingleUint32FieldNumber].GetValue(message));
Assert.AreEqual(message.SingleUint64, fields[TestAllTypes.SingleUint64FieldNumber].GetValue(message));
Assert.AreEqual(message.OneofBytes, fields[TestAllTypes.OneofBytesFieldNumber].GetValue(message));
Assert.AreEqual(message.OneofString, fields[TestAllTypes.OneofStringFieldNumber].GetValue(message));
Assert.AreEqual(message.OneofNestedMessage, fields[TestAllTypes.OneofNestedMessageFieldNumber].GetValue(message));
Assert.AreEqual(message.OneofUint32, fields[TestAllTypes.OneofUint32FieldNumber].GetValue(message));
// Just one example for repeated fields - they're all just returning the list
var list = (IList)fields[TestAllTypes.RepeatedInt32FieldNumber].GetValue(message);
Assert.AreEqual(message.RepeatedInt32, list);
Assert.AreEqual(message.RepeatedInt32[0], list[0]); // Just in case there was any doubt...
// Just a single map field, for the same reason
var mapMessage = new TestMap { MapStringString = { { "key1", "value1" }, { "key2", "value2" } } };
var dictionary = (IDictionary)mapMessage.Fields[TestMap.MapStringStringFieldNumber].GetValue(mapMessage);
Assert.AreEqual(mapMessage.MapStringString, dictionary);
Assert.AreEqual("value1", dictionary["key1"]);
}
[Test]
public void Reflection_Clear()
{
var message = SampleMessages.CreateFullTestAllTypes();
var fields = message.Fields;
fields[TestAllTypes.SingleBoolFieldNumber].Clear(message);
fields[TestAllTypes.SingleInt32FieldNumber].Clear(message);
fields[TestAllTypes.SingleStringFieldNumber].Clear(message);
fields[TestAllTypes.SingleBytesFieldNumber].Clear(message);
fields[TestAllTypes.SingleForeignEnumFieldNumber].Clear(message);
fields[TestAllTypes.SingleForeignMessageFieldNumber].Clear(message);
fields[TestAllTypes.RepeatedDoubleFieldNumber].Clear(message);
var expected = new TestAllTypes(SampleMessages.CreateFullTestAllTypes())
{
SingleBool = false,
SingleInt32 = 0,
SingleString = "",
SingleBytes = ByteString.Empty,
SingleForeignEnum = 0,
SingleForeignMessage = null,
};
expected.RepeatedDouble.Clear();
Assert.AreEqual(expected, message);
// Separately, maps.
var mapMessage = new TestMap { MapStringString = { { "key1", "value1" }, { "key2", "value2" } } };
mapMessage.Fields[TestMap.MapStringStringFieldNumber].Clear(mapMessage);
Assert.AreEqual(0, mapMessage.MapStringString.Count);
}
[Test]
public void Reflection_SetValue_SingleFields()
{
// Just a sample (primitives, messages, enums, strings, byte strings)
var message = SampleMessages.CreateFullTestAllTypes();
var fields = message.Fields;
fields[TestAllTypes.SingleBoolFieldNumber].SetValue(message, false);
fields[TestAllTypes.SingleInt32FieldNumber].SetValue(message, 500);
fields[TestAllTypes.SingleStringFieldNumber].SetValue(message, "It's a string");
fields[TestAllTypes.SingleBytesFieldNumber].SetValue(message, ByteString.CopyFrom(99, 98, 97));
fields[TestAllTypes.SingleForeignEnumFieldNumber].SetValue(message, ForeignEnum.FOREIGN_FOO);
fields[TestAllTypes.SingleForeignMessageFieldNumber].SetValue(message, new ForeignMessage { C = 12345 });
fields[TestAllTypes.SingleDoubleFieldNumber].SetValue(message, 20150701.5);
var expected = new TestAllTypes(SampleMessages.CreateFullTestAllTypes())
{
SingleBool = false,
SingleInt32 = 500,
SingleString = "It's a string",
SingleBytes = ByteString.CopyFrom(99, 98, 97),
SingleForeignEnum = ForeignEnum.FOREIGN_FOO,
SingleForeignMessage = new ForeignMessage { C = 12345 },
SingleDouble = 20150701.5
};
Assert.AreEqual(expected, message);
}
[Test]
public void Reflection_SetValue_SingleFields_WrongType()
{
var message = SampleMessages.CreateFullTestAllTypes();
var fields = message.Fields;
Assert.Throws<InvalidCastException>(() => fields[TestAllTypes.SingleBoolFieldNumber].SetValue(message, "This isn't a bool"));
}
[Test]
public void Reflection_SetValue_MapFields()
{
var message = new TestMap();
var fields = message.Fields;
Assert.Throws<InvalidOperationException>(() => fields[TestMap.MapStringStringFieldNumber].SetValue(message, new Dictionary<string, string>()));
}
[Test]
public void Reflection_SetValue_RepeatedFields()
{
var message = SampleMessages.CreateFullTestAllTypes();
var fields = message.Fields;
Assert.Throws<InvalidOperationException>(() => fields[TestAllTypes.RepeatedDoubleFieldNumber].SetValue(message, new double[10]));
}
[Test]
public void Reflection_GetValue_IncorrectType()
{
var message = SampleMessages.CreateFullTestAllTypes();
Assert.Throws<InvalidCastException>(() => message.Fields[TestAllTypes.SingleBoolFieldNumber].GetValue(new TestMap()));
}
}
}

View File

@ -13,8 +13,7 @@ namespace Google.Protobuf.TestProtos {
public static partial class UnittestImportProto3 {
#region Static variables
internal static pbd::MessageDescriptor internal__static_protobuf_unittest_import_ImportMessage__Descriptor;
internal static pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.TestProtos.ImportMessage> internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable;
internal static pb::FieldAccess.FieldAccessorTable internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable;
#endregion
#region Descriptor
public static pbd::FileDescriptor Descriptor {
@ -33,17 +32,13 @@ namespace Google.Protobuf.TestProtos {
"UhAIEg4KCklNUE9SVF9CQVoQCUI8Chhjb20uZ29vZ2xlLnByb3RvYnVmLnRl",
"c3RIAfgBAaoCGkdvb2dsZS5Qcm90b2J1Zi5UZXN0UHJvdG9zUABiBnByb3Rv",
"Mw=="));
pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) {
descriptor = root;
internal__static_protobuf_unittest_import_ImportMessage__Descriptor = Descriptor.MessageTypes[0];
internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.TestProtos.ImportMessage>(internal__static_protobuf_unittest_import_ImportMessage__Descriptor,
new string[] { "D", });
};
pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
descriptor = pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
new pbd::FileDescriptor[] {
global::Google.Protobuf.TestProtos.UnittestImportPublicProto3.Descriptor,
}, assigner);
});
internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.TestProtos.ImportMessage), descriptor.MessageTypes[0],
new string[] { "D", });
}
#endregion
@ -67,19 +62,23 @@ namespace Google.Protobuf.TestProtos {
private static readonly string[] _fieldNames = new string[] { "d" };
private static readonly uint[] _fieldTags = new uint[] { 8 };
public static pbd::MessageDescriptor Descriptor {
get { return global::Google.Protobuf.TestProtos.UnittestImportProto3.internal__static_protobuf_unittest_import_ImportMessage__Descriptor; }
get { return global::Google.Protobuf.TestProtos.UnittestImportProto3.Descriptor.MessageTypes[0]; }
}
public pb::FieldAccess.FieldAccessorTable<ImportMessage> Fields {
public pb::FieldAccess.FieldAccessorTable Fields {
get { return global::Google.Protobuf.TestProtos.UnittestImportProto3.internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable; }
}
private bool _frozen = false;
public bool IsFrozen { get { return _frozen; } }
public ImportMessage() { }
public ImportMessage() {
OnConstruction();
}
public ImportMessage(ImportMessage other) {
partial void OnConstruction();
public ImportMessage(ImportMessage other) : this() {
d_ = other.d_;
}

View File

@ -13,8 +13,7 @@ namespace Google.Protobuf.TestProtos {
public static partial class UnittestImportPublicProto3 {
#region Static variables
internal static pbd::MessageDescriptor internal__static_protobuf_unittest_import_PublicImportMessage__Descriptor;
internal static pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.TestProtos.PublicImportMessage> internal__static_protobuf_unittest_import_PublicImportMessage__FieldAccessorTable;
internal static pb::FieldAccess.FieldAccessorTable internal__static_protobuf_unittest_import_PublicImportMessage__FieldAccessorTable;
#endregion
#region Descriptor
public static pbd::FileDescriptor Descriptor {
@ -29,16 +28,12 @@ namespace Google.Protobuf.TestProtos {
"bzMucHJvdG8SGHByb3RvYnVmX3VuaXR0ZXN0X2ltcG9ydCIgChNQdWJsaWNJ",
"bXBvcnRNZXNzYWdlEgkKAWUYASABKAVCNwoYY29tLmdvb2dsZS5wcm90b2J1",
"Zi50ZXN0qgIaR29vZ2xlLlByb3RvYnVmLlRlc3RQcm90b3NiBnByb3RvMw=="));
pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) {
descriptor = root;
internal__static_protobuf_unittest_import_PublicImportMessage__Descriptor = Descriptor.MessageTypes[0];
internal__static_protobuf_unittest_import_PublicImportMessage__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.TestProtos.PublicImportMessage>(internal__static_protobuf_unittest_import_PublicImportMessage__Descriptor,
new string[] { "E", });
};
pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
descriptor = pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
new pbd::FileDescriptor[] {
}, assigner);
});
internal__static_protobuf_unittest_import_PublicImportMessage__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.TestProtos.PublicImportMessage), descriptor.MessageTypes[0],
new string[] { "E", });
}
#endregion
@ -52,19 +47,23 @@ namespace Google.Protobuf.TestProtos {
private static readonly string[] _fieldNames = new string[] { "e" };
private static readonly uint[] _fieldTags = new uint[] { 8 };
public static pbd::MessageDescriptor Descriptor {
get { return global::Google.Protobuf.TestProtos.UnittestImportPublicProto3.internal__static_protobuf_unittest_import_PublicImportMessage__Descriptor; }
get { return global::Google.Protobuf.TestProtos.UnittestImportPublicProto3.Descriptor.MessageTypes[0]; }
}
public pb::FieldAccess.FieldAccessorTable<PublicImportMessage> Fields {
public pb::FieldAccess.FieldAccessorTable Fields {
get { return global::Google.Protobuf.TestProtos.UnittestImportPublicProto3.internal__static_protobuf_unittest_import_PublicImportMessage__FieldAccessorTable; }
}
private bool _frozen = false;
public bool IsFrozen { get { return _frozen; } }
public PublicImportMessage() { }
public PublicImportMessage() {
OnConstruction();
}
public PublicImportMessage(PublicImportMessage other) {
partial void OnConstruction();
public PublicImportMessage(PublicImportMessage other) : this() {
e_ = other.e_;
}

View File

@ -13,20 +13,13 @@ namespace UnitTest.Issues.TestProtos {
public static partial class UnittestIssues {
#region Static variables
internal static pbd::MessageDescriptor internal__static_unittest_issues_Issue307__Descriptor;
internal static pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.Issue307> internal__static_unittest_issues_Issue307__FieldAccessorTable;
internal static pbd::MessageDescriptor internal__static_unittest_issues_Issue307_NestedOnce__Descriptor;
internal static pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce> internal__static_unittest_issues_Issue307_NestedOnce__FieldAccessorTable;
internal static pbd::MessageDescriptor internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__Descriptor;
internal static pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Types.NestedTwice> internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__FieldAccessorTable;
internal static pbd::MessageDescriptor internal__static_unittest_issues_NegativeEnumMessage__Descriptor;
internal static pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.NegativeEnumMessage> internal__static_unittest_issues_NegativeEnumMessage__FieldAccessorTable;
internal static pbd::MessageDescriptor internal__static_unittest_issues_DeprecatedChild__Descriptor;
internal static pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.DeprecatedChild> internal__static_unittest_issues_DeprecatedChild__FieldAccessorTable;
internal static pbd::MessageDescriptor internal__static_unittest_issues_DeprecatedFieldsMessage__Descriptor;
internal static pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage> internal__static_unittest_issues_DeprecatedFieldsMessage__FieldAccessorTable;
internal static pbd::MessageDescriptor internal__static_unittest_issues_ItemField__Descriptor;
internal static pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.ItemField> internal__static_unittest_issues_ItemField__FieldAccessorTable;
internal static pb::FieldAccess.FieldAccessorTable internal__static_unittest_issues_Issue307__FieldAccessorTable;
internal static pb::FieldAccess.FieldAccessorTable internal__static_unittest_issues_Issue307_NestedOnce__FieldAccessorTable;
internal static pb::FieldAccess.FieldAccessorTable internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__FieldAccessorTable;
internal static pb::FieldAccess.FieldAccessorTable internal__static_unittest_issues_NegativeEnumMessage__FieldAccessorTable;
internal static pb::FieldAccess.FieldAccessorTable internal__static_unittest_issues_DeprecatedChild__FieldAccessorTable;
internal static pb::FieldAccess.FieldAccessorTable internal__static_unittest_issues_DeprecatedFieldsMessage__FieldAccessorTable;
internal static pb::FieldAccess.FieldAccessorTable internal__static_unittest_issues_ItemField__FieldAccessorTable;
#endregion
#region Descriptor
public static pbd::FileDescriptor Descriptor {
@ -55,40 +48,30 @@ namespace UnitTest.Issues.TestProtos {
"EPv//////////wESFQoITWludXNPbmUQ////////////ASouCg5EZXByZWNh",
"dGVkRW51bRITCg9ERVBSRUNBVEVEX1pFUk8QABIHCgNvbmUQAUIfSAGqAhpV",
"bml0VGVzdC5Jc3N1ZXMuVGVzdFByb3Rvc2IGcHJvdG8z"));
pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) {
descriptor = root;
internal__static_unittest_issues_Issue307__Descriptor = Descriptor.MessageTypes[0];
internal__static_unittest_issues_Issue307__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.Issue307>(internal__static_unittest_issues_Issue307__Descriptor,
new string[] { });
internal__static_unittest_issues_Issue307_NestedOnce__Descriptor = internal__static_unittest_issues_Issue307__Descriptor.NestedTypes[0];
internal__static_unittest_issues_Issue307_NestedOnce__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce>(internal__static_unittest_issues_Issue307_NestedOnce__Descriptor,
new string[] { });
internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__Descriptor = internal__static_unittest_issues_Issue307_NestedOnce__Descriptor.NestedTypes[0];
internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Types.NestedTwice>(internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__Descriptor,
new string[] { });
internal__static_unittest_issues_NegativeEnumMessage__Descriptor = Descriptor.MessageTypes[1];
internal__static_unittest_issues_NegativeEnumMessage__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.NegativeEnumMessage>(internal__static_unittest_issues_NegativeEnumMessage__Descriptor,
new string[] { "Value", "Values", "PackedValues", });
internal__static_unittest_issues_DeprecatedChild__Descriptor = Descriptor.MessageTypes[2];
internal__static_unittest_issues_DeprecatedChild__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.DeprecatedChild>(internal__static_unittest_issues_DeprecatedChild__Descriptor,
new string[] { });
internal__static_unittest_issues_DeprecatedFieldsMessage__Descriptor = Descriptor.MessageTypes[3];
internal__static_unittest_issues_DeprecatedFieldsMessage__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage>(internal__static_unittest_issues_DeprecatedFieldsMessage__Descriptor,
new string[] { "PrimitiveValue", "PrimitiveArray", "MessageValue", "MessageArray", "EnumValue", "EnumArray", });
internal__static_unittest_issues_ItemField__Descriptor = Descriptor.MessageTypes[4];
internal__static_unittest_issues_ItemField__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.ItemField>(internal__static_unittest_issues_ItemField__Descriptor,
new string[] { "Item", });
};
pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
descriptor = pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
new pbd::FileDescriptor[] {
}, assigner);
});
internal__static_unittest_issues_Issue307__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.Issue307), descriptor.MessageTypes[0],
new string[] { });
internal__static_unittest_issues_Issue307_NestedOnce__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce), descriptor.MessageTypes[0].NestedTypes[0],
new string[] { });
internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Types.NestedTwice), descriptor.MessageTypes[0].NestedTypes[0].NestedTypes[0],
new string[] { });
internal__static_unittest_issues_NegativeEnumMessage__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.NegativeEnumMessage), descriptor.MessageTypes[1],
new string[] { "Value", "Values", "PackedValues", });
internal__static_unittest_issues_DeprecatedChild__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.DeprecatedChild), descriptor.MessageTypes[2],
new string[] { });
internal__static_unittest_issues_DeprecatedFieldsMessage__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage), descriptor.MessageTypes[3],
new string[] { "PrimitiveValue", "PrimitiveArray", "MessageValue", "MessageArray", "EnumValue", "EnumArray", });
internal__static_unittest_issues_ItemField__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.ItemField), descriptor.MessageTypes[4],
new string[] { "Item", });
}
#endregion
@ -116,19 +99,23 @@ namespace UnitTest.Issues.TestProtos {
private static readonly string[] _fieldNames = new string[] { };
private static readonly uint[] _fieldTags = new uint[] { };
public static pbd::MessageDescriptor Descriptor {
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_Issue307__Descriptor; }
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.Descriptor.MessageTypes[0]; }
}
public pb::FieldAccess.FieldAccessorTable<Issue307> Fields {
public pb::FieldAccess.FieldAccessorTable Fields {
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_Issue307__FieldAccessorTable; }
}
private bool _frozen = false;
public bool IsFrozen { get { return _frozen; } }
public Issue307() { }
public Issue307() {
OnConstruction();
}
public Issue307(Issue307 other) {
partial void OnConstruction();
public Issue307(Issue307 other) : this() {
}
public Issue307 Clone() {
@ -201,19 +188,23 @@ namespace UnitTest.Issues.TestProtos {
private static readonly string[] _fieldNames = new string[] { };
private static readonly uint[] _fieldTags = new uint[] { };
public static pbd::MessageDescriptor Descriptor {
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_Issue307_NestedOnce__Descriptor; }
get { return global::UnitTest.Issues.TestProtos.Issue307.Descriptor.NestedTypes[0]; }
}
public pb::FieldAccess.FieldAccessorTable<NestedOnce> Fields {
public pb::FieldAccess.FieldAccessorTable Fields {
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_Issue307_NestedOnce__FieldAccessorTable; }
}
private bool _frozen = false;
public bool IsFrozen { get { return _frozen; } }
public NestedOnce() { }
public NestedOnce() {
OnConstruction();
}
public NestedOnce(NestedOnce other) {
partial void OnConstruction();
public NestedOnce(NestedOnce other) : this() {
}
public NestedOnce Clone() {
@ -286,19 +277,23 @@ namespace UnitTest.Issues.TestProtos {
private static readonly string[] _fieldNames = new string[] { };
private static readonly uint[] _fieldTags = new uint[] { };
public static pbd::MessageDescriptor Descriptor {
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__Descriptor; }
get { return global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Descriptor.NestedTypes[0]; }
}
public pb::FieldAccess.FieldAccessorTable<NestedTwice> Fields {
public pb::FieldAccess.FieldAccessorTable Fields {
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__FieldAccessorTable; }
}
private bool _frozen = false;
public bool IsFrozen { get { return _frozen; } }
public NestedTwice() { }
public NestedTwice() {
OnConstruction();
}
public NestedTwice(NestedTwice other) {
partial void OnConstruction();
public NestedTwice(NestedTwice other) : this() {
}
public NestedTwice Clone() {
@ -380,19 +375,23 @@ namespace UnitTest.Issues.TestProtos {
private static readonly string[] _fieldNames = new string[] { "packed_values", "value", "values" };
private static readonly uint[] _fieldTags = new uint[] { 26, 8, 16 };
public static pbd::MessageDescriptor Descriptor {
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_NegativeEnumMessage__Descriptor; }
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.Descriptor.MessageTypes[1]; }
}
public pb::FieldAccess.FieldAccessorTable<NegativeEnumMessage> Fields {
public pb::FieldAccess.FieldAccessorTable Fields {
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_NegativeEnumMessage__FieldAccessorTable; }
}
private bool _frozen = false;
public bool IsFrozen { get { return _frozen; } }
public NegativeEnumMessage() { }
public NegativeEnumMessage() {
OnConstruction();
}
public NegativeEnumMessage(NegativeEnumMessage other) {
partial void OnConstruction();
public NegativeEnumMessage(NegativeEnumMessage other) : this() {
value_ = other.value_;
values_ = other.values_.Clone();
packedValues_ = other.packedValues_.Clone();
@ -529,19 +528,23 @@ namespace UnitTest.Issues.TestProtos {
private static readonly string[] _fieldNames = new string[] { };
private static readonly uint[] _fieldTags = new uint[] { };
public static pbd::MessageDescriptor Descriptor {
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_DeprecatedChild__Descriptor; }
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.Descriptor.MessageTypes[2]; }
}
public pb::FieldAccess.FieldAccessorTable<DeprecatedChild> Fields {
public pb::FieldAccess.FieldAccessorTable Fields {
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_DeprecatedChild__FieldAccessorTable; }
}
private bool _frozen = false;
public bool IsFrozen { get { return _frozen; } }
public DeprecatedChild() { }
public DeprecatedChild() {
OnConstruction();
}
public DeprecatedChild(DeprecatedChild other) {
partial void OnConstruction();
public DeprecatedChild(DeprecatedChild other) : this() {
}
public DeprecatedChild Clone() {
@ -613,19 +616,23 @@ namespace UnitTest.Issues.TestProtos {
private static readonly string[] _fieldNames = new string[] { "EnumArray", "EnumValue", "MessageArray", "MessageValue", "PrimitiveArray", "PrimitiveValue" };
private static readonly uint[] _fieldTags = new uint[] { 50, 40, 34, 26, 18, 8 };
public static pbd::MessageDescriptor Descriptor {
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_DeprecatedFieldsMessage__Descriptor; }
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.Descriptor.MessageTypes[3]; }
}
public pb::FieldAccess.FieldAccessorTable<DeprecatedFieldsMessage> Fields {
public pb::FieldAccess.FieldAccessorTable Fields {
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_DeprecatedFieldsMessage__FieldAccessorTable; }
}
private bool _frozen = false;
public bool IsFrozen { get { return _frozen; } }
public DeprecatedFieldsMessage() { }
public DeprecatedFieldsMessage() {
OnConstruction();
}
public DeprecatedFieldsMessage(DeprecatedFieldsMessage other) {
partial void OnConstruction();
public DeprecatedFieldsMessage(DeprecatedFieldsMessage other) : this() {
primitiveValue_ = other.primitiveValue_;
primitiveArray_ = other.primitiveArray_.Clone();
MessageValue = other.messageValue_ != null ? other.MessageValue.Clone() : null;
@ -849,19 +856,23 @@ namespace UnitTest.Issues.TestProtos {
private static readonly string[] _fieldNames = new string[] { "item" };
private static readonly uint[] _fieldTags = new uint[] { 8 };
public static pbd::MessageDescriptor Descriptor {
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_ItemField__Descriptor; }
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.Descriptor.MessageTypes[4]; }
}
public pb::FieldAccess.FieldAccessorTable<ItemField> Fields {
public pb::FieldAccess.FieldAccessorTable Fields {
get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_ItemField__FieldAccessorTable; }
}
private bool _frozen = false;
public bool IsFrozen { get { return _frozen; } }
public ItemField() { }
public ItemField() {
OnConstruction();
}
public ItemField(ItemField other) {
partial void OnConstruction();
public ItemField(ItemField other) : this() {
item_ = other.item_;
}

File diff suppressed because it is too large Load Diff

View File

@ -48,7 +48,7 @@ namespace Google.Protobuf.Collections
/// </remarks>
/// <typeparam name="TKey">Key type in the map. Must be a type supported by Protocol Buffer map keys.</typeparam>
/// <typeparam name="TValue">Value type in the map. Must be a type supported by Protocol Buffers.</typeparam>
public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IFreezable, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>
public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IFreezable, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDictionary
{
// TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)
private bool frozen;
@ -64,7 +64,7 @@ namespace Google.Protobuf.Collections
{
foreach (var pair in list)
{
clone.Add(pair.Key, pair.Value == null ? pair.Value : ((IDeepCloneable<TValue>) pair.Value).Clone());
clone.Add(pair.Key, pair.Value == null ? pair.Value : ((IDeepCloneable<TValue>)pair.Value).Clone());
}
}
else
@ -309,7 +309,7 @@ namespace Google.Protobuf.Collections
/// </remarks>
/// <param name="input">Stream to read from</param>
/// <param name="codec">Codec describing how the key/value pairs are encoded</param>
public void AddEntriesFrom(CodedInputStream input, Codec codec)
public void AddEntriesFrom(CodedInputStream input, Codec codec)
{
var adapter = new Codec.MessageAdapter(codec);
do
@ -318,7 +318,7 @@ namespace Google.Protobuf.Collections
input.ReadMessage(adapter);
this[adapter.Key] = adapter.Value;
} while (input.MaybeConsumeTag(codec.MapTag));
}
}
public void WriteTo(CodedOutputStream output, Codec codec)
{
@ -350,6 +350,104 @@ namespace Google.Protobuf.Collections
return size;
}
#region IDictionary explicit interface implementation
void IDictionary.Add(object key, object value)
{
Add((TKey)key, (TValue)value);
}
bool IDictionary.Contains(object key)
{
if (!(key is TKey))
{
return false;
}
return ContainsKey((TKey)key);
}
IDictionaryEnumerator IDictionary.GetEnumerator()
{
return new DictionaryEnumerator(GetEnumerator());
}
void IDictionary.Remove(object key)
{
ThrowHelper.ThrowIfNull(key, "key");
this.CheckMutable();
if (!(key is TKey))
{
return;
}
Remove((TKey)key);
}
void ICollection.CopyTo(Array array, int index)
{
// This is ugly and slow as heck, but with any luck it will never be used anyway.
ICollection temp = this.Select(pair => new DictionaryEntry(pair.Key, pair.Value)).ToList();
temp.CopyTo(array, index);
}
bool IDictionary.IsFixedSize { get { return IsFrozen; } }
ICollection IDictionary.Keys { get { return (ICollection)Keys; } }
ICollection IDictionary.Values { get { return (ICollection)Values; } }
bool ICollection.IsSynchronized { get { return false; } }
object ICollection.SyncRoot { get { return this; } }
object IDictionary.this[object key]
{
get
{
ThrowHelper.ThrowIfNull(key, "key");
if (!(key is TKey))
{
return null;
}
TValue value;
TryGetValue((TKey)key, out value);
return value;
}
set
{
if (frozen)
{
throw new NotSupportedException("Dictionary is frozen");
}
this[(TKey)key] = (TValue)value;
}
}
#endregion
private class DictionaryEnumerator : IDictionaryEnumerator
{
private readonly IEnumerator<KeyValuePair<TKey, TValue>> enumerator;
internal DictionaryEnumerator(IEnumerator<KeyValuePair<TKey, TValue>> enumerator)
{
this.enumerator = enumerator;
}
public bool MoveNext()
{
return enumerator.MoveNext();
}
public void Reset()
{
enumerator.Reset();
}
public object Current { get { return Entry; } }
public DictionaryEntry Entry { get { return new DictionaryEntry(Key, Value); } }
public object Key { get { return enumerator.Current.Key; } }
public object Value { get { return enumerator.Current.Value; } }
}
/// <summary>
/// A codec for a specific map field. This contains all the information required to encoded and
/// decode the nested messages.

View File

@ -41,7 +41,7 @@ namespace Google.Protobuf.Collections
/// restrictions (no null values) and capabilities (deep cloning and freezing).
/// </summary>
/// <typeparam name="T">The element type of the repeated field.</typeparam>
public sealed class RepeatedField<T> : IList<T>, IDeepCloneable<RepeatedField<T>>, IEquatable<RepeatedField<T>>, IFreezable
public sealed class RepeatedField<T> : IList<T>, IList, IDeepCloneable<RepeatedField<T>>, IEquatable<RepeatedField<T>>, IFreezable
{
private static readonly T[] EmptyArray = new T[0];
@ -376,6 +376,7 @@ namespace Google.Protobuf.Collections
this.CheckMutable();
EnsureSize(count + 1);
Array.Copy(array, index, array, index + 1, count - index);
array[index] = item;
count++;
}
@ -415,7 +416,60 @@ namespace Google.Protobuf.Collections
array[index] = value;
}
}
#region Explicit interface implementation for IList and ICollection.
bool IList.IsFixedSize { get { return IsFrozen; } }
void ICollection.CopyTo(Array array, int index)
{
Array.Copy(this.array, 0, array, index, count);
}
bool ICollection.IsSynchronized { get { return false; } }
object ICollection.SyncRoot { get { return this; } }
object IList.this[int index]
{
get { return this[index]; }
set { this[index] = (T)value; }
}
int IList.Add(object value)
{
Add((T) value);
return count - 1;
}
bool IList.Contains(object value)
{
return (value is T && Contains((T)value));
}
int IList.IndexOf(object value)
{
if (!(value is T))
{
return -1;
}
return IndexOf((T)value);
}
void IList.Insert(int index, object value)
{
Insert(index, (T) value);
}
void IList.Remove(object value)
{
if (!(value is T))
{
return;
}
Remove((T)value);
}
#endregion
public struct Enumerator : IEnumerator<T>
{
private int index;
@ -431,6 +485,7 @@ namespace Google.Protobuf.Collections
{
if (index + 1 >= field.Count)
{
index = field.Count;
return false;
}
index++;

View File

@ -30,36 +30,18 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
// This file just contains partial classes for each of the
// autogenerated classes, so that they implement
// IDescriptorProto
// This file just contains partial classes for any autogenerated classes that need additional support.
namespace Google.Protobuf.DescriptorProtos
{
public partial class DescriptorProto : IDescriptorProto<MessageOptions>
{
}
public partial class EnumDescriptorProto : IDescriptorProto<EnumOptions>
{
}
public partial class EnumValueDescriptorProto : IDescriptorProto<EnumValueOptions>
{
}
public partial class FieldDescriptorProto : IDescriptorProto<FieldOptions>
{
}
public partial class FileDescriptorProto : IDescriptorProto<FileOptions>
{
}
public partial class MethodDescriptorProto : IDescriptorProto<MethodOptions>
{
}
public partial class ServiceDescriptorProto : IDescriptorProto<ServiceOptions>
internal partial class FieldDescriptorProto
{
// We can't tell the difference between "explicitly set to 0" and "not set"
// in proto3, but we need to tell the difference for OneofIndex. descriptor.proto
// is really a proto2 file, but the runtime doesn't know about proto2 semantics...
// We fake it by defaulting to -1.
partial void OnConstruction()
{
OneofIndex = -1;
}
}
}

View File

@ -34,61 +34,36 @@ using Google.Protobuf.DescriptorProtos;
namespace Google.Protobuf.Descriptors
{
// TODO(jonskeet): The descriptor type hierarchy needs changing so that we can hide the descriptor protos.
/// <summary>
/// Base class for nearly all descriptors, providing common functionality.
/// </summary>
/// <typeparam name="TProto">Type of the protocol buffer form of this descriptor</typeparam>
/// <typeparam name="TOptions">Type of the options protocol buffer for this descriptor</typeparam>
public abstract class DescriptorBase<TProto, TOptions> : IDescriptor<TProto>
where TProto : IMessage, IDescriptorProto<TOptions>
public abstract class DescriptorBase : IDescriptor
{
private TProto proto;
private readonly FileDescriptor file;
private readonly string fullName;
private readonly int index;
protected DescriptorBase(TProto proto, FileDescriptor file, string fullName)
internal DescriptorBase(FileDescriptor file, string fullName, int index)
{
this.proto = proto;
this.file = file;
this.fullName = fullName;
this.index = index;
}
internal virtual void ReplaceProto(TProto newProto)
/// <value>
/// The index of this descriptor within its parent descriptor.
/// </value>
/// <remarks>
/// This returns the index of this descriptor within its parent, for
/// this descriptor's type. (There can be duplicate values for different
/// types, e.g. one enum type with index 0 and one message type with index 0.)
/// </remarks>
public int Index
{
this.proto = newProto;
get { return index; }
}
protected static string ComputeFullName(FileDescriptor file, MessageDescriptor parent, string name)
{
if (parent != null)
{
return parent.FullName + "." + name;
}
if (file.Package.Length > 0)
{
return file.Package + "." + name;
}
return name;
}
IMessage IDescriptor.Proto
{
get { return proto; }
}
/// <summary>
/// Returns the protocol buffer form of this descriptor.
/// </summary>
public TProto Proto
{
get { return proto; }
}
public TOptions Options
{
get { return proto.Options; }
}
public abstract string Name { get; }
/// <summary>
/// The fully qualified name of the descriptor's target.
@ -98,14 +73,6 @@ namespace Google.Protobuf.Descriptors
get { return fullName; }
}
/// <summary>
/// The brief name of the descriptor's target.
/// </summary>
public string Name
{
get { return proto.Name; }
}
/// <value>
/// The file this descriptor was declared in.
/// </value>

View File

@ -257,7 +257,7 @@ namespace Google.Protobuf.Descriptors
/// or unqualified. C++-like name lookup semantics are used to search for the
/// matching descriptor.
/// </summary>
public IDescriptor LookupSymbol(string name, IDescriptor relativeTo)
internal IDescriptor LookupSymbol(string name, IDescriptor relativeTo)
{
// TODO(jonskeet): This could be optimized in a number of ways.

View File

@ -38,14 +38,16 @@ namespace Google.Protobuf.Descriptors
/// <summary>
/// Descriptor for an enum type in a .proto file.
/// </summary>
public sealed class EnumDescriptor : IndexedDescriptorBase<EnumDescriptorProto, EnumOptions>
public sealed class EnumDescriptor : DescriptorBase
{
private readonly EnumDescriptorProto proto;
private readonly MessageDescriptor containingType;
private readonly IList<EnumValueDescriptor> values;
internal EnumDescriptor(EnumDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index)
: base(proto, file, ComputeFullName(file, parent, proto.Name), index)
: base(file, file.ComputeFullName(parent, proto.Name), index)
{
this.proto = proto;
containingType = parent;
if (proto.Value.Count == 0)
@ -61,6 +63,13 @@ namespace Google.Protobuf.Descriptors
File.DescriptorPool.AddSymbol(this);
}
internal EnumDescriptorProto Proto { get { return proto; } }
/// <summary>
/// The brief name of the descriptor's target.
/// </summary>
public override string Name { get { return proto.Name; } }
/// <value>
/// If this is a nested type, get the outer descriptor, otherwise null.
/// </value>
@ -95,14 +104,5 @@ namespace Google.Protobuf.Descriptors
{
return File.DescriptorPool.FindSymbol<EnumValueDescriptor>(FullName + "." + name);
}
internal override void ReplaceProto(EnumDescriptorProto newProto)
{
base.ReplaceProto(newProto);
for (int i = 0; i < values.Count; i++)
{
values[i].ReplaceProto(newProto.Value[i]);
}
}
}
}

View File

@ -37,27 +37,27 @@ namespace Google.Protobuf.Descriptors
/// <summary>
/// Descriptor for a single enum value within an enum in a .proto file.
/// </summary>
public sealed class EnumValueDescriptor : IndexedDescriptorBase<EnumValueDescriptorProto, EnumValueOptions>
public sealed class EnumValueDescriptor : DescriptorBase
{
private readonly EnumDescriptor enumDescriptor;
private readonly EnumValueDescriptorProto proto;
internal EnumValueDescriptor(EnumValueDescriptorProto proto, FileDescriptor file,
EnumDescriptor parent, int index)
: base(proto, file, parent.FullName + "." + proto.Name, index)
: base(file, parent.FullName + "." + proto.Name, index)
{
this.proto = proto;
enumDescriptor = parent;
file.DescriptorPool.AddSymbol(this);
file.DescriptorPool.AddEnumValueByNumber(this);
}
public int Number
{
get { return Proto.Number; }
}
internal EnumValueDescriptorProto Proto { get { return proto; } }
public override string Name { get { return proto.Name; } }
public EnumDescriptor EnumDescriptor
{
get { return enumDescriptor; }
}
public int Number { get { return Proto.Number; } }
public EnumDescriptor EnumDescriptor { get { return enumDescriptor; } }
}
}

View File

@ -38,21 +38,20 @@ namespace Google.Protobuf.Descriptors
/// <summary>
/// Descriptor for a field or extension within a message in a .proto file.
/// </summary>
public sealed class FieldDescriptor : IndexedDescriptorBase<FieldDescriptorProto, FieldOptions>,
IComparable<FieldDescriptor>
public sealed class FieldDescriptor : DescriptorBase, IComparable<FieldDescriptor>
{
private readonly FieldDescriptorProto proto;
private EnumDescriptor enumType;
private MessageDescriptor messageType;
private MessageDescriptor containingType;
private OneofDescriptor containingOneof;
private readonly MessageDescriptor containingType;
private readonly OneofDescriptor containingOneof;
private FieldType fieldType;
private readonly object optionsLock = new object();
internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file,
MessageDescriptor parent, int index)
: base(proto, file, ComputeFullName(file, parent, proto.Name), index)
: base(file, file.ComputeFullName(parent, proto.Name), index)
{
this.proto = proto;
if (proto.Type != 0)
{
fieldType = GetFieldTypeFromProtoType(proto.Type);
@ -64,7 +63,8 @@ namespace Google.Protobuf.Descriptors
"Field numbers must be positive integers.");
}
containingType = parent;
if (proto.OneofIndex != 0)
// OneofIndex "defaults" to -1 due to a hack in FieldDescriptor.OnConstruction.
if (proto.OneofIndex != -1)
{
if (proto.OneofIndex < 0 || proto.OneofIndex >= parent.Proto.OneofDecl.Count)
{
@ -72,12 +72,18 @@ namespace Google.Protobuf.Descriptors
"FieldDescriptorProto.oneof_index is out of range for type " + parent.Name);
}
containingOneof = parent.Oneofs[proto.OneofIndex];
containingOneof.fieldCount ++;
}
file.DescriptorPool.AddSymbol(this);
}
/// <summary>
/// The brief name of the descriptor's target.
/// </summary>
public override string Name { get { return proto.Name; } }
internal FieldDescriptorProto Proto { get { return proto; } }
/// <summary>
/// Maps a field type as included in the .proto file to a FieldType.
/// </summary>
@ -131,9 +137,14 @@ namespace Google.Protobuf.Descriptors
get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_REPEATED; }
}
public bool IsMap
{
get { return fieldType == FieldType.Message && messageType.Proto.Options != null && messageType.Proto.Options.MapEntry; }
}
public bool IsPacked
{
get { return Proto.Options.Packed; }
get { return Proto.Options != null && Proto.Options.Packed; }
}
/// <summary>
@ -273,7 +284,7 @@ namespace Google.Protobuf.Descriptors
File.DescriptorPool.AddFieldByNumber(this);
if (containingType != null && containingType.Options != null && containingType.Options.MessageSetWireFormat)
if (containingType != null && containingType.Proto.Options != null && containingType.Proto.Options.MessageSetWireFormat)
{
throw new DescriptorValidationException(this, "MessageSet format is not supported.");
}

View File

@ -43,7 +43,7 @@ namespace Google.Protobuf.Descriptors
/// IDescriptor is implemented such that the File property returns this descriptor,
/// and the FullName is the same as the Name.
/// </summary>
public sealed class FileDescriptor : IDescriptor<FileDescriptorProto>
public sealed class FileDescriptor : IDescriptor
{
private readonly FileDescriptorProto proto;
private readonly IList<MessageDescriptor> messageTypes;
@ -87,6 +87,22 @@ namespace Google.Protobuf.Descriptors
new ServiceDescriptor(service, this, index));
}
/// <summary>
/// Computes the full name of a descriptor within this file, with an optional parent message.
/// </summary>
internal string ComputeFullName(MessageDescriptor parent, string name)
{
if (parent != null)
{
return parent.FullName + "." + name;
}
if (Package.Length > 0)
{
return Package + "." + name;
}
return name;
}
/// <summary>
/// Extracts public dependencies from direct dependencies. This is a static method despite its
/// first parameter, as the value we're in the middle of constructing is only used for exceptions.
@ -127,19 +143,11 @@ namespace Google.Protobuf.Descriptors
/// <value>
/// The descriptor in its protocol message representation.
/// </value>
public FileDescriptorProto Proto
internal FileDescriptorProto Proto
{
get { return proto; }
}
/// <value>
/// The <see cref="DescriptorProtos.FileOptions" /> defined in <c>descriptor.proto</c>.
/// </value>
public FileOptions Options
{
get { return proto.Options; }
}
/// <value>
/// The file name.
/// </value>
@ -213,14 +221,6 @@ namespace Google.Protobuf.Descriptors
get { return this; }
}
/// <value>
/// Protocol buffer describing this descriptor.
/// </value>
IMessage IDescriptor.Proto
{
get { return Proto; }
}
/// <value>
/// Pool containing symbol descriptors.
/// </value>
@ -255,22 +255,7 @@ namespace Google.Protobuf.Descriptors
}
return null;
}
/// <summary>
/// Builds a FileDescriptor from its protocol buffer representation.
/// </summary>
/// <param name="proto">The protocol message form of the FileDescriptor.</param>
/// <param name="dependencies">FileDescriptors corresponding to all of the
/// file's dependencies, in the exact order listed in the .proto file. May be null,
/// in which case it is treated as an empty array.</param>
/// <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>
public static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies)
{
return BuildFrom(proto, dependencies, false);
}
/// <summary>
/// Builds a FileDescriptor from its protocol buffer representation.
/// </summary>
@ -336,33 +321,8 @@ namespace Google.Protobuf.Descriptors
}
}
/// <summary>
/// This method is to be called by generated code only. It is equivalent
/// to BuildFrom except that the FileDescriptorProto is encoded in
/// protocol buffer wire format. This overload is maintained for backward
/// compatibility with source code generated before the custom options were available
/// (and working).
/// </summary>
public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData, FileDescriptor[] dependencies)
{
return InternalBuildGeneratedFileFrom(descriptorData, dependencies, x => { });
}
/// <summary>
/// This delegate should be used by generated code only. When calling
/// FileDescriptor.InternalBuildGeneratedFileFrom, the caller can provide
/// a callback which assigns the global variables defined in the generated code
/// which point at parts of the FileDescriptor. The callback returns an
/// Extension Registry which contains any extensions which might be used in
/// the descriptor - that is, extensions of the various "Options" messages defined
/// in descriptor.proto. The callback may also return null to indicate that
/// no extensions are used in the descriptor.
/// </summary>
public delegate void InternalDescriptorAssigner(FileDescriptor descriptor);
public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData,
FileDescriptor[] dependencies,
InternalDescriptorAssigner descriptorAssigner)
FileDescriptor[] dependencies)
{
FileDescriptorProto proto;
try
@ -374,20 +334,16 @@ namespace Google.Protobuf.Descriptors
throw new ArgumentException("Failed to parse protocol buffer descriptor for generated code.", e);
}
FileDescriptor result;
try
{
// When building descriptors for generated code, we allow unknown
// dependencies by default.
result = BuildFrom(proto, dependencies, true);
return BuildFrom(proto, dependencies, true);
}
catch (DescriptorValidationException e)
{
throw new ArgumentException("Invalid embedded descriptor for \"" + proto.Name + "\".", e);
}
descriptorAssigner(result);
return result;
}
public override string ToString()

View File

@ -33,23 +33,12 @@
namespace Google.Protobuf.Descriptors
{
/// <summary>
/// The non-generic form of the IDescriptor interface. Useful for describing a general
/// descriptor.
/// Interface implemented by all descriptor types.
/// </summary>
public interface IDescriptor
{
string Name { get; }
string FullName { get; }
FileDescriptor File { get; }
IMessage Proto { get; }
}
/// <summary>
/// Strongly-typed form of the IDescriptor interface.
/// </summary>
/// <typeparam name="TProto">Protocol buffer type underlying this descriptor type</typeparam>
internal interface IDescriptor<TProto> : IDescriptor where TProto : IMessage
{
new TProto Proto { get; }
}
}
}

View File

@ -1,65 +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 Google.Protobuf.DescriptorProtos;
namespace Google.Protobuf.Descriptors
{
/// <summary>
/// Base class for descriptors which are also indexed. This is all of them other than
/// <see cref="FileDescriptor" />.
/// </summary>
public abstract class IndexedDescriptorBase<TProto, TOptions> : DescriptorBase<TProto, TOptions>
where TProto : IMessage<TProto>, IDescriptorProto<TOptions>
{
private readonly int index;
protected IndexedDescriptorBase(TProto proto, FileDescriptor file, string fullName, int index)
: base(proto, file, fullName)
{
this.index = index;
}
/// <value>
/// The index of this descriptor within its parent descriptor.
/// </value>
/// <remarks>
/// This returns the index of this descriptor within its parent, for
/// this descriptor's type. (There can be duplicate values for different
/// types, e.g. one enum type with index 0 and one message type with index 0.)
/// </remarks>
public int Index
{
get { return index; }
}
}
}

View File

@ -39,8 +39,9 @@ namespace Google.Protobuf.Descriptors
/// <summary>
/// Describes a message type.
/// </summary>
public sealed class MessageDescriptor : IndexedDescriptorBase<DescriptorProto, MessageOptions>
public sealed class MessageDescriptor : DescriptorBase
{
private readonly DescriptorProto proto;
private readonly MessageDescriptor containingType;
private readonly IList<MessageDescriptor> nestedTypes;
private readonly IList<EnumDescriptor> enumTypes;
@ -48,8 +49,9 @@ namespace Google.Protobuf.Descriptors
private readonly IList<OneofDescriptor> oneofs;
internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex)
: base(proto, file, ComputeFullName(file, parent, proto.Name), typeIndex)
: base(file, file.ComputeFullName(parent, proto.Name), typeIndex)
{
this.proto = proto;
containingType = parent;
oneofs = DescriptorUtil.ConvertAndMakeReadOnly(proto.OneofDecl,
@ -68,23 +70,16 @@ namespace Google.Protobuf.Descriptors
fields = DescriptorUtil.ConvertAndMakeReadOnly(proto.Field,
(field, index) =>
new FieldDescriptor(field, file, this, index));
for (int i = 0; i < proto.OneofDecl.Count; i++)
{
oneofs[i].fields = new FieldDescriptor[oneofs[i].FieldCount];
oneofs[i].fieldCount = 0;
}
for (int i = 0; i< proto.Field.Count; i++)
{
OneofDescriptor oneofDescriptor = fields[i].ContainingOneof;
if (oneofDescriptor != null)
{
oneofDescriptor.fields[oneofDescriptor.fieldCount++] = fields[i];
}
}
file.DescriptorPool.AddSymbol(this);
}
/// <summary>
/// The brief name of the descriptor's target.
/// </summary>
public override string Name { get { return proto.Name; } }
internal DescriptorProto Proto { get { return proto; } }
/// <value>
/// If this is a nested type, get the outer descriptor, otherwise null.
/// </value>
@ -144,7 +139,7 @@ namespace Google.Protobuf.Descriptors
/// <summary>
/// Finds a nested descriptor by name. The is valid for fields, nested
/// message types and enums.
/// message types, oneofs and enums.
/// </summary>
/// <param name="name">The unqualified name of the descriptor, e.g. "Foo"</param>
/// <returns>The descriptor, or null if not found.</returns>
@ -171,32 +166,8 @@ namespace Google.Protobuf.Descriptors
foreach (OneofDescriptor oneof in oneofs)
{
// TODO(jonskeet): Do we need to do this?
// oneof.C
oneof.CrossLink();
}
}
/// <summary>
/// See FileDescriptor.ReplaceProto
/// </summary>
internal override void ReplaceProto(DescriptorProto newProto)
{
base.ReplaceProto(newProto);
for (int i = 0; i < nestedTypes.Count; i++)
{
nestedTypes[i].ReplaceProto(newProto.NestedType[i]);
}
for (int i = 0; i < enumTypes.Count; i++)
{
enumTypes[i].ReplaceProto(newProto.EnumType[i]);
}
for (int i = 0; i < fields.Count; i++)
{
fields[i].ReplaceProto(newProto.Field[i]);
}
}
}
}
}

View File

@ -37,8 +37,9 @@ namespace Google.Protobuf.Descriptors
/// <summary>
/// Describes a single method in a service.
/// </summary>
public sealed class MethodDescriptor : IndexedDescriptorBase<MethodDescriptorProto, MethodOptions>
public sealed class MethodDescriptor : DescriptorBase
{
private readonly MethodDescriptorProto proto;
private readonly ServiceDescriptor service;
private MessageDescriptor inputType;
private MessageDescriptor outputType;
@ -46,35 +47,34 @@ namespace Google.Protobuf.Descriptors
/// <value>
/// The service this method belongs to.
/// </value>
public ServiceDescriptor Service
{
get { return service; }
}
public ServiceDescriptor Service { get { return service; } }
/// <value>
/// The method's input type.
/// </value>
public MessageDescriptor InputType
{
get { return inputType; }
}
public MessageDescriptor InputType { get { return inputType; } }
/// <value>
/// The method's input type.
/// </value>
public MessageDescriptor OutputType
{
get { return outputType; }
}
public MessageDescriptor OutputType { get { return outputType; } }
internal MethodDescriptor(MethodDescriptorProto proto, FileDescriptor file,
ServiceDescriptor parent, int index)
: base(proto, file, parent.FullName + "." + proto.Name, index)
: base(file, parent.FullName + "." + proto.Name, index)
{
this.proto = proto;
service = parent;
file.DescriptorPool.AddSymbol(this);
}
internal MethodDescriptorProto Proto { get { return proto; } }
/// <summary>
/// The brief name of the descriptor's target.
/// </summary>
public override string Name { get { return proto.Name; } }
internal void CrossLink()
{
IDescriptor lookup = File.DescriptorPool.LookupSymbol(Proto.InputType, this);

View File

@ -31,48 +31,49 @@
#endregion
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Google.Protobuf.DescriptorProtos;
namespace Google.Protobuf.Descriptors
{
public sealed class OneofDescriptor
public sealed class OneofDescriptor : DescriptorBase
{
private int index;
private OneofDescriptorProto proto;
private FileDescriptor file;
private readonly OneofDescriptorProto proto;
private MessageDescriptor containingType;
internal int fieldCount;
internal IList<FieldDescriptor> fields;
private IList<FieldDescriptor> fields;
internal OneofDescriptor(OneofDescriptorProto proto, FileDescriptor file,
MessageDescriptor parent, int index)
internal OneofDescriptor(OneofDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index)
: base(file, file.ComputeFullName(parent, proto.Name), index)
{
this.proto = proto;
this.file = file;
this.index = index;
containingType = parent;
fieldCount = 0;
file.DescriptorPool.AddSymbol(this);
}
public int Index
{
get { return index; }
}
/// <summary>
/// The brief name of the descriptor's target.
/// </summary>
public override string Name { get { return proto.Name; } }
public MessageDescriptor ContainingType
{
get { return containingType; }
}
public int FieldCount
{
get { return fieldCount; }
}
public IList<FieldDescriptor> Fields { get { return fields; } }
public FieldDescriptor Field(int index)
internal void CrossLink()
{
return fields[index];
List<FieldDescriptor> fieldCollection = new List<FieldDescriptor>();
foreach (var field in ContainingType.Fields)
{
if (field.ContainingOneof == this)
{
fieldCollection.Add(field);
}
}
fields = new ReadOnlyCollection<FieldDescriptor>(fieldCollection);
}
}
}

View File

@ -37,7 +37,7 @@ namespace Google.Protobuf.Descriptors
/// just as placeholders so that someone cannot define, say, a message type
/// that has the same name as an existing package.
/// </summary>
internal sealed class PackageDescriptor : IDescriptor<IMessage>
internal sealed class PackageDescriptor : IDescriptor
{
private readonly string name;
private readonly string fullName;
@ -50,11 +50,6 @@ namespace Google.Protobuf.Descriptors
this.name = name;
}
public IMessage Proto
{
get { return file.Proto; }
}
public string Name
{
get { return name; }

View File

@ -39,19 +39,28 @@ namespace Google.Protobuf.Descriptors
/// <summary>
/// Describes a service type.
/// </summary>
public sealed class ServiceDescriptor : IndexedDescriptorBase<ServiceDescriptorProto, ServiceOptions>
public sealed class ServiceDescriptor : DescriptorBase
{
private readonly ServiceDescriptorProto proto;
private readonly IList<MethodDescriptor> methods;
public ServiceDescriptor(ServiceDescriptorProto proto, FileDescriptor file, int index)
: base(proto, file, ComputeFullName(file, null, proto.Name), index)
internal ServiceDescriptor(ServiceDescriptorProto proto, FileDescriptor file, int index)
: base(file, file.ComputeFullName(null, proto.Name), index)
{
this.proto = proto;
methods = DescriptorUtil.ConvertAndMakeReadOnly(proto.Method,
(method, i) => new MethodDescriptor(method, file, this, i));
file.DescriptorPool.AddSymbol(this);
}
/// <summary>
/// The brief name of the descriptor's target.
/// </summary>
public override string Name { get { return proto.Name; } }
internal ServiceDescriptorProto Proto { get { return proto; } }
/// <value>
/// An unmodifiable list of methods in this service.
/// </value>
@ -77,14 +86,5 @@ namespace Google.Protobuf.Descriptors
method.CrossLink();
}
}
internal override void ReplaceProto(ServiceDescriptorProto newProto)
{
base.ReplaceProto(newProto);
for (int i = 0; i < methods.Count; i++)
{
methods[i].ReplaceProto(newProto.Method[i]);
}
}
}
}

View File

@ -32,34 +32,37 @@
using System;
using System.Reflection;
using Google.Protobuf.Descriptors;
namespace Google.Protobuf.FieldAccess
{
/// <summary>
/// Base class for field accessors.
/// </summary>
/// <typeparam name="T">Type of message containing the field</typeparam>
internal abstract class FieldAccessorBase<T> : IFieldAccessor<T> where T : IMessage<T>
internal abstract class FieldAccessorBase : IFieldAccessor
{
private readonly Func<T, object> getValueDelegate;
private readonly Func<object, object> getValueDelegate;
private readonly FieldDescriptor descriptor;
internal FieldAccessorBase(string name)
internal FieldAccessorBase(Type type, string propertyName, FieldDescriptor descriptor)
{
PropertyInfo property = typeof(T).GetProperty(name);
PropertyInfo property = type.GetProperty(propertyName);
if (property == null || !property.CanRead)
{
throw new ArgumentException("Not all required properties/methods available");
}
getValueDelegate = ReflectionUtil.CreateUpcastDelegate<T>(property.GetGetMethod());
this.descriptor = descriptor;
getValueDelegate = ReflectionUtil.CreateFuncObjectObject(property.GetGetMethod());
}
public object GetValue(T message)
public FieldDescriptor Descriptor { get { return descriptor; } }
public object GetValue(object message)
{
return getValueDelegate(message);
}
public abstract bool HasValue(T message);
public abstract void Clear(T message);
public abstract void SetValue(T message, object value);
public abstract void Clear(object message);
public abstract void SetValue(object message, object value);
}
}

View File

@ -31,6 +31,7 @@
#endregion
using System;
using System.Collections.ObjectModel;
using Google.Protobuf.Descriptors;
namespace Google.Protobuf.FieldAccess
@ -38,34 +39,43 @@ namespace Google.Protobuf.FieldAccess
/// <summary>
/// Provides access to fields in generated messages via reflection.
/// </summary>
public sealed class FieldAccessorTable<T> where T : IMessage<T>
public sealed class FieldAccessorTable
{
private readonly IFieldAccessor<T>[] accessors;
private readonly ReadOnlyCollection<IFieldAccessor> accessors;
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(MessageDescriptor descriptor, string[] propertyNames)
public FieldAccessorTable(Type type, MessageDescriptor descriptor, string[] propertyNames)
{
this.descriptor = descriptor;
accessors = new IFieldAccessor<T>[descriptor.Fields.Count];
bool supportFieldPresence = descriptor.File.Syntax == FileDescriptor.ProtoSyntax.Proto2;
for (int i = 0; i < accessors.Length; i++)
var accessorsArray = new IFieldAccessor[descriptor.Fields.Count];
for (int i = 0; i < accessorsArray.Length; i++)
{
var field = descriptor.Fields[i];
var name = propertyNames[i];
accessors[i] = field.IsRepeated
? (IFieldAccessor<T>) new RepeatedFieldAccessor<T>(propertyNames[i])
: new SingleFieldAccessor<T>(field, name, supportFieldPresence);
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);
// TODO(jonskeet): Oneof support
}
internal IFieldAccessor<T> this[int fieldNumber]
// 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 IFieldAccessor this[int fieldNumber]
{
get
{
@ -74,7 +84,7 @@ namespace Google.Protobuf.FieldAccess
}
}
internal IFieldAccessor<T> this[FieldDescriptor field]
internal IFieldAccessor this[FieldDescriptor field]
{
get
{

View File

@ -30,39 +30,41 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using Google.Protobuf.Descriptors;
namespace Google.Protobuf.FieldAccess
{
/// <summary>
/// Allows fields to be reflectively accessed in a smart manner.
/// The property descriptors for each field are created once and then cached.
/// In addition, this interface holds knowledge of repeated fields, builders etc.
/// Allows fields to be reflectively accessed.
/// </summary>
internal interface IFieldAccessor<T> where T : IMessage<T>
public interface IFieldAccessor
{
/// <summary>
/// Indicates whether the specified message contains the field. For primitive fields
/// declared in proto3-syntax messages, this simply checks whether the value is the default one.
/// Returns the descriptor associated with this field.
/// </summary>
/// <exception cref="InvalidOperationException">The field is a repeated field, or a single primitive field.</exception>
bool HasValue(T message);
FieldDescriptor Descriptor { get; }
/// <summary>
/// Clears the field in the specified message. (For repeated fields,
/// this clears the list.)
/// </summary>
void Clear(T message);
void Clear(object message);
/// <summary>
/// Fetches the field value. For repeated values, this will be an
/// <see cref="IList"/> implementation.
/// <see cref="IList"/> implementation. For map values, this will be an
/// <see cref="IDictionary"/> implementation.
/// </summary>
object GetValue(T message);
object GetValue(object message);
/// <summary>
/// Mutator for single fields only. (Repeated fields must be mutated
/// by fetching the list, then mutating that.)
/// Mutator for single "simple" fields only.
/// </summary>
/// <exception cref="InvalidOperationException">The field is a repeated field.</exception>
void SetValue(T message, object value);
/// <remarks>
/// Repeated fields are mutated by fetching the value and manipulating it as a list.
/// Map fields are mutated by fetching the value and manipulating it as a dictionary.
/// </remarks>
/// <exception cref="InvalidOperationException">The field is not a "simple" field, or the message is frozen.</exception>
void SetValue(object message, object value);
}
}

View File

@ -1,53 +1,59 @@
#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
namespace Google.Protobuf.DescriptorProtos
{
/// <summary>
/// Interface implemented by all DescriptorProtos. The generator doesn't
/// emit the interface implementation claim, so PartialClasses.cs contains
/// partial class declarations for each of them.
/// </summary>
/// <typeparam name="TOptions">The associated options protocol buffer type</typeparam>
public interface IDescriptorProto<TOptions>
{
/// <summary>
/// The brief name of the descriptor's target.
/// </summary>
string Name { get; }
/// <summary>
/// The options for this descriptor.
/// </summary>
TOptions Options { get; }
}
}
#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;
using System.Collections;
using Google.Protobuf.Descriptors;
namespace Google.Protobuf.FieldAccess
{
/// <summary>
/// Accessor for map fields.
/// </summary>
internal sealed class MapFieldAccessor : FieldAccessorBase
{
internal MapFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor)
{
}
public override void Clear(object message)
{
IDictionary list = (IDictionary) GetValue(message);
list.Clear();
}
public override void SetValue(object message, object value)
{
throw new InvalidOperationException("SetValue is not implemented for map fields");
}
}
}

View File

@ -31,6 +31,7 @@
#endregion
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Google.Protobuf.FieldAccess
@ -51,101 +52,42 @@ namespace Google.Protobuf.FieldAccess
internal static readonly Type[] EmptyTypes = new Type[0];
/// <summary>
/// Creates a delegate which will execute the given method and then return
/// the result as an object.
/// Creates a delegate which will cast the argument to the appropriate method target type,
/// call the method on it, then convert the result to object.
/// </summary>
public static Func<T, object> CreateUpcastDelegate<T>(MethodInfo method)
internal static Func<object, object> CreateFuncObjectObject(MethodInfo method)
{
// The tricky bit is invoking CreateCreateUpcastDelegateImpl with the right type parameters
MethodInfo openImpl = typeof(ReflectionUtil).GetMethod("CreateUpcastDelegateImpl");
MethodInfo closedImpl = openImpl.MakeGenericMethod(typeof(T), method.ReturnType);
return (Func<T, object>) closedImpl.Invoke(null, new object[] {method});
ParameterExpression parameter = Expression.Parameter(typeof(object), "p");
Expression downcast = Expression.Convert(parameter, method.DeclaringType);
Expression call = Expression.Call(downcast, method);
Expression upcast = Expression.Convert(call, typeof(object));
return Expression.Lambda<Func<object, object>>(upcast, parameter).Compile();
}
/// <summary>
/// Creates a delegate which will execute the given method after casting the first argument to
/// the target type of the method, and the second argument to the first parameter type of the method.
/// </summary>
internal static Action<object, object> CreateActionObjectObject(MethodInfo method)
{
ParameterExpression targetParameter = Expression.Parameter(typeof(object), "target");
ParameterExpression argParameter = Expression.Parameter(typeof(object), "arg");
Expression castTarget = Expression.Convert(targetParameter, method.DeclaringType);
Expression castArgument = Expression.Convert(argParameter, method.GetParameters()[0].ParameterType);
Expression call = Expression.Call(castTarget, method, castArgument);
return Expression.Lambda<Action<object, object>>(call, targetParameter, argParameter).Compile();
}
/// <summary>
/// Method used solely for implementing CreateUpcastDelegate. Public to avoid trust issues
/// in low-trust scenarios.
/// Creates a delegate which will execute the given method after casting the first argument to
/// the target type of the method.
/// </summary>
public static Func<TSource, object> CreateUpcastDelegateImpl<TSource, TResult>(MethodInfo method)
internal static Action<object> CreateActionObject(MethodInfo method)
{
// Convert the reflection call into an open delegate, i.e. instead of calling x.Method()
// we'll call getter(x).
Func<TSource, TResult> getter = ReflectionUtil.CreateDelegateFunc<TSource, TResult>(method);
// Implicit upcast to object (within the delegate)
return source => getter(source);
}
/// <summary>
/// Creates a delegate which will execute the given method after casting the parameter
/// down from object to the required parameter type.
/// </summary>
public static Action<T, object> CreateDowncastDelegate<T>(MethodInfo method)
{
MethodInfo openImpl = typeof(ReflectionUtil).GetMethod("CreateDowncastDelegateImpl");
MethodInfo closedImpl = openImpl.MakeGenericMethod(typeof(T), method.GetParameters()[0].ParameterType);
return (Action<T, object>) closedImpl.Invoke(null, new object[] {method});
}
public static Action<TSource, object> CreateDowncastDelegateImpl<TSource, TParam>(MethodInfo method)
{
// Convert the reflection call into an open delegate, i.e. instead of calling x.Method(y) we'll
// call Method(x, y)
Action<TSource, TParam> call = ReflectionUtil.CreateDelegateAction<TSource, TParam>(method);
return (source, parameter) => call(source, (TParam) parameter);
}
/// <summary>
/// Creates a delegate which will execute the given method after casting the parameter
/// down from object to the required parameter type.
/// </summary>
public static Action<T, object> CreateDowncastDelegateIgnoringReturn<T>(MethodInfo method)
{
MethodInfo openImpl = typeof(ReflectionUtil).GetMethod("CreateDowncastDelegateIgnoringReturnImpl");
MethodInfo closedImpl = openImpl.MakeGenericMethod(typeof(T), method.GetParameters()[0].ParameterType,
method.ReturnType);
return (Action<T, object>) closedImpl.Invoke(null, new object[] {method});
}
public static Action<TSource, object> CreateDowncastDelegateIgnoringReturnImpl<TSource, TParam, TReturn>(
MethodInfo method)
{
// Convert the reflection call into an open delegate, i.e. instead of calling x.Method(y) we'll
// call Method(x, y)
Func<TSource, TParam, TReturn> call = ReflectionUtil.CreateDelegateFunc<TSource, TParam, TReturn>(method);
return delegate(TSource source, object parameter) { call(source, (TParam) parameter); };
}
internal static Func<TResult> CreateDelegateFunc<TResult>(MethodInfo method)
{
object tdelegate = Delegate.CreateDelegate(typeof(Func<TResult>), null, method);
return (Func<TResult>)tdelegate;
}
internal static Func<T, TResult> CreateDelegateFunc<T, TResult>(MethodInfo method)
{
object tdelegate = Delegate.CreateDelegate(typeof(Func<T, TResult>), null, method);
return (Func<T, TResult>)tdelegate;
}
internal static Func<T1, T2, TResult> CreateDelegateFunc<T1, T2, TResult>(MethodInfo method)
{
object tdelegate = Delegate.CreateDelegate(typeof(Func<T1, T2, TResult>), null, method);
return (Func<T1, T2, TResult>)tdelegate;
}
internal static Action<T> CreateDelegateAction<T>(MethodInfo method)
{
object tdelegate = Delegate.CreateDelegate(typeof(Action<T>), null, method);
return (Action<T>)tdelegate;
}
internal static Action<T1, T2> CreateDelegateAction<T1, T2>(MethodInfo method)
{
object tdelegate = Delegate.CreateDelegate(typeof(Action<T1, T2>), null, method);
return (Action<T1, T2>)tdelegate;
ParameterExpression targetParameter = Expression.Parameter(typeof(object), "target");
Expression castTarget = Expression.Convert(targetParameter, method.DeclaringType);
Expression call = Expression.Call(castTarget, method);
return Expression.Lambda<Action<object>>(call, targetParameter).Compile();
}
}
}

View File

@ -32,33 +32,28 @@
using System;
using System.Collections;
using Google.Protobuf.Descriptors;
namespace Google.Protobuf.FieldAccess
{
/// <summary>
/// Accessor for repeated fields.
/// </summary>
/// <typeparam name="T">The type of message containing the field.</typeparam>
internal sealed class RepeatedFieldAccessor<T> : FieldAccessorBase<T> where T : IMessage<T>
internal sealed class RepeatedFieldAccessor : FieldAccessorBase
{
internal RepeatedFieldAccessor(string name) : base(name)
internal RepeatedFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor)
{
}
public override void Clear(T message)
public override void Clear(object message)
{
IList list = (IList) GetValue(message);
list.Clear();
}
public override bool HasValue(T message)
public override void SetValue(object message, object value)
{
throw new NotImplementedException("HasValue is not implemented for repeated fields");
}
public override void SetValue(T message, object value)
{
throw new NotImplementedException("SetValue is not implemented for repeated fields");
throw new InvalidOperationException("SetValue is not implemented for repeated fields");
}
}

View File

@ -39,76 +39,46 @@ namespace Google.Protobuf.FieldAccess
/// <summary>
/// Accessor for single fields.
/// </summary>
/// <typeparam name="T">The type of message containing the field.</typeparam>
internal sealed class SingleFieldAccessor<T> : FieldAccessorBase<T> where T : IMessage<T>
internal sealed class SingleFieldAccessor : FieldAccessorBase
{
// All the work here is actually done in the constructor - it creates the appropriate delegates.
// There are various cases to consider, based on the property type (message, string/bytes, or "genuine" primitive)
// and proto2 vs proto3 for non-message types, as proto3 doesn't support "full" presence detection or default
// values.
private readonly Action<T, object> setValueDelegate;
private readonly Action<T> clearDelegate;
private readonly Func<T, bool> hasValueDelegate;
private readonly Action<object, object> setValueDelegate;
private readonly Action<object> clearDelegate;
internal SingleFieldAccessor(FieldDescriptor descriptor, string name, bool supportsFieldPresence) : base(name)
internal SingleFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor)
{
PropertyInfo property = typeof(T).GetProperty(name);
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");
}
setValueDelegate = ReflectionUtil.CreateDowncastDelegate<T>(property.GetSetMethod());
setValueDelegate = ReflectionUtil.CreateActionObjectObject(property.GetSetMethod());
var clrType = property.PropertyType;
// TODO: What should clear on a oneof member do? Clear the oneof?
if (typeof(IMessage).IsAssignableFrom(clrType))
{
// Message types are simple - we only need to detect nullity.
clearDelegate = message => SetValue(message, null);
hasValueDelegate = message => GetValue(message) == null;
}
if (supportsFieldPresence)
{
// Proto2: we expect a HasFoo property and a ClearFoo method.
// For strings and byte arrays, setting the property to null would have the equivalent effect,
// but we generate the method for consistency, which makes this simpler.
PropertyInfo hasProperty = typeof(T).GetProperty("Has" + name);
MethodInfo clearMethod = typeof(T).GetMethod("Clear" + name);
if (hasProperty == null || clearMethod == null || !hasProperty.CanRead)
{
throw new ArgumentException("Not all required properties/methods available");
}
hasValueDelegate = ReflectionUtil.CreateDelegateFunc<T, bool>(hasProperty.GetGetMethod());
clearDelegate = ReflectionUtil.CreateDelegateAction<T>(clearMethod);
}
else
{
/*
// TODO(jonskeet): Reimplement. We need a better way of working out default values.
// Proto3: for field detection, we just need the default value of the field (0, "", byte[0] etc)
// To clear a field, we set the value to that default.
object defaultValue = descriptor.DefaultValue;
hasValueDelegate = message => GetValue(message).Equals(defaultValue);
clearDelegate = message => SetValue(message, defaultValue);
*/
}
// TODO: Validate that this is a reasonable single field? (Should be a value type, a message type, or string/ByteString.)
object defaultValue =
typeof(IMessage).IsAssignableFrom(clrType) ? null
: clrType == typeof(string) ? ""
: clrType == typeof(ByteString) ? ByteString.Empty
: Activator.CreateInstance(clrType);
clearDelegate = message => SetValue(message, defaultValue);
}
public override bool HasValue(T message)
{
return hasValueDelegate(message);
}
public override void Clear(T message)
public override void Clear(object message)
{
clearDelegate(message);
}
public override void SetValue(T message, object value)
public override void SetValue(object message, object value)
{
setValueDelegate(message, value);
}

View File

@ -68,12 +68,12 @@ namespace Google.Protobuf
public static FieldCodec<uint> ForFixed32(uint tag)
{
return new FieldCodec<uint>(input => input.ReadFixed32(), (output, value) => output.WriteFixed32(value), CodedOutputStream.ComputeFixed32Size, tag);
return new FieldCodec<uint>(input => input.ReadFixed32(), (output, value) => output.WriteFixed32(value), 4, tag);
}
public static FieldCodec<int> ForSFixed32(uint tag)
{
return new FieldCodec<int>(input => input.ReadSFixed32(), (output, value) => output.WriteSFixed32(value), CodedOutputStream.ComputeSFixed32Size, tag);
return new FieldCodec<int>(input => input.ReadSFixed32(), (output, value) => output.WriteSFixed32(value), 4, tag);
}
public static FieldCodec<uint> ForUInt32(uint tag)
@ -93,12 +93,12 @@ namespace Google.Protobuf
public static FieldCodec<ulong> ForFixed64(uint tag)
{
return new FieldCodec<ulong>(input => input.ReadFixed64(), (output, value) => output.WriteFixed64(value), CodedOutputStream.ComputeFixed64Size, tag);
return new FieldCodec<ulong>(input => input.ReadFixed64(), (output, value) => output.WriteFixed64(value), 8, tag);
}
public static FieldCodec<long> ForSFixed64(uint tag)
{
return new FieldCodec<long>(input => input.ReadSFixed64(), (output, value) => output.WriteSFixed64(value), CodedOutputStream.ComputeSFixed64Size, tag);
return new FieldCodec<long>(input => input.ReadSFixed64(), (output, value) => output.WriteSFixed64(value), 8, tag);
}
public static FieldCodec<ulong> ForUInt64(uint tag)

View File

@ -40,12 +40,11 @@ namespace Google.Protobuf
// TODO(jonskeet): Split these interfaces into separate files when we're happy with them.
/// <summary>
/// Reflection support for a specific message type. message
/// Reflection support for a specific message type.
/// </summary>
/// <typeparam name="T">The message type being reflected.</typeparam>
public interface IReflectedMessage<T> where T : IMessage<T>
public interface IReflectedMessage
{
FieldAccessorTable<T> Fields { get; }
FieldAccessorTable Fields { get; }
// TODO(jonskeet): Descriptor? Or a single property which has "all you need for reflection"?
}

View File

@ -61,7 +61,6 @@
<Compile Include="Collections\ReadOnlyDictionary.cs" />
<Compile Include="Collections\RepeatedField.cs" />
<Compile Include="DescriptorProtos\DescriptorProtoFile.cs" />
<Compile Include="DescriptorProtos\IDescriptorProto.cs" />
<Compile Include="DescriptorProtos\PartialClasses.cs" />
<Compile Include="Descriptors\DescriptorBase.cs" />
<Compile Include="Descriptors\DescriptorPool.cs" />
@ -74,11 +73,11 @@
<Compile Include="Descriptors\FileDescriptor.cs" />
<Compile Include="Descriptors\OneofDescriptor.cs" />
<Compile Include="Descriptors\IDescriptor.cs" />
<Compile Include="Descriptors\IndexedDescriptorBase.cs" />
<Compile Include="Descriptors\MessageDescriptor.cs" />
<Compile Include="Descriptors\MethodDescriptor.cs" />
<Compile Include="Descriptors\PackageDescriptor.cs" />
<Compile Include="Descriptors\ServiceDescriptor.cs" />
<Compile Include="FieldAccess\MapFieldAccessor.cs" />
<Compile Include="FieldCodec.cs" />
<Compile Include="FrameworkPortability.cs" />
<Compile Include="Freezable.cs" />

View File

@ -1,6 +1,6 @@
// See README.txt for information and build instructions.
syntax = "proto2";
syntax = "proto3";
package tutorial;
@ -9,9 +9,9 @@ option java_outer_classname = "AddressBookProtos";
option csharp_namespace = "Google.Protobuf.Examples.AddressBook";
message Person {
required string name = 1;
required int32 id = 2; // Unique ID number for this person.
optional string email = 3;
string name = 1;
int32 id = 2; // Unique ID number for this person.
string email = 3;
enum PhoneType {
MOBILE = 0;
@ -20,8 +20,8 @@ message Person {
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phone = 4;

View File

@ -65,6 +65,7 @@ void FieldGeneratorBase::SetCommonFieldVariables(
tag_bytes += ", " + SimpleItoa(tag_array[i]);
}
(*variables)["access_level"] = class_access_level();
(*variables)["tag"] = SimpleItoa(tag);
(*variables)["tag_size"] = SimpleItoa(tag_size);
(*variables)["tag_bytes"] = tag_bytes;

View File

@ -68,6 +68,12 @@ bool Generator::Generate(
vector<pair<string, string> > options;
ParseGeneratorParameter(parameter, &options);
// We only support proto3 - but we make an exception for descriptor.proto.
if (file->syntax() != FileDescriptor::SYNTAX_PROTO3 && !IsDescriptorProto(file)) {
*error = "C# code generation only supports proto3 syntax";
return false;
}
std::string file_extension = ".cs";
for (int i = 0; i < options.size(); i++) {
if (options[i].first == "file_extension") {

View File

@ -101,6 +101,15 @@ uint FixedMakeTag(const FieldDescriptor* descriptor);
FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
// Determines whether we're generating code for the proto representation of descriptors etc,
// for use in the runtime. This is the only type which is allowed to use proto2 syntax,
// and it generates internal classes.
inline bool IsDescriptorProto(const FileDescriptor* descriptor) {
// TODO: Do this better! (Currently this depends on a hack in generate_protos.sh to rename
// the file...)
return descriptor->name() == "google/protobuf/descriptor_proto_file.proto";
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf

View File

@ -78,7 +78,7 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer);
printer->Print(
variables_,
"public pbc::MapField<$key_type_name$, $value_type_name$> $property_name$ {\n"
"$access_level$ pbc::MapField<$key_type_name$, $value_type_name$> $property_name$ {\n"
" get { return $name$_; }\n"
"}\n");
}

View File

@ -116,8 +116,7 @@ void MessageGenerator::GenerateStaticVariables(io::Printer* printer) {
// The descriptor for this type.
printer->Print(
"internal static pbd::MessageDescriptor internal__$identifier$__Descriptor;\n"
"internal static pb::FieldAccess.FieldAccessorTable<$full_class_name$> internal__$identifier$__FieldAccessorTable;\n",
"internal static pb::FieldAccess.FieldAccessorTable internal__$identifier$__FieldAccessorTable;\n",
"identifier", GetUniqueFileScopeIdentifier(descriptor_),
"full_class_name", full_class_name());
@ -130,24 +129,23 @@ void MessageGenerator::GenerateStaticVariables(io::Printer* printer) {
void MessageGenerator::GenerateStaticVariableInitializers(io::Printer* printer) {
map<string, string> vars;
vars["identifier"] = GetUniqueFileScopeIdentifier(descriptor_);
vars["index"] = SimpleItoa(descriptor_->index());
vars["full_class_name"] = full_class_name();
if (descriptor_->containing_type() != NULL) {
vars["parent"] = GetUniqueFileScopeIdentifier(
descriptor_->containing_type());
}
printer->Print(vars, "internal__$identifier$__Descriptor = ");
if (!descriptor_->containing_type()) {
printer->Print(vars, "Descriptor.MessageTypes[$index$];\n");
} else {
printer->Print(vars, "internal__$parent$__Descriptor.NestedTypes[$index$];\n");
// 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 pb::FieldAccess.FieldAccessorTable<$full_class_name$>(internal__$identifier$__Descriptor,\n");
" new pb::FieldAccess.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$\", ",
@ -201,23 +199,35 @@ void MessageGenerator::Generate(io::Printer* printer) {
"private static readonly uint[] _fieldTags = new uint[] { $tags$ };\n",
"tags", JoinStrings(tags, ", "));
// Access the message descriptor via the relevant file descriptor or containing message descriptor.
if (!descriptor_->containing_type()) {
vars["descriptor_accessor"] = GetFullUmbrellaClassName(descriptor_->file())
+ ".Descriptor.MessageTypes[" + SimpleItoa(descriptor_->index()) + "]";
} else {
vars["descriptor_accessor"] = GetClassName(descriptor_->containing_type())
+ ".Descriptor.NestedTypes[" + SimpleItoa(descriptor_->index()) + "]";
}
printer->Print(
vars,
"public static pbd::MessageDescriptor Descriptor {\n"
" get { return $umbrella_class_name$.internal__$identifier$__Descriptor; }\n"
" get { return $descriptor_accessor$; }\n"
"}\n"
"\n"
"public pb::FieldAccess.FieldAccessorTable<$class_name$> Fields {\n"
"public pb::FieldAccess.FieldAccessorTable Fields {\n"
" get { return $umbrella_class_name$.internal__$identifier$__FieldAccessorTable; }\n"
"}\n"
"\n"
"private bool _frozen = false;\n"
"public bool IsFrozen { get { return _frozen; } }\n\n");
// Parameterless constructor
// Parameterless constructor and partial OnConstruction method.
printer->Print(
vars,
"public $class_name$() { }\n\n");
"public $class_name$() {\n"
" OnConstruction();\n"
"}\n\n"
"partial void OnConstruction();\n\n");
GenerateCloningCode(printer);
GenerateFreezingCode(printer);
@ -304,7 +314,7 @@ void MessageGenerator::GenerateCloningCode(io::Printer* printer) {
vars["class_name"] = class_name();
printer->Print(
vars,
"public $class_name$($class_name$ other) {\n");
"public $class_name$($class_name$ other) : this() {\n");
printer->Indent();
// Clone non-oneof fields first
for (int i = 0; i < descriptor_->field_count(); i++) {

View File

@ -64,7 +64,7 @@ void MessageFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer);
printer->Print(
variables_,
"public $type_name$ $property_name$ {\n"
"$access_level$ $type_name$ $property_name$ {\n"
" get { return $name$_; }\n"
" set {\n"
" pb::Freezable.CheckMutable(this);\n"
@ -158,7 +158,7 @@ void MessageOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer);
printer->Print(
variables_,
"public $type_name$ $property_name$ {\n"
"$access_level$ $type_name$ $property_name$ {\n"
" get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : null; }\n"
" set {\n"
" pb::Freezable.CheckMutable(this);\n"

View File

@ -71,7 +71,7 @@ void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer);
printer->Print(
variables_,
"public $type_name$ $property_name$ {\n"
"$access_level$ $type_name$ $property_name$ {\n"
" get { return $name$_; }\n"
" set {\n"
" pb::Freezable.CheckMutable(this);\n");
@ -174,7 +174,7 @@ void PrimitiveOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer);
printer->Print(
variables_,
"public $type_name$ $property_name$ {\n"
"$access_level$ $type_name$ $property_name$ {\n"
" get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : $default_value$; }\n"
" set {\n"
" pb::Freezable.CheckMutable(this);\n");

View File

@ -65,7 +65,7 @@ void RepeatedEnumFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer);
printer->Print(
variables_,
"public pbc::RepeatedField<$type_name$> $property_name$ {\n"
"$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
" get { return $name$_; }\n"
"}\n");
}

View File

@ -65,7 +65,7 @@ void RepeatedMessageFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer);
printer->Print(
variables_,
"public pbc::RepeatedField<$type_name$> $property_name$ {\n"
"$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
" get { return $name$_; }\n"
"}\n");
}

View File

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

View File

@ -57,7 +57,7 @@ void SourceGeneratorBase::WriteGeneratedCodeAttributes(io::Printer* printer) {
}
std::string SourceGeneratorBase::class_access_level() {
return "public"; // public_classes is always on.
return IsDescriptorProto(descriptor_) ? "internal" : "public"; // public_classes is always on.
}
} // namespace csharp

View File

@ -176,22 +176,11 @@ void UmbrellaClassGenerator::WriteDescriptor(io::Printer* printer) {
printer->Print("\"$base64$\"));\n", "base64", base64);
printer->Outdent();
printer->Outdent();
printer->Print(
"pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) {\n");
printer->Indent();
printer->Print("descriptor = root;\n");
for (int i = 0; i < file_->message_type_count(); i++) {
MessageGenerator messageGenerator(file_->message_type(i));
messageGenerator.GenerateStaticVariableInitializers(printer);
}
printer->Outdent();
printer->Print("};\n");
// -----------------------------------------------------------------
// Invoke internalBuildGeneratedFileFrom() to build the file.
// Invoke InternalBuildGeneratedFileFrom() to build the file.
printer->Print(
"pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,\n");
"descriptor = pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,\n");
printer->Print(" new pbd::FileDescriptor[] {\n");
for (int i = 0; i < file_->dependency_count(); i++) {
printer->Print(
@ -199,7 +188,12 @@ void UmbrellaClassGenerator::WriteDescriptor(io::Printer* printer) {
"full_umbrella_class_name",
GetFullUmbrellaClassName(file_->dependency(i)));
}
printer->Print(" }, assigner);\n");
printer->Print(" });\n");
// Then invoke any other static variable initializers, e.g. field accessors.
for (int i = 0; i < file_->message_type_count(); i++) {
MessageGenerator messageGenerator(file_->message_type(i));
messageGenerator.GenerateStaticVariableInitializers(printer);
}
printer->Outdent();
printer->Print("}\n");
printer->Print("#endregion\n\n");