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 /// The syntax of the file 254 /// </summary> 255 public Syntax Syntax { get; } 256 257 /// <value> 258 /// The file name. 259 /// </value> 260 public string Name => Proto.Name; 261 262 /// <summary> 263 /// The package as declared in the .proto file. This may or may not 264 /// be equivalent to the .NET namespace of the generated classes. 265 /// </summary> 266 public string Package => Proto.Package; 267 268 /// <value> 269 /// Unmodifiable list of top-level message types declared in this file. 270 /// </value> 271 public IList<MessageDescriptor> MessageTypes { get; } 272 273 /// <value> 274 /// Unmodifiable list of top-level enum types declared in this file. 275 /// </value> 276 public IList<EnumDescriptor> EnumTypes { get; } 277 278 /// <value> 279 /// Unmodifiable list of top-level services declared in this file. 280 /// </value> 281 public IList<ServiceDescriptor> Services { get; } 282 283 /// <summary> 284 /// Unmodifiable list of top-level extensions declared in this file. 285 /// Note that some extensions may be incomplete (FieldDescriptor.Extension may be null) 286 /// if this descriptor was generated using a version of protoc that did not fully 287 /// support extensions in C#. 288 /// </summary> 289 public ExtensionCollection Extensions { get; } 290 291 /// <value> 292 /// Unmodifiable list of this file's dependencies (imports). 293 /// </value> 294 public IList<FileDescriptor> Dependencies { get; } 295 296 /// <value> 297 /// Unmodifiable list of this file's public dependencies (public imports). 298 /// </value> 299 public IList<FileDescriptor> PublicDependencies { get; } 300 301 /// <value> 302 /// The original serialized binary form of this descriptor. 303 /// </value> 304 public ByteString SerializedData { get; } 305 306 /// <value> 307 /// Implementation of IDescriptor.FullName - just returns the same as Name. 308 /// </value> 309 string IDescriptor.FullName => Name; 310 311 /// <value> 312 /// Implementation of IDescriptor.File - just returns this descriptor. 313 /// </value> 314 FileDescriptor IDescriptor.File => this; 315 316 /// <value> 317 /// Pool containing symbol descriptors. 318 /// </value> 319 internal DescriptorPool DescriptorPool { get; } 320 321 /// <summary> 322 /// Finds a type (message, enum, service or extension) in the file by name. Does not find nested types. 323 /// </summary> 324 /// <param name="name">The unqualified type name to look for.</param> 325 /// <typeparam name="T">The type of descriptor to look for</typeparam> 326 /// <returns>The type's descriptor, or null if not found.</returns> 327 public T FindTypeByName<T>(String name) 328 where T : class, IDescriptor 329 { 330 // Don't allow looking up nested types. This will make optimization 331 // easier later. 332 if (name.IndexOf('.') != -1) 333 { 334 return null; 335 } 336 if (Package.Length > 0) 337 { 338 name = Package + "." + name; 339 } 340 T result = DescriptorPool.FindSymbol<T>(name); 341 if (result != null && result.File == this) 342 { 343 return result; 344 } 345 return null; 346 } 347 348 /// <summary> 349 /// Builds a FileDescriptor from its protocol buffer representation. 350 /// </summary> 351 /// <param name="descriptorData">The original serialized descriptor data. 352 /// We have only limited proto2 support, so serializing FileDescriptorProto 353 /// would not necessarily give us this.</param> 354 /// <param name="proto">The protocol message form of the FileDescriptor.</param> 355 /// <param name="dependencies">FileDescriptors corresponding to all of the 356 /// file's dependencies, in the exact order listed in the .proto file. May be null, 357 /// in which case it is treated as an empty array.</param> 358 /// <param name="allowUnknownDependencies">Whether unknown dependencies are ignored (true) or cause an exception to be thrown (false).</param> 359 /// <param name="generatedCodeInfo">Details about generated code, for the purposes of reflection.</param> 360 /// <exception cref="DescriptorValidationException">If <paramref name="proto"/> is not 361 /// a valid descriptor. This can occur for a number of reasons, such as a field 362 /// 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)363 private static FileDescriptor BuildFrom(ByteString descriptorData, FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies, GeneratedClrTypeInfo generatedCodeInfo) 364 { 365 // Building descriptors involves two steps: translating and linking. 366 // In the translation step (implemented by FileDescriptor's 367 // constructor), we build an object tree mirroring the 368 // FileDescriptorProto's tree and put all of the descriptors into the 369 // DescriptorPool's lookup tables. In the linking step, we look up all 370 // type references in the DescriptorPool, so that, for example, a 371 // FieldDescriptor for an embedded message contains a pointer directly 372 // to the Descriptor for that message's type. We also detect undefined 373 // types in the linking step. 374 if (dependencies == null) 375 { 376 dependencies = new FileDescriptor[0]; 377 } 378 379 DescriptorPool pool = new DescriptorPool(dependencies); 380 FileDescriptor result = new FileDescriptor(descriptorData, proto, dependencies, pool, allowUnknownDependencies, generatedCodeInfo); 381 382 // Validate that the dependencies we've been passed (as FileDescriptors) are actually the ones we 383 // need. 384 if (dependencies.Length != proto.Dependency.Count) 385 { 386 throw new DescriptorValidationException( 387 result, 388 "Dependencies passed to FileDescriptor.BuildFrom() don't match " + 389 "those listed in the FileDescriptorProto."); 390 } 391 392 result.CrossLink(); 393 return result; 394 } 395 CrossLink()396 private void CrossLink() 397 { 398 foreach (MessageDescriptor message in MessageTypes) 399 { 400 message.CrossLink(); 401 } 402 403 foreach (ServiceDescriptor service in Services) 404 { 405 service.CrossLink(); 406 } 407 408 Extensions.CrossLink(); 409 } 410 411 /// <summary> 412 /// Creates a descriptor for generated code. 413 /// </summary> 414 /// <remarks> 415 /// This method is only designed to be used by the results of generating code with protoc, 416 /// which creates the appropriate dependencies etc. It has to be public because the generated 417 /// code is "external", but should not be called directly by end users. 418 /// </remarks> FromGeneratedCode( byte[] descriptorData, FileDescriptor[] dependencies, GeneratedClrTypeInfo generatedCodeInfo)419 public static FileDescriptor FromGeneratedCode( 420 byte[] descriptorData, 421 FileDescriptor[] dependencies, 422 GeneratedClrTypeInfo generatedCodeInfo) 423 { 424 ExtensionRegistry registry = new ExtensionRegistry(); 425 registry.AddRange(GetAllExtensions(dependencies, generatedCodeInfo)); 426 427 FileDescriptorProto proto; 428 try 429 { 430 proto = FileDescriptorProto.Parser.WithExtensionRegistry(registry).ParseFrom(descriptorData); 431 } 432 catch (InvalidProtocolBufferException e) 433 { 434 throw new ArgumentException("Failed to parse protocol buffer descriptor for generated code.", e); 435 } 436 437 try 438 { 439 // When building descriptors for generated code, we allow unknown 440 // dependencies by default. 441 return BuildFrom(ByteString.CopyFrom(descriptorData), proto, dependencies, true, generatedCodeInfo); 442 } 443 catch (DescriptorValidationException e) 444 { 445 throw new ArgumentException($"Invalid embedded descriptor for \"{proto.Name}\".", e); 446 } 447 } 448 GetAllExtensions(FileDescriptor[] dependencies, GeneratedClrTypeInfo generatedInfo)449 private static IEnumerable<Extension> GetAllExtensions(FileDescriptor[] dependencies, GeneratedClrTypeInfo generatedInfo) 450 { 451 return dependencies.SelectMany(GetAllDependedExtensions).Distinct(ExtensionRegistry.ExtensionComparer.Instance).Concat(GetAllGeneratedExtensions(generatedInfo)); 452 } 453 GetAllGeneratedExtensions(GeneratedClrTypeInfo generated)454 private static IEnumerable<Extension> GetAllGeneratedExtensions(GeneratedClrTypeInfo generated) 455 { 456 return generated.Extensions.Concat(generated.NestedTypes.Where(t => t != null).SelectMany(GetAllGeneratedExtensions)); 457 } 458 GetAllDependedExtensions(FileDescriptor descriptor)459 private static IEnumerable<Extension> GetAllDependedExtensions(FileDescriptor descriptor) 460 { 461 return descriptor.Extensions.UnorderedExtensions 462 .Select(s => s.Extension) 463 .Where(e => e != null) 464 .Concat(descriptor.Dependencies.Concat(descriptor.PublicDependencies).SelectMany(GetAllDependedExtensions)) 465 .Concat(descriptor.MessageTypes.SelectMany(GetAllDependedExtensionsFromMessage)); 466 } 467 GetAllDependedExtensionsFromMessage(MessageDescriptor descriptor)468 private static IEnumerable<Extension> GetAllDependedExtensionsFromMessage(MessageDescriptor descriptor) 469 { 470 return descriptor.Extensions.UnorderedExtensions 471 .Select(s => s.Extension) 472 .Where(e => e != null) 473 .Concat(descriptor.NestedTypes.SelectMany(GetAllDependedExtensionsFromMessage)); 474 } 475 476 /// <summary> 477 /// Converts the given descriptor binary data into FileDescriptor objects. 478 /// Note: reflection using the returned FileDescriptors is not currently supported. 479 /// </summary> 480 /// <param name="descriptorData">The binary file descriptor proto data. Must not be null, and any 481 /// dependencies must come before the descriptor which depends on them. (If A depends on B, and B 482 /// depends on C, then the descriptors must be presented in the order C, B, A.) This is compatible 483 /// with the order in which protoc provides descriptors to plugins.</param> 484 /// <returns>The file descriptors corresponding to <paramref name="descriptorData"/>.</returns> BuildFromByteStrings(IEnumerable<ByteString> descriptorData)485 public static IReadOnlyList<FileDescriptor> BuildFromByteStrings(IEnumerable<ByteString> descriptorData) 486 { 487 ProtoPreconditions.CheckNotNull(descriptorData, nameof(descriptorData)); 488 489 // TODO: See if we can build a single DescriptorPool instead of building lots of them. 490 // This will all behave correctly, but it's less efficient than we'd like. 491 var descriptors = new List<FileDescriptor>(); 492 var descriptorsByName = new Dictionary<string, FileDescriptor>(); 493 foreach (var data in descriptorData) 494 { 495 var proto = FileDescriptorProto.Parser.ParseFrom(data); 496 var dependencies = new List<FileDescriptor>(); 497 foreach (var dependencyName in proto.Dependency) 498 { 499 FileDescriptor dependency; 500 if (!descriptorsByName.TryGetValue(dependencyName, out dependency)) 501 { 502 throw new ArgumentException($"Dependency missing: {dependencyName}"); 503 } 504 dependencies.Add(dependency); 505 } 506 var pool = new DescriptorPool(dependencies); 507 FileDescriptor descriptor = new FileDescriptor( 508 data, proto, dependencies, pool, 509 allowUnknownDependencies: false, generatedCodeInfo: null); 510 descriptor.CrossLink(); 511 descriptors.Add(descriptor); 512 if (descriptorsByName.ContainsKey(descriptor.Name)) 513 { 514 throw new ArgumentException($"Duplicate descriptor name: {descriptor.Name}"); 515 } 516 descriptorsByName.Add(descriptor.Name, descriptor); 517 } 518 return new ReadOnlyCollection<FileDescriptor>(descriptors); 519 } 520 521 /// <summary> 522 /// Returns a <see cref="System.String" /> that represents this instance. 523 /// </summary> 524 /// <returns> 525 /// A <see cref="System.String" /> that represents this instance. 526 /// </returns> ToString()527 public override string ToString() 528 { 529 return $"FileDescriptor for {Name}"; 530 } 531 532 /// <summary> 533 /// Returns the file descriptor for descriptor.proto. 534 /// </summary> 535 /// <remarks> 536 /// This is used for protos which take a direct dependency on <c>descriptor.proto</c>, typically for 537 /// annotations. While <c>descriptor.proto</c> is a proto2 file, it is built into the Google.Protobuf 538 /// runtime for reflection purposes. The messages are internal to the runtime as they would require 539 /// proto2 semantics for full support, but the file descriptor is available via this property. The 540 /// C# codegen in protoc automatically uses this property when it detects a dependency on <c>descriptor.proto</c>. 541 /// </remarks> 542 /// <value> 543 /// The file descriptor for <c>descriptor.proto</c>. 544 /// </value> 545 public static FileDescriptor DescriptorProtoFileDescriptor { get { return DescriptorReflection.Descriptor; } } 546 547 /// <summary> 548 /// The (possibly empty) set of custom options for this file. 549 /// </summary> 550 [Obsolete("CustomOptions are obsolete. Use the GetOptions() method.")] 551 public CustomOptions CustomOptions => new CustomOptions(Proto.Options?._extensions?.ValuesByNumber); 552 553 /// <summary> 554 /// The <c>FileOptions</c>, defined in <c>descriptor.proto</c>. 555 /// If the options message is not present (i.e. there are no options), <c>null</c> is returned. 556 /// Custom options can be retrieved as extensions of the returned message. 557 /// NOTE: A defensive copy is created each time this property is retrieved. 558 /// </summary> GetOptions()559 public FileOptions GetOptions() => Proto.Options?.Clone(); 560 561 /// <summary> 562 /// Gets a single value file option for this descriptor 563 /// </summary> 564 [Obsolete("GetOption is obsolete. Use the GetOptions() method.")] GetOption(Extension<FileOptions, T> extension)565 public T GetOption<T>(Extension<FileOptions, T> extension) 566 { 567 var value = Proto.Options.GetExtension(extension); 568 return value is IDeepCloneable<T> ? (value as IDeepCloneable<T>).Clone() : value; 569 } 570 571 /// <summary> 572 /// Gets a repeated value file option for this descriptor 573 /// </summary> 574 [Obsolete("GetOption is obsolete. Use the GetOptions() method.")] GetOption(RepeatedExtension<FileOptions, T> extension)575 public RepeatedField<T> GetOption<T>(RepeatedExtension<FileOptions, T> extension) 576 { 577 return Proto.Options.GetExtension(extension).Clone(); 578 } 579 580 /// <summary> 581 /// Performs initialization for the given generic type argument. 582 /// </summary> 583 /// <remarks> 584 /// This method is present for the sake of AOT compilers. It allows code (whether handwritten or generated) 585 /// to make calls into the reflection machinery of this library to express an intention to use that type 586 /// reflectively (e.g. for JSON parsing and formatting). The call itself does almost nothing, but AOT compilers 587 /// attempting to determine which generic type arguments need to be handled will spot the code path and act 588 /// accordingly. 589 /// </remarks> 590 /// <typeparam name="T">The type to force initialization for.</typeparam> ForceReflectionInitialization()591 public static void ForceReflectionInitialization<T>() => ReflectionUtil.ForceInitialize<T>(); 592 } 593 } 594