Skip to content

Commit

Permalink
Code sharing between generic methods in member accessor (dotnet#36710)
Browse files Browse the repository at this point in the history
* Code sharing between generic methods in member accessor

* Removed redudant value type check
  • Loading branch information
YohDeadfall authored May 20, 2020
1 parent d4b356e commit 43cad9b
Showing 1 changed file with 61 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#if NETFRAMEWORK || NETCOREAPP
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
Expand Down Expand Up @@ -56,10 +57,14 @@ internal sealed class ReflectionEmitMemberAccessor : MemberAccessor
return (JsonClassInfo.ConstructorDelegate)dynamicMethod.CreateDelegate(typeof(JsonClassInfo.ConstructorDelegate));
}

public override JsonClassInfo.ParameterizedConstructorDelegate<T>? CreateParameterizedConstructor<T>(ConstructorInfo constructor)
public override JsonClassInfo.ParameterizedConstructorDelegate<T>? CreateParameterizedConstructor<T>(ConstructorInfo constructor) =>
CreateDelegate<JsonClassInfo.ParameterizedConstructorDelegate<T>>(CreateParameterizedConstructor(constructor));

private static DynamicMethod? CreateParameterizedConstructor(ConstructorInfo constructor)
{
Type type = typeof(T);
Type? type = constructor.DeclaringType;

Debug.Assert(type != null);
Debug.Assert(!type.IsAbstract);
Debug.Assert(type.GetConstructors(BindingFlags.Public | BindingFlags.Instance).Contains(constructor));

Expand Down Expand Up @@ -87,87 +92,73 @@ internal sealed class ReflectionEmitMemberAccessor : MemberAccessor
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldc_I4_S, i);
generator.Emit(OpCodes.Ldelem_Ref);

if (paramType.IsValueType)
{
generator.Emit(OpCodes.Unbox_Any, paramType);
}
else
{
generator.Emit(OpCodes.Castclass, paramType);
};
generator.Emit(OpCodes.Unbox_Any, paramType);
}

generator.Emit(OpCodes.Newobj, constructor);
generator.Emit(OpCodes.Ret);

return (JsonClassInfo.ParameterizedConstructorDelegate<T>)dynamicMethod.CreateDelegate(typeof(JsonClassInfo.ParameterizedConstructorDelegate<T>));
return dynamicMethod;
}

public override JsonClassInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, TArg2, TArg3>?
CreateParameterizedConstructor<T, TArg0, TArg1, TArg2, TArg3>(ConstructorInfo constructor)
CreateParameterizedConstructor<T, TArg0, TArg1, TArg2, TArg3>(ConstructorInfo constructor) =>
CreateDelegate<JsonClassInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, TArg2, TArg3>>(
CreateParameterizedConstructor(constructor, typeof(TArg0), typeof(TArg1), typeof(TArg2), typeof(TArg3)));

