diff options
-rw-r--r-- | csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs | 24 | ||||
-rw-r--r-- | csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs | 41 |
2 files changed, 47 insertions, 18 deletions
diff --git a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs index 9124beee..be94cb10 100644 --- a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs @@ -30,6 +30,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion +using Google.Protobuf.WellKnownTypes; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -43,6 +44,16 @@ namespace Google.Protobuf.Reflection /// </summary> public sealed class FileDescriptor : IDescriptor { + // Prevent linker failures when using IL2CPP with the well-known types. + static FileDescriptor() + { + ForceReflectionInitialization<Syntax>(); + ForceReflectionInitialization<NullValue>(); + ForceReflectionInitialization<Field.Types.Cardinality>(); + ForceReflectionInitialization<Field.Types.Kind>(); + ForceReflectionInitialization<Value.KindOneofCase>(); + } + private FileDescriptor(ByteString descriptorData, FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies, GeneratedClrTypeInfo generatedCodeInfo) { SerializedData = descriptorData; @@ -334,5 +345,18 @@ namespace Google.Protobuf.Reflection /// The (possibly empty) set of custom options for this file. /// </summary> public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty; + + /// <summary> + /// Performs initialization for the given generic type argument. + /// </summary> + /// <remarks> + /// This method is present for the sake of AOT compilers. It allows code (whether handwritten or generated) + /// to make calls into the reflection machinery of this library to express an intention to use that type + /// reflectively (e.g. for JSON parsing and formatting). The call itself does almost nothing, but AOT compilers + /// attempting to determine which generic type arguments need to be handled will spot the code path and act + /// accordingly. + /// </remarks> + /// <typeparam name="T">The type to force initialization for.</typeparam> + public static void ForceReflectionInitialization<T>() => ReflectionUtil.ForceInitialize<T>(); } } diff --git a/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs b/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs index 80d5c774..18a70b80 100644 --- a/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs +++ b/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs @@ -50,6 +50,29 @@ namespace Google.Protobuf.Reflection /// </summary> internal static class ReflectionUtil { + static ReflectionUtil() + { + ForceInitialize<string>(); // Handles all reference types + ForceInitialize<int>(); + ForceInitialize<long>(); + ForceInitialize<uint>(); + ForceInitialize<ulong>(); + ForceInitialize<float>(); + ForceInitialize<double>(); + ForceInitialize<bool>(); + ForceInitialize<int?>(); + ForceInitialize<long?>(); + ForceInitialize<uint?>(); + ForceInitialize<ulong?>(); + ForceInitialize<float?>(); + ForceInitialize<double?>(); + ForceInitialize<bool?>(); + ForceInitialize<SampleEnum>(); + SampleEnumMethod(); + } + + internal static void ForceInitialize<T>() => new ReflectionHelper<IMessage, T>(); + /// <summary> /// Empty Type[] used when calling GetProperty to force property instead of indexer fetching. /// </summary> @@ -163,7 +186,6 @@ namespace Google.Protobuf.Reflection { try { - PreventLinkerFailures(); // Try to do the conversion using reflection, so we can see whether it's supported. MethodInfo method = typeof(ReflectionUtil).GetMethod(nameof(SampleEnumMethod)); // If this passes, we're in a reasonable runtime. @@ -176,23 +198,6 @@ namespace Google.Protobuf.Reflection } } - /// <summary> - /// This method is effectively pointless, but only called once. It's present (and called) - /// to avoid the Unity linker from removing code that's only called via reflection. - /// </summary> - private static void PreventLinkerFailures() - { - // Exercise the method directly. This should avoid any pro-active linkers from stripping - // the method out. - SampleEnum x = SampleEnumMethod(); - if (x != SampleEnum.X) - { - throw new InvalidOperationException("Failure in reflection utilities"); - } - // Make sure the ReflectionHelper parameterless constructor isn't removed... - var helper = new ReflectionHelper<int, int>(); - } - public enum SampleEnum { X |