Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix to allow AOT compilers to play nicely with reflection #4559

Merged
merged 1 commit into from Apr 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 24 additions & 0 deletions csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -334,5 +345,18 @@ public override string ToString()
/// 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>();
}
}
41 changes: 23 additions & 18 deletions csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs
Expand Up @@ -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>
Expand Down Expand Up @@ -163,7 +186,6 @@ private static bool CheckCanConvertEnumFuncToInt32Func()
{
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.
Expand All @@ -176,23 +198,6 @@ private static bool CheckCanConvertEnumFuncToInt32Func()
}
}

/// <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
Expand Down