private static DynamicMethod? CreateParameterizedConstructor(ConstructorInfo constructor, Type parameterType1, Type parameterType2, Type parameterType3, Type parameterType4)
{
Type type = typeof(T);
Type? type = constructor.DeclaringType;

Debug.Assert(type != null);
Debug.Assert(!type.IsAbstract);
Debug.Assert(type.GetConstructors(BindingFlags.Public | BindingFlags.Instance).Contains(constructor));

ParameterInfo[] parameters = constructor.GetParameters();
int parameterCount = parameters.Length;

Debug.Assert(parameterCount <= JsonConstants.UnboxedParameterCountThreshold);

var dynamicMethod = new DynamicMethod(
ConstructorInfo.ConstructorName,
type,
new[] { typeof(TArg0), typeof(TArg1), typeof(TArg2), typeof(TArg3) },
new[] { parameterType1, parameterType2, parameterType3, parameterType4 },
typeof(ReflectionEmitMemberAccessor).Module,
skipVisibility: true);

ILGenerator generator = dynamicMethod.GetILGenerator();

for (int index = 0; index < parameterCount; index++)
{
switch (index)
{
case 0:
generator.Emit(OpCodes.Ldarg_0);
break;
case 1:
generator.Emit(OpCodes.Ldarg_1);
break;
case 2:
generator.Emit(OpCodes.Ldarg_2);
break;
case 3:
generator.Emit(OpCodes.Ldarg_3);
break;
default:
Debug.Fail("We shouldn't be here if there are more than 4 parameters.");
throw new InvalidOperationException();
}
Debug.Assert(index <= JsonConstants.UnboxedParameterCountThreshold);

generator.Emit(
index switch
{
0 => OpCodes.Ldarg_0,
1 => OpCodes.Ldarg_1,
2 => OpCodes.Ldarg_2,
3 => OpCodes.Ldarg_3,
_ => throw new InvalidOperationException()
});
}

generator.Emit(OpCodes.Newobj, constructor);
generator.Emit(OpCodes.Ret);

return (JsonClassInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, TArg2, TArg3>)
dynamicMethod.CreateDelegate(
typeof(JsonClassInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, TArg2, TArg3>));
return dynamicMethod;
}

