-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
System.Reflection.Emit: ILGenerator.Emit(OpCodes.Ldtoken, <MethodInfo>) produces incorrect metadata for methods with C# 7.2 in
parameters in generic types
#25958
Comments
Shame this issue missed the 2.1.0 bus. It will become increasingly difficult to use any sort of mocking framework as |
Both the CLR and Core CLR versions of System.Reflection.Emit do not reproduce method signatures correctly if they include a modreq (such as is caused by a C# 7.2 `in` parameter modifier) and if the method, its declaring type, or both are generic. See dotnet/corefx#29254.
@atsushikan - I'd like to give this a try. Would you accept a PR? |
Sure, go for it. |
Just a quick note about this issue's current state: it is still open because the PR which was supposed to fix this (dotnet/coreclr#17881) caused a regression (https://github.com/dotnet/coreclr/issues/18034) and because of that, was reverted (dotnet/coreclr#18036). No attempt has been made since to provide another patch for this problem. |
I think I am having this problem for FSI (F# Interactive). I discovered this while working on this PR: dotnet/fsharp#6213 ; I built a test on FSI uses System.Reflection.Emit to emit IL dynamically. This means F# interactive will fail on a call to a method that has modreq/modopt in any part of its signature. Is this likely to be fixed by .NET Core 3.0? |
@steveharter , will this make the bar for .NET Core 3.0? @terrajobst told me to ask you :) |
Proposing moving to 3.0 unless someone picks this up. |
@steveharter I assume you mean moving out of 3.0. I'll move it (since it's not necessary to ship 3.0) but for now there is still a little room to fix non 3.0 bugs. |
Had a look into this and found what was causing the regression in dotnet/coreclr#18034
(Am I allowed to say all this code is kind of a mess? :P) @stakx If this wouldn't work, or you want to do it, let me know. But otherwise I'll resurrect your PR with that one change. At some point. When I get around to it. |
@hamish-milne, I touched this code once but I'm certainly not territorial about it 😄. If you think you can fix this, I'll be happy to defer to you. Please do reuse whatever seems useful from my old PR. It would be great to finally see this fixed! P.S.: I didn't realise I was still assigned to this issue after all that time 😲, I haven't looked at this code closely for many months! Unassigned now. |
Waiting for some fix about this: dotnet/runtime#25958
Signature generation now includes CustomModifiers MethodBuilder and ConstructorBuilder passes their SignatureHelper for generation Fixes dotnet#25958
Changing milestone to 6.0 for risk (5.0 is almost in ask-mode and no more previews); see the PR for more information. cc @GrabYourPitchforks |
@steveharter Just to check, is it likely that #40587 fixes this problem dotnet/fsharp#9401 (comment) That is, a Reflection emit call to |
Ok there was a mistake in my IL. It works =) var a = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("aa"), AssemblyBuilderAccess.Run);
var m = a.DefineDynamicModule("MyDynamicAsm");
var t = m.DefineType("MyDynamicType", TypeAttributes.Public);
var mb = t.DefineMethod("foo", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(char), new Type[] { typeof(string) });
var ilg = mb.GetILGenerator();
var minfo1 = typeof(MemoryExtensions).GetMethod("AsSpan", new Type[] { typeof(string) });
var minfo2 = typeof(ReadOnlySpan<char>).GetMethod("get_Item", BindingFlags.Public | BindingFlags.Instance);
LocalBuilder myLB1 = ilg.DeclareLocal(typeof(ReadOnlySpan<char>));
ilg.Emit(OpCodes.Ldarg_0);
ilg.Emit(OpCodes.Call, minfo1);
ilg.Emit(OpCodes.Stloc_0);
ilg.Emit(OpCodes.Ldloca_S, 0);
ilg.Emit(OpCodes.Ldc_I4_0);
ilg.Emit(OpCodes.Call, minfo2);
ilg.Emit(OpCodes.Ldind_U2);
ilg.Emit(OpCodes.Ret);
t.CreateType();
var m2 = t.GetMethod("foo");
byte[] il = m2.GetMethodBody().GetILAsByteArray();
int ilMethodMetadataToken = BitConverter.ToInt32(il, 2);
MethodBase resolvedMethod = t.Module.ResolveMethod(ilMethodMetadataToken);
Assert.Equal(minfo1, resolvedMethod);
int ilMethodMetadataToken2 = BitConverter.ToInt32(il, 14);
MethodBase resolvedMethod2 = t.Module.ResolveMethod(ilMethodMetadataToken2);
Assert.Equal(minfo2, resolvedMethod2);
var c = (char)m2.Invoke(null, new object[] { "abc" });
Assert.Equal('a', c); |
@TAGC recently noted in castleproject/Core#339 (comment) that DynamicProxy-based mocking libraries (e.g. Moq, NSubstitute) appear to have problems mocking the following generic interface:
Mocking such a type will typically result in a
MissingMethodException
. It only seems to occur with generic types/methods, and in the presence of a C# 7.2in
parameter modifier.I've been able to track this down to what appears to be a bug in System.Reflection.Emit. A brief description follows below; if more detail or code is required, I can probably provide it... please ask.
Given this C# 7.2+ interface:
(Expand to see the equivalent type definition in IL.)
DynamicProxy would need to generate the following IL instruction sequence using a
System.Reflection.Emit.ILGenerator
(because it cachesMethodInfo
for proxied methods):This is how one would supposedly do it with
ILGenerator
:I've verified (by saving the dynamic assembly to disk using the .NET Framework 4.7, then dumping it with ILDASM) that the following IL is generated instead:
Note the absence of the
modreq
in the first instruction's method reference. This will result in aMissingMethodException
(because method signatures don't match) when the generated code is run:The same
MissingMethodException
occurs with .NET Core 2.0, so I suspect it inherited the exact same problem from the .NET Framework. If you'd rather have repro code for .NET Core, you'll find it below.(Expand for an error reproduction on .NET Core.)
Note again that this problem only occurs for methods in generic types/methods, and in the presence of an
in
parameter modifier./cc @jonorossi (for DynamicProxy), @zvirja (for NSubstitute), @thomaslevesque (for FakeItEasy)
The text was updated successfully, but these errors were encountered: