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