Skip to content

Commit

Permalink
Add ToProto() method to all C# descriptor classes
Browse files Browse the repository at this point in the history
Fixes #9425.
  • Loading branch information
jskeet committed Jan 28, 2022
1 parent b48ba57 commit b926a7d
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 7 deletions.
24 changes: 24 additions & 0 deletions csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs
Expand Up @@ -124,6 +124,7 @@ private void TestFileDescriptor(FileDescriptor file, FileDescriptor importedFile
}

Assert.AreEqual(10, file.SerializedData[0]);
TestDescriptorToProto(file.ToProto, file.Proto);
}

[Test]
Expand Down Expand Up @@ -231,6 +232,7 @@ public void MessageDescriptor()
{
Assert.AreEqual(i, messageType.EnumTypes[i].Index);
}
TestDescriptorToProto(messageType.ToProto, messageType.Proto);
}

[Test]
Expand Down Expand Up @@ -294,6 +296,11 @@ public void FieldDescriptor_BuildFromByteStrings()
// For a field in a regular onoef, ContainingOneof and RealContainingOneof should be the same.
Assert.AreEqual("oneof_field", fieldInOneof.ContainingOneof.Name);
Assert.AreSame(fieldInOneof.ContainingOneof, fieldInOneof.RealContainingOneof);

TestDescriptorToProto(primitiveField.ToProto, primitiveField.Proto);
TestDescriptorToProto(enumField.ToProto, enumField.Proto);
TestDescriptorToProto(foreignMessageField.ToProto, foreignMessageField.Proto);
TestDescriptorToProto(fieldInOneof.ToProto, fieldInOneof.Proto);
}

[Test]
Expand Down Expand Up @@ -338,6 +345,8 @@ public void EnumDescriptor()
{
Assert.AreEqual(i, enumType.Values[i].Index);
}
TestDescriptorToProto(enumType.ToProto, enumType.Proto);
TestDescriptorToProto(nestedType.ToProto, nestedType.Proto);
}

[Test]
Expand All @@ -361,6 +370,7 @@ public void OneofDescriptor()
}

CollectionAssert.AreEquivalent(expectedFields, descriptor.Fields);
TestDescriptorToProto(descriptor.ToProto, descriptor.Proto);
}

[Test]
Expand All @@ -370,6 +380,7 @@ public void MapEntryMessageDescriptor()
Assert.IsNull(descriptor.Parser);
Assert.IsNull(descriptor.ClrType);
Assert.IsNull(descriptor.Fields[1].Accessor);
TestDescriptorToProto(descriptor.ToProto, descriptor.Proto);
}

// From TestFieldOrdering:
Expand All @@ -391,6 +402,7 @@ public void DescriptorProtoFileDescriptor()
{
var descriptor = Google.Protobuf.Reflection.FileDescriptor.DescriptorProtoFileDescriptor;
Assert.AreEqual("google/protobuf/descriptor.proto", descriptor.Name);
TestDescriptorToProto(descriptor.ToProto, descriptor.Proto);
}

[Test]
Expand Down Expand Up @@ -453,5 +465,17 @@ public void SyntheticOneofReflection()
}
}
}

