Skip to content

Commit fa97ab1

Browse files
author
maximv
committed
fixed: #310 Ensure that FEC.LightExpression.ILGeneratorHacks does not crash if some method is not reflected
1 parent bf6328d commit fa97ab1

File tree

1 file changed

+55
-40
lines changed

1 file changed

+55
-40
lines changed

src/DryIoc/FastExpressionCompiler.cs

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4620,12 +4620,11 @@ public static T GetFirst<T>(this T[] source)
46204620
}
46214621
}
46224622

4623-
/// Hey
4623+
/// <summary>Reflecting the internal methods to access the more performant for defining the local variable</summary>
46244624
public static class ILGeneratorHacks
46254625
{
4626+
// The original ILGenerator methods we are trying to hack without allocating the `LocalBuilder`
46264627
/*
4627-
// Original methods from ILGenerator.cs
4628-
46294628
public virtual LocalBuilder DeclareLocal(Type localType)
46304629
{
46314630
return this.DeclareLocal(localType, false);
@@ -4649,56 +4648,72 @@ public virtual LocalBuilder DeclareLocal(Type localType, bool pinned)
46494648
}
46504649
*/
46514650

4652-
/// Not allocating the LocalBuilder class
4653-
/// emitting this:
4654-
/// il.m_localSignature.AddArgument(type);
4655-
/// return PostInc(ref il.LocalCount);
4656-
public static Func<ILGenerator, Type, int> CompileGetNextLocalVarIndex()
4651+
private static readonly Func<ILGenerator, Type, int> _getNextLocalVarIndex;
4652+
4653+
internal static int PostInc(ref int i) => i++;
4654+
4655+
static ILGeneratorHacks()
46574656
{
4658-
if (LocalCountField == null || LocalSignatureField == null || AddArgumentMethod == null)
4659-
return (i, t) => i.DeclareLocal(t).LocalIndex;
4657+
// the default allocatee method
4658+
_getNextLocalVarIndex = (il, type) => il.DeclareLocal(type).LocalIndex;
4659+
4660+
// now let's try to acquire the more efficient less allocating method
4661+
var ilGenTypeInfo = typeof(ILGenerator).GetTypeInfo();
4662+
var localSignatureField = ilGenTypeInfo.GetDeclaredField("m_localSignature");
4663+
if (localSignatureField == null)
4664+
return;
4665+
4666+
var localCountField = ilGenTypeInfo.GetDeclaredField("m_localCount");
4667+
if (localCountField == null)
4668+
return;
4669+
4670+
// looking for the `SignatureHelper.AddArgument(Type argument, bool pinned)`
4671+
MethodInfo addArgumentMethod = null;
4672+
foreach (var m in typeof(SignatureHelper).GetTypeInfo().GetDeclaredMethods("AddArgument"))
4673+
{
4674+
var ps = m.GetParameters();
4675+
if (ps.Length == 2 && ps[0].ParameterType == typeof(Type) && ps[1].ParameterType == typeof(bool))
4676+
{
4677+
addArgumentMethod = m;
4678+
break;
4679+
}
4680+
}
46604681

4661-
var method = new DynamicMethod(string.Empty,
4682+
if (addArgumentMethod == null)
4683+
return;
4684+
4685+
// our own helper - always available
4686+
var postIncMethod = typeof(ILGeneratorHacks).GetTypeInfo().GetDeclaredMethod(nameof(PostInc));
4687+
4688+
// now let's compile the following method without allocating the LocalBuilder class:
4689+
/*
4690+
il.m_localSignature.AddArgument(type);
4691+
return PostInc(ref il.LocalCount);
4692+
*/
4693+
var efficientMethod = new DynamicMethod(string.Empty,
46624694
typeof(int), new[] { typeof(ExpressionCompiler.ArrayClosure), typeof(ILGenerator), typeof(Type) },
46634695
typeof(ExpressionCompiler.ArrayClosure), skipVisibility: true);
4696+
var il = efficientMethod.GetILGenerator();
46644697

4665-
var il = method.GetILGenerator();
4666-
4667-
il.Emit(OpCodes.Ldarg_1); // load `il` argument
4668-
il.Emit(OpCodes.Ldfld, LocalSignatureField);
4669-
il.Emit(OpCodes.Ldarg_2); // load `type` argument
4698+
// emitting `il.m_localSignature.AddArgument(type);`
4699+
il.Emit(OpCodes.Ldarg_1); // load `il` argument (arg_0 is the empty closure object)
4700+
il.Emit(OpCodes.Ldfld, localSignatureField);
4701+
il.Emit(OpCodes.Ldarg_2); // load `type` argument
46704702
il.Emit(OpCodes.Ldc_I4_0); // load `pinned: false` argument
4671-
il.Emit(OpCodes.Call, AddArgumentMethod);
4703+
il.Emit(OpCodes.Call, addArgumentMethod);
46724704

4705+
// emitting `return PostInc(ref il.LocalCount);`
46734706
il.Emit(OpCodes.Ldarg_1); // load `il` argument
4674-
il.Emit(OpCodes.Ldflda, LocalCountField);
4675-
il.Emit(OpCodes.Call, PostIncMethod);
4707+
il.Emit(OpCodes.Ldflda, localCountField);
4708+
il.Emit(OpCodes.Call, postIncMethod);
46764709

46774710
il.Emit(OpCodes.Ret);
46784711

4679-
return (Func<ILGenerator, Type, int>)method.CreateDelegate(typeof(Func<ILGenerator, Type, int>), ExpressionCompiler.EmptyArrayClosure);
4712+
_getNextLocalVarIndex = (Func<ILGenerator, Type, int>)efficientMethod.CreateDelegate(
4713+
typeof(Func<ILGenerator, Type, int>), ExpressionCompiler.EmptyArrayClosure);
46804714
}
46814715

4682-
internal static int PostInc(ref int i) => i++;
4683-
4684-
public static readonly MethodInfo PostIncMethod = typeof(ILGeneratorHacks).GetTypeInfo()
4685-
.GetDeclaredMethod(nameof(PostInc));
4686-
4687-
/// Get via reflection
4688-
public static readonly FieldInfo LocalSignatureField = typeof(ILGenerator).GetTypeInfo()
4689-
.GetDeclaredField("m_localSignature");
4690-
4691-
/// Get via reflection
4692-
public static readonly FieldInfo LocalCountField = typeof(ILGenerator).GetTypeInfo()
4693-
.GetDeclaredField("m_localCount");
4694-
4695-
/// Get via reflection
4696-
public static readonly MethodInfo AddArgumentMethod = typeof(SignatureHelper).GetTypeInfo()
4697-
.GetDeclaredMethods(nameof(SignatureHelper.AddArgument)).First(m => m.GetParameters().Length == 2);
4698-
4699-
private static readonly Func<ILGenerator, Type, int> _getNextLocalVarIndex = CompileGetNextLocalVarIndex();
4700-
4701-
/// Does the job
4716+
/// <summary>Efficiently returns the next variable index, hopefully without unnecessary allocations.</summary>
47024717
public static int GetNextLocalVarIndex(this ILGenerator il, Type t) => _getNextLocalVarIndex(il, t);
47034718
}
47044719

0 commit comments

Comments
 (0)