public override Action<TCollection, object?> CreateAddMethodDelegate<TCollection>()
{
Type collectionType = typeof(TCollection);
Type elementType = typeof(object);
public override Action<TCollection, object?> CreateAddMethodDelegate<TCollection>() =>
CreateDelegate<Action<TCollection, object?>>(CreateAddMethodDelegate(typeof(TCollection)));

private static DynamicMethod CreateAddMethodDelegate(Type collectionType)
{
// We verified this won't be null when we created the converter that calls this method.
MethodInfo realMethod = (collectionType.GetMethod("Push") ?? collectionType.GetMethod("Enqueue"))!;

var dynamicMethod = new DynamicMethod(
realMethod.Name,
typeof(void),
new[] { collectionType, elementType },
new[] { collectionType, typeof(object) },
typeof(ReflectionEmitMemberAccessor).Module,
skipVisibility: true);

Expand All @@ -178,18 +169,21 @@ public override JsonClassInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1,
generator.Emit(OpCodes.Callvirt, realMethod);
generator.Emit(OpCodes.Ret);

return (Action<TCollection, object?>)dynamicMethod.CreateDelegate(typeof(Action<TCollection, object?>));
return dynamicMethod;
}

public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TElement, TCollection>()
public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TElement, TCollection>() =>
CreateDelegate<Func<IEnumerable<TElement>, TCollection>>(
CreateImmutableEnumerableCreateRangeDelegate(typeof(TCollection), typeof(TElement), typeof(IEnumerable<TElement>)));

private static DynamicMethod CreateImmutableEnumerableCreateRangeDelegate(Type collectionType, Type elementType, Type enumerableType)
{
Type collectionType = typeof(TCollection);
MethodInfo realMethod = collectionType.GetImmutableEnumerableCreateRangeMethod(typeof(TElement));
MethodInfo realMethod = collectionType.GetImmutableEnumerableCreateRangeMethod(elementType);

var dynamicMethod = new DynamicMethod(
realMethod.Name,
collectionType,
new[] { typeof(IEnumerable<TElement>) },
new[] { enumerableType },
typeof(ReflectionEmitMemberAccessor).Module,
skipVisibility: true);

Expand All @@ -199,18 +193,21 @@ public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerab
generator.Emit(OpCodes.Call, realMethod);
generator.Emit(OpCodes.Ret);

return (Func<IEnumerable<TElement>, TCollection>)dynamicMethod.CreateDelegate(typeof(Func<IEnumerable<TElement>, TCollection>));
return dynamicMethod;
}

public override Func<IEnumerable<KeyValuePair<string, TElement>>, TCollection> CreateImmutableDictionaryCreateRangeDelegate<TElement, TCollection>()
public override Func<IEnumerable<KeyValuePair<string, TElement>>, TCollection> CreateImmutableDictionaryCreateRangeDelegate<TElement, TCollection>() =>
CreateDelegate<Func<IEnumerable<KeyValuePair<string, TElement>>, TCollection>>(
CreateImmutableDictionaryCreateRangeDelegate(typeof(TCollection), typeof(TElement), typeof(IEnumerable<KeyValuePair<string, TElement>>)));

private static DynamicMethod CreateImmutableDictionaryCreateRangeDelegate(Type collectionType, Type elementType, Type enumerableType)
{
Type collectionType = typeof(TCollection);
MethodInfo realMethod = collectionType.GetImmutableDictionaryCreateRangeMethod(typeof(TElement));
MethodInfo realMethod = collectionType.GetImmutableDictionaryCreateRangeMethod(elementType);

var dynamicMethod = new DynamicMethod(
realMethod.Name,
collectionType,
new[] { typeof(IEnumerable<KeyValuePair<string, TElement>>) },
new[] { enumerableType },
typeof(ReflectionEmitMemberAccessor).Module,
skipVisibility: true);

Expand All @@ -220,13 +217,13 @@ public override Func<IEnumerable<KeyValuePair<string, TElement>>, TCollection> C
generator.Emit(OpCodes.Call, realMethod);
generator.Emit(OpCodes.Ret);

return (Func<IEnumerable<KeyValuePair<string, TElement>>, TCollection>)dynamicMethod.CreateDelegate(typeof(Func<IEnumerable<KeyValuePair<string, TElement>>, TCollection>));
return dynamicMethod;
}

public override Func<object?, TProperty> CreatePropertyGetter<TProperty>(PropertyInfo propertyInfo) =>
(Func<object?, TProperty>)CreatePropertyGetter(propertyInfo, propertyInfo.DeclaringType!, typeof(TProperty));
CreateDelegate<Func<object?, TProperty>>(CreatePropertyGetter(propertyInfo, propertyInfo.DeclaringType!, typeof(TProperty)));

private static Delegate CreatePropertyGetter(PropertyInfo propertyInfo, Type classType, Type propertyType)
private static DynamicMethod CreatePropertyGetter(PropertyInfo propertyInfo, Type classType, Type propertyType)
{
MethodInfo? realMethod = propertyInfo.GetMethod;
Type objectType = typeof(object);
Expand Down Expand Up @@ -256,13 +253,13 @@ private static Delegate CreatePropertyGetter(PropertyInfo propertyInfo, Type cla

generator.Emit(OpCodes.Ret);

return dynamicMethod.CreateDelegate(typeof(Func<,>).MakeGenericType(objectType, propertyType));
return dynamicMethod;
}

public override Action<object?, TProperty> CreatePropertySetter<TProperty>(PropertyInfo propertyInfo) =>
(Action<object?, TProperty>)CreatePropertySetter(propertyInfo, propertyInfo.DeclaringType!, typeof(TProperty));
CreateDelegate<Action<object?, TProperty>>(CreatePropertySetter(propertyInfo, propertyInfo.DeclaringType!, typeof(TProperty)));

private static Delegate CreatePropertySetter(PropertyInfo propertyInfo, Type classType, Type propertyType)
private static DynamicMethod CreatePropertySetter(PropertyInfo propertyInfo, Type classType, Type propertyType)
{
MethodInfo? realMethod = propertyInfo.SetMethod;
Type objectType = typeof(object);
Expand Down Expand Up @@ -294,8 +291,12 @@ private static Delegate CreatePropertySetter(PropertyInfo propertyInfo, Type cla

generator.Emit(OpCodes.Ret);

return dynamicMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(objectType, propertyType));
return dynamicMethod;
}

[return: NotNullIfNotNull("method")]
private static T? CreateDelegate<T>(DynamicMethod? method) where T : Delegate =>
(T?)method?.CreateDelegate(typeof(T));
}
}
#endif

0 comments on commit 43cad9b

Please sign in to comment.