다음을 통해 공유


Always Peverify IL Code of your Dynamic Method

Current dynamic method implementation does not have built-in support to pre-check whether the dynamic method code is verifiable. With bad IL sequence, very often you will get "System.InvalidProgramException: Common Language Runtime detected an invalid program" when it gets executed. See the code example below, where C1, C2 are 2 simple reference types. Btw I understand only me write such "fun" IL code.

LocalBuilder lb = ilgen.DeclareLocal(typeof(C2));
ilgen.Emit(OpCodes.Newobj, typeof(C1).GetConstructor(Type.EmptyTypes));
ilgen.Emit(OpCodes.Stloc_0, lb);
ilgen.Emit(OpCodes.Ldc_I4_0);
ilgen.EmitWriteLine("I got called");
ilgen.Emit(OpCodes.Ret);

Even if you did not get InvalidProgramException, it does not mean the code-gen is correct. When the following code get invoked, "I got called too" will be printed; but the code is not verifiable. When the SkipVerification permission is refused, executing this dynamic method causes JIT to verify first: "System.Security.VerficationException: Operation could destabilize the runtime" throws.

LocalBuilder lb = ilgen.DeclareLocal(typeof(C2));
ilgen.Emit(OpCodes.Newobj, typeof(C1).GetConstructor(Type.EmptyTypes));
ilgen.Emit(OpCodes.Stloc_0, lb);
ilgen.EmitWriteLine("I got called too");
ilgen.Emit(OpCodes.Ret);

However both exception messages contain no much useful information: they do not tell which instruction could be blamed. For any production code using DynamicMethod, to ensure the code-gen quality, it is a good practice to have another implementation which have Reflection.Emit to emit the same IL code to disk, and call Peverify.exe to verify it. IronPython's code-gen has both approaches: at the beginning when we enforced this check, we caught several code-gen issues. This is what peverify.exe shows for the 2 scenarios above:

[IL]: Error: [D:\snippets.dll : Sample::M][offset 0x00000005][found ref 'C1'][expected ref 'C2'] Unexpected type on the stack.
[IL]: Error: [D:\snippets.dll : Sample::M][offset 0x00000011] Stack must be empty on return from a void function.
----
[IL]: Error: [D:\snippets.dll : Sample::M][offset 0x00000005][found ref 'C1'][expected ref 'C2'] Unexpected type on the stack.