aboutsummaryrefslogtreecommitdiff
path: root/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
diff options
context:
space:
mode:
Diffstat (limited to 'csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs')
-rw-r--r--csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs80
1 files changed, 80 insertions, 0 deletions
diff --git a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
index 216e03cc..6d4520c0 100644
--- a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
@@ -34,7 +34,10 @@ using Google.Protobuf.WellKnownTypes;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Diagnostics;
using System.Linq;
+using System.Threading;
+using static Google.Protobuf.Reflection.SourceCodeInfo.Types;
namespace Google.Protobuf.Reflection
{
@@ -55,6 +58,8 @@ namespace Google.Protobuf.Reflection
ForceReflectionInitialization<Value.KindOneofCase>();
}
+ private readonly Lazy<Dictionary<IDescriptor, DescriptorDeclaration>> declarations;
+
private FileDescriptor(ByteString descriptorData, FileDescriptorProto proto, IEnumerable<FileDescriptor> dependencies, DescriptorPool pool, bool allowUnknownDependencies, GeneratedClrTypeInfo generatedCodeInfo)
{
SerializedData = descriptorData;
@@ -77,6 +82,81 @@ namespace Google.Protobuf.Reflection
Services = DescriptorUtil.ConvertAndMakeReadOnly(proto.Service,
(service, index) =>
new ServiceDescriptor(service, this, index));
+
+ declarations = new Lazy<Dictionary<IDescriptor, DescriptorDeclaration>>(CreateDeclarationMap, LazyThreadSafetyMode.ExecutionAndPublication);
+ }
+
+ private Dictionary<IDescriptor, DescriptorDeclaration> CreateDeclarationMap()
+ {
+ var dictionary = new Dictionary<IDescriptor, DescriptorDeclaration>();
+ foreach (var location in Proto.SourceCodeInfo?.Location ?? Enumerable.Empty<Location>())
+ {
+ var descriptor = FindDescriptorForPath(location.Path);
+ if (descriptor != null)
+ {
+ dictionary[descriptor] = DescriptorDeclaration.FromProto(descriptor, location);
+ }
+ }
+ return dictionary;
+
+ IDescriptor FindDescriptorForPath(IList<int> path)
+ {
+ // All complete declarations have an even, non-empty path length
+ // (There can be an empty path for a descriptor declaration, but that can't have any comments,
+ // so we currently ignore it.)
+ if (path.Count == 0 || (path.Count & 1) != 0)
+ {
+ return null;
+ }
+ IReadOnlyList<DescriptorBase> topLevelList = GetNestedDescriptorListForField(path[0]);
+ DescriptorBase current = GetDescriptorFromList(topLevelList, path[1]);
+
+ for (int i = 2; current != null && i < path.Count; i += 2)
+ {
+ var list = current.GetNestedDescriptorListForField(path[i]);
+ current = GetDescriptorFromList(list, path[i + 1]);
+ }
+ return current;
+ }
+
+ DescriptorBase GetDescriptorFromList(IReadOnlyList<DescriptorBase> list, int index)
+ {
+ // This is fine: it may be a newer version of protobuf than we understand, with a new descriptor
+ // field.
+ if (list == null)
+ {
+ return null;
+ }
+ // We *could* return null to silently continue, but this is basically data corruption.
+ if (index < 0 || index >= list.Count)
+ {
+ // We don't have much extra information to give at this point unfortunately. If this becomes a problem,
+ // we can pass in the complete path and report that and the file name.
+ throw new InvalidProtocolBufferException($"Invalid descriptor location path: index out of range");
+ }
+ return list[index];
+ }
+
+ IReadOnlyList<DescriptorBase> GetNestedDescriptorListForField(int fieldNumber)
+ {
+ switch (fieldNumber)
+ {
+ case FileDescriptorProto.ServiceFieldNumber:
+ return (IReadOnlyList<DescriptorBase>) Services;
+ case FileDescriptorProto.MessageTypeFieldNumber:
+ return (IReadOnlyList<DescriptorBase>) MessageTypes;
+ case FileDescriptorProto.EnumTypeFieldNumber:
+ return (IReadOnlyList<DescriptorBase>) EnumTypes;
+ default:
+ return null;
+ }
+ }
+ }
+
+ internal DescriptorDeclaration GetDeclaration(IDescriptor descriptor)
+ {
+ declarations.Value.TryGetValue(descriptor, out var declaration);
+ return declaration;
}
/// <summary>