1 #region Copyright notice and license 2 // Protocol Buffers - Google's data interchange format 3 // Copyright 2008 Google Inc. All rights reserved. 4 // https://developers.google.com/protocol-buffers/ 5 // 6 // Redistribution and use in source and binary forms, with or without 7 // modification, are permitted provided that the following conditions are 8 // met: 9 // 10 // * Redistributions of source code must retain the above copyright 11 // notice, this list of conditions and the following disclaimer. 12 // * Redistributions in binary form must reproduce the above 13 // copyright notice, this list of conditions and the following disclaimer 14 // in the documentation and/or other materials provided with the 15 // distribution. 16 // * Neither the name of Google Inc. nor the names of its 17 // contributors may be used to endorse or promote products derived from 18 // this software without specific prior written permission. 19 // 20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 #endregion 32 33 using Google.Protobuf.Collections; 34 using Google.Protobuf.WellKnownTypes; 35 using System; 36 using System.Collections.Generic; 37 using System.Collections.ObjectModel; 38 using System.Diagnostics; 39 using System.Linq; 40 using System.Threading; 41 using static Google.Protobuf.Reflection.SourceCodeInfo.Types; 42 43 namespace Google.Protobuf.Reflection 44 { 45 /// <summary> 46 /// The syntax of a .proto file 47 /// </summary> 48 public enum Syntax 49 { 50 /// <summary> 51 /// Proto2 syntax 52 /// </summary> 53 Proto2, 54 /// <summary> 55 /// Proto3 syntax 56 /// </summary> 57 Proto3, 58 /// <summary> 59 /// An unknown declared syntax 60 /// </summary> 61 Unknown 62 } 63 64 /// <summary> 65 /// Describes a .proto file, including everything defined within. 66 /// IDescriptor is implemented such that the File property returns this descriptor, 67 /// and the FullName is the same as the Name. 68 /// </summary> 69 public sealed class FileDescriptor : IDescriptor 70 { 71 // Prevent linker failures when using IL2CPP with the well-known types. FileDescriptor()72 static FileDescriptor() 73 { 74 ForceReflectionInitialization<Syntax>(); 75 ForceReflectionInitialization<NullValue>(); 76 ForceReflectionInitialization<Field.Types.Cardinality>(); 77 ForceReflectionInitialization<Field.Types.Kind>(); 78 ForceReflectionInitialization<Value.KindOneofCase>(); 79 } 80 81 private readonly Lazy<Dictionary<IDescriptor, DescriptorDeclaration>> declarations; 82 FileDescriptor(ByteString descriptorData, FileDescriptorProto proto, IEnumerable<FileDescriptor> dependencies, DescriptorPool pool, bool allowUnknownDependencies, GeneratedClrTypeInfo generatedCodeInfo)83 private FileDescriptor(ByteString descriptorData, FileDescriptorProto proto, IEnumerable<FileDescriptor> dependencies, DescriptorPool pool, bool allowUnknownDependencies, GeneratedClrTypeInfo generatedCodeInfo) 84 { 85 SerializedData = descriptorData; 86 DescriptorPool = pool; 87 Proto = proto; 88 Dependencies = new ReadOnlyCollection<FileDescriptor>(dependencies.ToList()); 89 90 PublicDependencies = DeterminePublicDependencies(this, proto, dependencies, allowUnknownDependencies); 91 92 pool.AddPackage(Package, this); 93 94 MessageTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.MessageType, 95 (message, index) => 96 new MessageDescriptor(message, this, null, index, generatedCodeInfo?.NestedTypes[index])); 97 98 EnumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType, 99 (enumType, index) => 100 new EnumDescriptor(enumType, this, null, index, generatedCodeInfo?.NestedEnums[index])); 101 102 Services = DescriptorUtil.ConvertAndMakeReadOnly(proto.Service, 103 (service, index) => 104 new ServiceDescriptor(service, this, index)); 105 106 Extensions = new ExtensionCollection(this, generatedCodeInfo?.Extensions); 107 108 declarations = new Lazy<Dictionary<IDescriptor, DescriptorDeclaration>>(CreateDeclarationMap, LazyThreadSafetyMode.ExecutionAndPublication); 109 110 if (!proto.HasSyntax || proto.Syntax == "proto2") 111 { 112 Syntax = Syntax.Proto2; 113 } 114 else if (proto.Syntax == "proto3") 115 { 116 Syntax = Syntax.Proto3; 117 } 118 else 119 { 120 Syntax = Syntax.Unknown; 121 } 122 } 123 CreateDeclarationMap()124 private Dictionary<IDescriptor, DescriptorDeclaration> CreateDeclarationMap() 125 { 126 var dictionary = new Dictionary<IDescriptor, DescriptorDeclaration>(); 127 foreach (var location in Proto.SourceCodeInfo?.Location ?? Enumerable.Empty<Location>()) 128 { 129 var descriptor = FindDescriptorForPath(location.Path); 130 if (descriptor != null) 131 { 132 dictionary[descriptor] = DescriptorDeclaration.FromProto(descriptor, location); 133 } 134 } 135 return dictionary; 136 } 137 FindDescriptorForPath(IList<int> path)138 private IDescriptor FindDescriptorForPath(IList<int> path) 139 { 140 // All complete declarations have an even, non-empty path length 141 // (There can be an empty path for a descriptor declaration, but that can't have any comments, 142 // so we currently ignore it.) 143 if (path.Count == 0 || (path.Count & 1) != 0) 144 { 145 return null; 146 } 147 IReadOnlyList<DescriptorBase> topLevelList = GetNestedDescriptorListForField(path[0]); 148 DescriptorBase current = GetDescriptorFromList(topLevelList, path[1]); 149 150 for (int i = 2; current != null && i < path.Count; i += 2) 151 { 152 var list = current.GetNestedDescriptorListForField(path[i]); 153 current = GetDescriptorFromList(list, path[i + 1]); 154 } 155 return current; 156 } 157 GetDescriptorFromList(IReadOnlyList<DescriptorBase> list, int index)158 private DescriptorBase GetDescriptorFromList(IReadOnlyList<DescriptorBase> list, int index) 159 { 160 // This is fine: it may be a newer version of protobuf than we understand, with a new descriptor 161 // field. 162 if (list == null) 163 { 164 return null; 165 } 166 // We *could* return null to silently continue, but this is basically data corruption. 167 if (index < 0 || index >= list.Count) 168 { 169 // We don't have much extra information to give at this point unfortunately. If this becomes a problem, 170 // we can pass in the complete path and report that and the file name. 171 throw new InvalidProtocolBufferException($"Invalid descriptor location path: index out of range"); 172 } 173 return list[index]; 174 } 175 GetNestedDescriptorListForField(int fieldNumber)176 private IReadOnlyList<DescriptorBase> GetNestedDescriptorListForField(int fieldNumber) 177 { 178 switch (fieldNumber) 179 { 180 case FileDescriptorProto.ServiceFieldNumber: 181 return (IReadOnlyList<DescriptorBase>) Services; 182 case FileDescriptorProto.MessageTypeFieldNumber: 183 return (IReadOnlyList<DescriptorBase>) MessageTypes; 184 case FileDescriptorProto.EnumTypeFieldNumber: 185 return (IReadOnlyList<DescriptorBase>) EnumTypes; 186 default: 187 return null; 188 } 189 } 190 GetDeclaration(IDescriptor descriptor)191 internal DescriptorDeclaration GetDeclaration(IDescriptor descriptor) 192 { 193 DescriptorDeclaration declaration; 194 declarations.Value.TryGetValue(descriptor, out declaration); 195 return declaration; 196 } 197 198 /// <summary> 199 /// Computes the full name of a descriptor within this file, with an optional parent message. 200 /// </summary> ComputeFullName(MessageDescriptor parent, string name)201 internal string ComputeFullName(MessageDescriptor parent, string name) 202 { 203 if (parent != null) 204 { 205 return parent.FullName + "." + name; 206 } 207 if (Package.Length > 0) 208 { 209 return Package + "." + name; 210 } 211 return name; 212 } 213 214 /// <summary> 215 /// Extracts public dependencies from direct dependencies. This is a static method despite its 216 /// first parameter, as the value we're in the middle of constructing is only used for exceptions. 217 /// </summary> 218 private static IList<FileDescriptor> DeterminePublicDependencies(FileDescriptor @this, FileDescriptorProto proto, IEnumerable<FileDescriptor> dependencies, bool allowUnknownDependencies) 219 { 220 var nameToFileMap = dependencies.ToDictionary(file => file.Name); 221 var publicDependencies = new List<FileDescriptor>(); 222 for (int i = 0; i < proto.PublicDependency.Count; i++) 223 { 224 int index = proto.PublicDependency[i]; 225 if (index < 0 || index >= proto.Dependency.Count) 226 { 227 throw new DescriptorValidationException(@this, "Invalid public dependency index."); 228 } 229 string name = proto.Dependency[index]; 230 FileDescriptor file; 231 if (!nameToFileMap.TryGetValue(name, out file)) 232 { 233 if (!allowUnknownDependencies) 234 { 235 throw new DescriptorValidationException(@this, "Invalid public dependency: " + name); 236 } 237 // Ignore unknown dependencies. 238 } 239 else 240 { 241 publicDependencies.Add(file); 242 } 243 } 244 return new ReadOnlyCollection<FileDescriptor>(publicDependencies); 245 } 246 247 /// <value> 248 /// The descriptor in its protocol message representation. 249 /// </value> 250 internal FileDescriptorProto Proto { get; } 251 252 /// <summary> 253 /// Returns a clone of the underlying <see cref="FileDescriptorProto"/> describing this file. 254 /// Note that a copy is taken every time this method is called, so clients using it frequently 255 /// (and not modifying it) may want to cache the returned value. 256 /// </summary> 257 /// <returns>A protobuf representation of this file descriptor.</returns> ToProto()258 public FileDescriptorProto ToProto() => Proto.Clone(); 259 260 /// <summary> 261 /// The syntax of the file 262 /// </summary> 263 public Syntax Syntax { get; } 264 265 /// <value> 266 /// The file name. 267 /// </value> 268 public string Name => Proto.Name; 269 270 /// <summary> 271 /// The package as declared in the .proto file. This may or may not 272 /// be equivalent to the .NET namespace of the generated classes. 273 /// </summary> 274 public string Package => Proto.Package; 275 276 /// <value> 277 /// Unmodifiable list of top-level message types declared in this file. 278 /// </value> 279 public IList<MessageDescriptor> MessageTypes { get; } 280 281 /// <value> 282 /// Unmodifiable list of top-level enum types declared in this file. 283 /// </value> 284 public IList<EnumDescriptor> EnumTypes { get; } 285 286 /// <value> 287 /// Unmodifiable list of top-level services declared in this file. 288 /// </value> 289 public IList<ServiceDescriptor> Services { get; } 290 291 /// <summary> 292 /// Unmodifiable list of top-level extensions declared in this file. 293 /// Note that some extensions may be incomplete (FieldDescriptor.Extension may be null) 294 /// if this descriptor was generated using a version of protoc that did not fully 295 /// support extensions in C#. 296 /// </summary> 297 public ExtensionCollection Extensions { get; } 298 299 /// <value> 300 /// Unmodifiable list of this file's dependencies (imports). 301 /// </value> 302 public IList<FileDescriptor> Dependencies { get; } 303 304 /// <value> 305 /// Unmodifiable list of this file's public dependencies (public imports). 306 /// </value> 307 public IList<FileDescriptor> PublicDependencies { get; } 308 309 /// <value> 310 /// The original serialized binary form of this descriptor. 311 /// </value> 312 public ByteString SerializedData { get; } 313 314 /// <value> 315 /// Implementation of IDescriptor.FullName - just returns the same as Name. 316 /// </value> 317 string IDescriptor.FullName => Name; 318 319 /// <value> 320 /// Implementation of IDescriptor.File - just returns this descriptor. 321 /// </value> 322 FileDescriptor IDescriptor.File => this; 323 324 /// <value> 325 /// Pool containing symbol descriptors. 326 /// </value> 327 internal DescriptorPool DescriptorPool { get; } 328 329 /// <summary> 330 /// Finds a type (message, enum, service or extension) in the file by name. Does not find nested types. 331 /// </summary> 332 /// <param name="name">The unqualified type name to look for.</param> 333 /// <typeparam name="T">The type of descriptor to look for</typeparam> 334 /// <returns>The type's descriptor, or null if not found.</returns> 335 public T FindTypeByName<T>(String name) 336 where T : class, IDescriptor 337 { 338 // Don't allow looking up nested types. This will make optimization 339 // easier later. 340 if (name.IndexOf('.') != -1) 341 { 342 return null; 343 } 344 if (Package.Length > 0) 345 { 346 name = Package + "." + name; 347 } 348 T result = DescriptorPool.FindSymbol<T>(name); 349 if (result != null && result.File == this) 350 { 351 return result; 352 } 353 return null; 354 } 355 356 /// <summary> 357 /// Builds a FileDescriptor from its protocol buffer representation. 358 /// </summary> 359 /// <param name="descriptorData">The original serialized descriptor data. 360 /// We have only limited proto2 support, so serializing FileDescriptorProto 361 /// would not necessarily give us this.</param> 362 /// <param name="proto">The protocol message form of the FileDescriptor.</param> 363 /// <param name="dependencies">FileDescriptors corresponding to all of the 364 /// file's dependencies, in the exact order listed in the .proto file. May be null, 365 /// in which case it is treated as an empty array.</param> 366 /// <param name="allowUnknownDependencies">Whether unknown dependencies are ignored (true) or cause an exception to be thrown (false).</param> 367 /// <param name="generatedCodeInfo">Details about generated code, for the purposes of reflection.</param> 368 /// <exception cref="DescriptorValidationException">If <paramref name="proto"/> is not 369 /// a valid descriptor. This can occur for a number of reasons, such as a field 370 /// having an undefined type or because two messages were defined with the same name.</exception> BuildFrom(ByteString descriptorData, FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies, GeneratedClrTypeInfo generatedCodeInfo)371 private static FileDescriptor BuildFrom(ByteString descriptorData, FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies, GeneratedClrTypeInfo generatedCodeInfo) 372 { 373 // Building descriptors involves two steps: translating and linking. 374 // In the translation step (implemented by FileDescriptor's 375 // constructor), we build an object tree mirroring the 376 // FileDescriptorProto's tree and put all of the descriptors into the 377 // DescriptorPool's lookup tables. In the linking step, we look up all 378 // type references in the DescriptorPool, so that, for example, a 379 // FieldDescriptor for an embedded message contains a pointer directly 380 // to the Descriptor for that message's type. We also detect undefined 381 // types in the linking step. 382 if (dependencies == null) 383 { 384 dependencies = new FileDescriptor[0]; 385 } 386 387 DescriptorPool pool = new DescriptorPool(dependencies); 388 FileDescriptor result = new FileDescriptor(descriptorData, proto, dependencies, pool, allowUnknownDependencies, generatedCodeInfo); 389 390 // Validate that the dependencies we've been passed (as FileDescriptors) are actually the ones we 391 // need. 392 if (dependencies.Length != proto.Dependency.Count) 393 { 394 throw new DescriptorValidationException( 395 result, 396 "Dependencies passed to FileDescriptor.BuildFrom() don't match " + 397 "those listed in the FileDescriptorProto."); 398 } 399 400 result.CrossLink(); 401 return result; 402 } 403 CrossLink()404 private void CrossLink() 405 { 406 foreach (MessageDescriptor message in MessageTypes) 407 { 408 message.CrossLink(); 409 } 410 411 foreach (ServiceDescriptor service in Services) 412 { 413 service.CrossLink(); 414 } 415 416 Extensions.CrossLink(); 417 } 418 419 /// <summary> 420 /// Creates a descriptor for generated code. 421 /// </summary> 422 /// <remarks> 423 /// This method is only designed to be used by the results of generating code with protoc, 424 /// which creates the appropriate dependencies etc. It has to be public because the generated 425 /// code is "external", but should not be called directly by end users. 426 /// </remarks> FromGeneratedCode( byte[] descriptorData, FileDescriptor[] dependencies, GeneratedClrTypeInfo generatedCodeInfo)427 public static FileDescriptor FromGeneratedCode( 428 byte[] descriptorData, 429 FileDescriptor[] dependencies, 430 GeneratedClrTypeInfo generatedCodeInfo) 431 { 432 ExtensionRegistry registry = new ExtensionRegistry(); 433 registry.AddRange(GetAllExtensions(dependencies, generatedCodeInfo)); 434 435 FileDescriptorProto proto; 436 try 437 { 438 proto = FileDescriptorProto.Parser.WithExtensionRegistry(registry).ParseFrom(descriptorData); 439 } 440 catch (InvalidProtocolBufferException e) 441 { 442 throw new ArgumentException("Failed to parse protocol buffer descriptor for generated code.", e); 443 } 444 445 try 446 { 447 // When building descriptors for generated code, we allow unknown 448 // dependencies by default. 449 return BuildFrom(ByteString.CopyFrom(descriptorData), proto, dependencies, true, generatedCodeInfo); 450 } 451 catch (DescriptorValidationException e) 452 { 453 throw new ArgumentException($"Invalid embedded descriptor for \"{proto.Name}\".", e); 454 } 455 } 456 GetAllExtensions(FileDescriptor[] dependencies, GeneratedClrTypeInfo generatedInfo)457 private static IEnumerable<Extension> GetAllExtensions(FileDescriptor[] dependencies, GeneratedClrTypeInfo generatedInfo) 458 { 459 return dependencies.SelectMany(GetAllDependedExtensions).Distinct(ExtensionRegistry.ExtensionComparer.Instance).Concat(GetAllGeneratedExtensions(generatedInfo)); 460 } 461 GetAllGeneratedExtensions(GeneratedClrTypeInfo generated)462 private static IEnumerable<Extension> GetAllGeneratedExtensions(GeneratedClrTypeInfo generated) 463 { 464 return generated.Extensions.Concat(generated.NestedTypes.Where(t => t != null).SelectMany(GetAllGeneratedExtensions)); 465 } 466 GetAllDependedExtensions(FileDescriptor descriptor)467 private static IEnumerable<Extension> GetAllDependedExtensions(FileDescriptor descriptor) 468 { 469 return descriptor.Extensions.UnorderedExtensions 470 .Select(s => s.Extension) 471 .Where(e => e != null) 472 .Concat(descriptor.Dependencies.Concat(descriptor.PublicDependencies).SelectMany(GetAllDependedExtensions)) 473 .Concat(descriptor.MessageTypes.SelectMany(GetAllDependedExtensionsFromMessage)); 474 } 475 GetAllDependedExtensionsFromMessage(MessageDescriptor descriptor)476 private static IEnumerable<Extension> GetAllDependedExtensionsFromMessage(MessageDescriptor descriptor) 477 { 478 return descriptor.Extensions.UnorderedExtensions 479 .Select(s => s.Extension) 480 .Where(e => e != null) 481 .Concat(descriptor.NestedTypes.SelectMany(GetAllDependedExtensionsFromMessage)); 482 } 483 484 /// <summary> 485 /// Converts the given descriptor binary data into FileDescriptor objects. 486 /// Note: reflection using the returned FileDescriptors is not currently supported. 487 /// </summary> 488 /// <param name="descriptorData">The binary file descriptor proto data. Must not be null, and any 489 /// dependencies must come before the descriptor which depends on them. (If A depends on B, and B 490 /// depends on C, then the descriptors must be presented in the order C, B, A.) This is compatible 491 /// with the order in which protoc provides descriptors to plugins.</param> 492 /// <param name="registry">The extension registry to use when parsing, or null if no extensions are required.</param> 493 /// <returns>The file descriptors corresponding to <paramref name="descriptorData"/>.</returns> BuildFromByteStrings(IEnumerable<ByteString> descriptorData, ExtensionRegistry registry)494 public static IReadOnlyList<FileDescriptor> BuildFromByteStrings(IEnumerable<ByteString> descriptorData, ExtensionRegistry registry) 495 { 496 ProtoPreconditions.CheckNotNull(descriptorData, nameof(descriptorData)); 497 498 var parser = FileDescriptorProto.Parser.WithExtensionRegistry(registry); 499 500 // TODO: See if we can build a single DescriptorPool instead of building lots of them. 501 // This will all behave correctly, but it's less efficient than we'd like. 502 var descriptors = new List<FileDescriptor>(); 503 var descriptorsByName = new Dictionary<string, FileDescriptor>(); 504 foreach (var data in descriptorData) 505 { 506 var proto = parser.ParseFrom(data); 507 var dependencies = new List<FileDescriptor>(); 508 foreach (var dependencyName in proto.Dependency) 509 { 510 FileDescriptor dependency; 511 if (!descriptorsByName.TryGetValue(dependencyName, out dependency)) 512 { 513 throw new ArgumentException($"Dependency missing: {dependencyName}"); 514 } 515 dependencies.Add(dependency); 516 } 517 var pool = new DescriptorPool(dependencies); 518 FileDescriptor descriptor = new FileDescriptor( 519 data, proto, dependencies, pool, 520 allowUnknownDependencies: false, generatedCodeInfo: null); 521 descriptor.CrossLink(); 522 descriptors.Add(descriptor); 523 if (descriptorsByName.ContainsKey(descriptor.Name)) 524 { 525 throw new ArgumentException($"Duplicate descriptor name: {descriptor.Name}"); 526 } 527 descriptorsByName.Add(descriptor.Name, descriptor); 528 } 529 return new ReadOnlyCollection<FileDescriptor>(descriptors); 530 } 531 532 /// <summary> 533 /// Converts the given descriptor binary data into FileDescriptor objects. 534 /// Note: reflection using the returned FileDescriptors is not currently supported. 535 /// </summary> 536 /// <param name="descriptorData">The binary file descriptor proto data. Must not be null, and any 537 /// dependencies must come before the descriptor which depends on them. (If A depends on B, and B 538 /// depends on C, then the descriptors must be presented in the order C, B, A.) This is compatible 539 /// with the order in which protoc provides descriptors to plugins.</param> 540 /// <returns>The file descriptors corresponding to <paramref name="descriptorData"/>.</returns> 541 public static IReadOnlyList<FileDescriptor> BuildFromByteStrings(IEnumerable<ByteString> descriptorData) => 542 BuildFromByteStrings(descriptorData, null); 543 544 /// <summary> 545 /// Returns a <see cref="System.String" /> that represents this instance. 546 /// </summary> 547 /// <returns> 548 /// A <see cref="System.String" /> that represents this instance. 549 /// </returns> ToString()550 public override string ToString() 551 { 552 return $"FileDescriptor for {Name}"; 553 } 554 555 /// <summary> 556 /// Returns the file descriptor for descriptor.proto. 557 /// </summary> 558 /// <remarks> 559 /// This is used for protos which take a direct dependency on <c>descriptor.proto</c>, typically for 560 /// annotations. While <c>descriptor.proto</c> is a proto2 file, it is built into the Google.Protobuf 561 /// runtime for reflection purposes. The messages are internal to the runtime as they would require 562 /// proto2 semantics for full support, but the file descriptor is available via this property. The 563 /// C# codegen in protoc automatically uses this property when it detects a dependency on <c>descriptor.proto</c>. 564 /// </remarks> 565 /// <value> 566 /// The file descriptor for <c>descriptor.proto</c>. 567 /// </value> 568 public static FileDescriptor DescriptorProtoFileDescriptor { get { return DescriptorReflection.Descriptor; } } 569 570 /// <summary> 571 /// The (possibly empty) set of custom options for this file. 572 /// </summary> 573 [Obsolete("CustomOptions are obsolete. Use the GetOptions() method.")] 574 public CustomOptions CustomOptions => new CustomOptions(Proto.Options?._extensions?.ValuesByNumber); 575 576 /// <summary> 577 /// The <c>FileOptions</c>, defined in <c>descriptor.proto</c>. 578 /// If the options message is not present (i.e. there are no options), <c>null</c> is returned. 579 /// Custom options can be retrieved as extensions of the returned message. 580 /// NOTE: A defensive copy is created each time this property is retrieved. 581 /// </summary> GetOptions()582 public FileOptions GetOptions() => Proto.Options?.Clone(); 583 584 /// <summary> 585 /// Gets a single value file option for this descriptor 586 /// </summary> 587 [Obsolete("GetOption is obsolete. Use the GetOptions() method.")] GetOption(Extension<FileOptions, T> extension)588 public T GetOption<T>(Extension<FileOptions, T> extension) 589 { 590 var value = Proto.Options.GetExtension(extension); 591 return value is IDeepCloneable<T> ? (value as IDeepCloneable<T>).Clone() : value; 592 } 593 594 /// <summary> 595 /// Gets a repeated value file option for this descriptor 596 /// </summary> 597 [Obsolete("GetOption is obsolete. Use the GetOptions() method.")] GetOption(RepeatedExtension<FileOptions, T> extension)598 public RepeatedField<T> GetOption<T>(RepeatedExtension<FileOptions, T> extension) 599 { 600 return Proto.Options.GetExtension(extension).Clone(); 601 } 602 603 /// <summary> 604 /// Performs initialization for the given generic type argument. 605 /// </summary> 606 /// <remarks> 607 /// This method is present for the sake of AOT compilers. It allows code (whether handwritten or generated) 608 /// to make calls into the reflection machinery of this library to express an intention to use that type 609 /// reflectively (e.g. for JSON parsing and formatting). The call itself does almost nothing, but AOT compilers 610 /// attempting to determine which generic type arguments need to be handled will spot the code path and act 611 /// accordingly. 612 /// </remarks> 613 /// <typeparam name="T">The type to force initialization for.</typeparam> ForceReflectionInitialization()614 public static void ForceReflectionInitialization<T>() => ReflectionUtil.ForceInitialize<T>(); 615 } 616 } 617