private static void TestDescriptorToProto(Func<IMessage> toProtoFunction, IMessage expectedProto)
{
var clone1 = toProtoFunction();
var clone2 = toProtoFunction();
Assert.AreNotSame(clone1, clone2);
Assert.AreNotSame(clone1, expectedProto);
Assert.AreNotSame(clone2, expectedProto);

Assert.AreEqual(clone1, clone2);
Assert.AreEqual(clone1, expectedProto);
}
}
}
8 changes: 8 additions & 0 deletions csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs
Expand Up @@ -68,6 +68,14 @@ internal EnumDescriptor(EnumDescriptorProto proto, FileDescriptor file, MessageD

internal EnumDescriptorProto Proto { get { return proto; } }

/// <summary>
/// Returns a clone of the underlying <see cref="EnumDescriptorProto"/> describing this enum.
/// Note that a copy is taken every time this method is called, so clients using it frequently
/// (and not modifying it) may want to cache the returned value.
/// </summary>
/// <returns>A protobuf representation of this enum descriptor.</returns>
public EnumDescriptorProto ToProto() => Proto.Clone();

/// <summary>
/// The brief name of the descriptor's target.
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions csharp/src/Google.Protobuf/Reflection/EnumValueDescriptor.cs
Expand Up @@ -55,6 +55,14 @@ public sealed class EnumValueDescriptor : DescriptorBase

internal EnumValueDescriptorProto Proto { get { return proto; } }

/// <summary>
/// Returns a clone of the underlying <see cref="EnumValueDescriptorProto"/> describing this enum value.
/// Note that a copy is taken every time this method is called, so clients using it frequently
/// (and not modifying it) may want to cache the returned value.
/// </summary>
/// <returns>A protobuf representation of this enum value descriptor.</returns>
public EnumValueDescriptorProto ToProto() => Proto.Clone();

/// <summary>
/// Returns the name of the enum value described by this object.
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
Expand Up @@ -91,6 +91,14 @@ public sealed class FieldDescriptor : DescriptorBase, IComparable<FieldDescripto

internal FieldDescriptorProto Proto { get; }

/// <summary>
/// Returns a clone of the underlying <see cref="FieldDescriptorProto"/> describing this field.
/// Note that a copy is taken every time this method is called, so clients using it frequently
/// (and not modifying it) may want to cache the returned value.
/// </summary>
/// <returns>A protobuf representation of this field descriptor.</returns>
public FieldDescriptorProto ToProto() => Proto.Clone();

/// <summary>
/// An extension identifier for this field, or <c>null</c> if this field isn't an extension.
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
Expand Up @@ -249,6 +249,14 @@ private static IList<FileDescriptor> DeterminePublicDependencies(FileDescriptor
/// </value>
internal FileDescriptorProto Proto { get; }

/// <summary>
/// Returns a clone of the underlying <see cref="FileDescriptorProto"/> describing this file.
/// Note that a copy is taken every time this method is called, so clients using it frequently
/// (and not modifying it) may want to cache the returned value.
/// </summary>
/// <returns>A protobuf representation of this file descriptor.</returns>
public FileDescriptorProto ToProto() => Proto.Clone();

/// <summary>
/// The syntax of the file
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
Expand Up @@ -151,6 +151,14 @@ internal override IReadOnlyList<DescriptorBase> GetNestedDescriptorListForField(

internal DescriptorProto Proto { get; }

/// <summary>
/// Returns a clone of the underlying <see cref="DescriptorProto"/> describing this message.
/// Note that a copy is taken every time this method is called, so clients using it frequently
/// (and not modifying it) may want to cache the returned value.
/// </summary>
/// <returns>A protobuf representation of this message descriptor.</returns>
public DescriptorProto ToProto() => Proto.Clone();

internal bool IsExtensionsInitialized(IMessage message)
{
if (Proto.ExtensionRange.Count == 0)
Expand Down
8 changes: 8 additions & 0 deletions csharp/src/Google.Protobuf/Reflection/MethodDescriptor.cs
Expand Up @@ -114,6 +114,14 @@ public RepeatedField<T> GetOption<T>(RepeatedExtension<MethodOptions, T> extensi

internal MethodDescriptorProto Proto { get { return proto; } }

/// <summary>
/// Returns a clone of the underlying <see cref="MethodDescriptorProto"/> describing this method.
/// Note that a copy is taken every time this method is called, so clients using it frequently
/// (and not modifying it) may want to cache the returned value.
/// </summary>
/// <returns>A protobuf representation of this method descriptor.</returns>
public MethodDescriptorProto ToProto() => Proto.Clone();

/// <summary>
/// The brief name of the descriptor's target.
/// </summary>
Expand Down
24 changes: 17 additions & 7 deletions csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs
Expand Up @@ -45,15 +45,14 @@ namespace Google.Protobuf.Reflection
/// </summary>
public sealed class OneofDescriptor : DescriptorBase
{
private readonly OneofDescriptorProto proto;
private MessageDescriptor containingType;
private IList<FieldDescriptor> fields;
private readonly OneofAccessor accessor;

internal OneofDescriptor(OneofDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index, string clrName)
: base(file, file.ComputeFullName(parent, proto.Name), index)
{
this.proto = proto;
this.Proto = proto;
containingType = parent;
file.DescriptorPool.AddSymbol(this);

Expand All @@ -68,7 +67,18 @@ internal OneofDescriptor(OneofDescriptorProto proto, FileDescriptor file, Messag
/// <summary>
/// The brief name of the descriptor's target.
/// </summary>
public override string Name { get { return proto.Name; } }
public override string Name => Proto.Name;

// Visible for testing
internal OneofDescriptorProto Proto { get; }

/// <summary>
/// Returns a clone of the underlying <see cref="OneofDescriptorProto"/> describing this oneof.
/// Note that a copy is taken every time this method is called, so clients using it frequently
/// (and not modifying it) may want to cache the returned value.
/// </summary>
/// <returns>A protobuf representation of this oneof descriptor.</returns>
public OneofDescriptorProto ToProto() => Proto.Clone();

/// <summary>
/// Gets the message type containing this oneof.
Expand Down Expand Up @@ -118,23 +128,23 @@ public MessageDescriptor ContainingType
/// The (possibly empty) set of custom options for this oneof.
/// </summary>
[Obsolete("CustomOptions are obsolete. Use the GetOptions method.")]
public CustomOptions CustomOptions => new CustomOptions(proto.Options?._extensions?.ValuesByNumber);
public CustomOptions CustomOptions => new CustomOptions(Proto.Options?._extensions?.ValuesByNumber);

/// <summary>
/// The <c>OneofOptions</c>, defined in <c>descriptor.proto</c>.
/// If the options message is not present (i.e. there are no options), <c>null</c> is returned.
/// Custom options can be retrieved as extensions of the returned message.
/// NOTE: A defensive copy is created each time this property is retrieved.
/// </summary>
public OneofOptions GetOptions() => proto.Options?.Clone();
public OneofOptions GetOptions() => Proto.Options?.Clone();

/// <summary>
/// Gets a single value oneof option for this descriptor
/// </summary>
[Obsolete("GetOption is obsolete. Use the GetOptions() method.")]
public T GetOption<T>(Extension<OneofOptions, T> extension)
{
var value = proto.Options.GetExtension(extension);
var value = Proto.Options.GetExtension(extension);
return value is IDeepCloneable<T> ? (value as IDeepCloneable<T>).Clone() : value;
}

Expand All @@ -144,7 +154,7 @@ public T GetOption<T>(Extension<OneofOptions, T> extension)
[Obsolete("GetOption is obsolete. Use the GetOptions() method.")]
public RepeatedField<T> GetOption<T>(RepeatedExtension<OneofOptions, T> extension)
{
return proto.Options.GetExtension(extension).Clone();
return Proto.Options.GetExtension(extension).Clone();
}

internal void CrossLink()
Expand Down
8 changes: 8 additions & 0 deletions csharp/src/Google.Protobuf/Reflection/ServiceDescriptor.cs
Expand Up @@ -73,6 +73,14 @@ internal override IReadOnlyList<DescriptorBase> GetNestedDescriptorListForField(

internal ServiceDescriptorProto Proto { get { return proto; } }

/// <summary>
/// Returns a clone of the underlying <see cref="ServiceDescriptorProto"/> describing this service.
/// Note that a copy is taken every time this method is called, so clients using it frequently
/// (and not modifying it) may want to cache the returned value.
/// </summary>
/// <returns>A protobuf representation of this service descriptor.</returns>
public ServiceDescriptorProto ToProto() => Proto.Clone();

/// <value>
/// An unmodifiable list of methods in this service.
/// </value>
Expand Down

0 comments on commit b926a7d

Please sign in to comment.