We'd get another SigilVerificationException at runtime - this time on line #4 "Add expected a by ref, double, float, int, long, native int, or pointer found System.RuntimeTypeHandle". If instead we "corrected" the example to 1: var e = Emit.NewDynamicMethod("foo") "Correcting" that to 1: var e = Emit.NewDynamicMethod("foo") ĭoes compile, but throws a SigilVerificationException at runtime on line #3 "Add expects 2 values on the stack". This fails to compile, as Add() takes no parameters. The previous example would be rendered as: 1: var e = Emit.NewDynamicMethod("foo") KM: Sigil provides a less error prone interface, and (by default) validates the IL stream as it is emitted. InfoQ: How does Sigil make IL Generation easier?
The raised exception will also lack detail, rarely saying more than "Common Language Runtime detected an invalid program." or "Operation could destabilize the runtime.".Īnother, less significant, difficulty is generating ideal IL such as using shorter forms when possible (Br_S instead of Br, Ldc_I4_0 instead of Ldc_I4 & 0, etc.), and reusing locals (rather than use additional slots on the stack). This typically means the first time a delegate or method is invoked (line #8 in the above example), which can be very far removed from the actual mistake. The second issue with ILGenerator is that it does no validation of the correctness of the emitted IL, correctness is only checked when the JIT gets it. Most opcodes in ILGenerator are emitted with a call to one of Emit's overloads, the problem is that Emit(.) does no checking that the instruction is actual sensible.įor example 1: var dyn = new DynamicMethod("foo", typeof(void), new Type) ħ: var del = (Action)dyn.CreateDelegate(typeof(Action)) ĭoesn't fail to compile, even though line #4 is nonsensical (the Add opcode takes no immediate values). KM: When using ILGenerator, there are two big stumbling blocks. InfoQ: What are the challenges of emitting IL at runtime? * Generic types, in the absence of constraints, could be either structs or reference types which has to be reflected in the emitted IL and handled in validation. Unbound generic types complicate validation and generation*, and since in the typical case all types are known when using Sigil there's been little demand for to support them. Fault blocks aren't really a thing in C#, and aren't legal in DynamicMethod anyway.
Sigil generator code#
There are a few use cases Sigil doesn't support (which are possible with ILGenerator): fault blocks and emitting code that uses generic (ie.
for which reflection is often used can benefit from Sigil/ILGenerator. Kevin Montrose: Typically IL is generated as a faster alternative to reflection, so tasks like (de)serialization, type mapping, mocking, etc. InfoQ: What are the most suited scenarios for generating IL? Is it possible to replace any piece of code using reflection with ILGenerator/Sigil? InfoQ reached out with Sigil's creator Kevin Montrose, team lead at StackOverflow, to get a better understanding of ILGenerator and Sigil. It wraps ILGenerator in a finer-grained interface, automates some optimizations and provides validations for the generated IL.
Sigil is a library for generating Common Intermediate Language (CIL).