Subterranean IL: Filter exception handlers

Posted by Simon Cooper on Simple Talk See other posts from Simple Talk or by Simon Cooper
Published on Mon, 14 Feb 2011 12:19:00 GMT Indexed on 2011/02/14 23:32 UTC
Read the original article Hit count: 519

Filed under:

Filter handlers are the second type of exception handler that aren't accessible from C#. Unlike the other handler types, which have defined conditions for when the handlers execute, filter lets you use custom logic to determine whether the handler should be run. However, similar to a catch block, the filter block does not get run if control flow exits the block without throwing an exception.

Introducing filter blocks

An example of a filter block in IL is the following:

.try {
    // try block
}
filter {
    // filter block
    endfilter
}{
    // filter handler
}
or, in v1 syntax,
TryStart:
   // try block
   
TryEnd:
FilterStart:
   // filter block
   
HandlerStart:
   // filter handler
HandlerEnd:

.try TryStart to TryEnd
    filter FilterStart
        handler HandlerStart to HandlerEnd
In the v1 syntax there is no end label specified for the filter block. This is because the filter block must come immediately before the filter handler; the end of the filter block is the start of the filter handler.

The filter block indicates to the CLR whether the filter handler should be executed using a boolean value on the stack when the endfilter instruction is run; true/non-zero if it is to be executed, false/zero if it isn't. At the start of the filter block, and the corresponding filter handler, a reference to the exception thrown is pushed onto the stack as a raw object (you have to manually cast to System.Exception).

The allowed IL inside a filter block is tightly controlled; you aren't allowed branches outside the block, rethrow instructions, and other exception handling clauses. You can, however, use call and callvirt instructions to call other methods.

Filter block logic

To demonstrate filter block logic, in this example I'm filtering on whether there's a particular key in the Data dictionary of the thrown exception:

.try {
    // try block
}
filter {
    // Filter starts with exception object on stack
    // C# code: ((Exception)e).Data.Contains("MyExceptionDataKey")
    // only execute handler if Contains returns true
    
    castclass [mscorlib]System.Exception
    callvirt instance class [mscorlib]System.Collections.IDictionary 
        [mscorlib]System.Exception::get_Data()
    ldstr "MyExceptionDataKey"
    callvirt instance bool 
        [mscorlib]System.Collections.IDictionary::Contains(object)
    endfilter
}{
    // filter handler
    // Also starts off with exception object on stack
    
    callvirt instance string [mscorlib]System.Object::ToString()
    call void [mscorlib]System.Console::WriteLine(string)
}

Conclusion

Filter exception handlers are another exception handler type that isn't accessible from C#, however, just like fault handlers, the behaviour can be replicated using a normal catch block:

try {
    // try block
}
catch (Exception e) {
    if (!FilterLogic(e)) throw;
    
    // handler logic
}

So, it's not that great a loss, but it's still annoying that this functionality isn't directly accessible. Well, every feature starts off with minus 100 points, so it's understandable why something like this didn't make it into the C# compiler ahead of a different feature.

© Simple Talk or respective owner