Codegen for On Error Resume Next
VB has a "On Error Resume Next", which tells each line to swallow exceptions and just keep executing to the next line. It's kind of like a try-catch around every single line.
It may be tempting for C++ developers to make fun of VB for this, but this is a lot like programming with HRESULT (or any error handling strategy based on return codes) when you forget to check the return value. And as we look at the codegen below, VB's code could turn out to be even more efficient than the unmanaged error handling equivalent.
So in this VB snippet, the exception is is swallowed and "Next" is printed.
Module Module1 Sub Main() On Error Resume Next Console.WriteLine("Hello!") Throw New Exception("Bong!") Console.WriteLine("Next") End Sub End Module
If you're wondering what the codegen is like, you can always compile and then view the IL in ildasm. Or you can view it in Reflector as C# instead of IL, which is easier to comprehend.
[STAThread] public static void Main() { // This item is obfuscated and can not be translated. int VB$ResumeTarget; try { int VB$CurrentStatement; Label_0001: ProjectData.ClearProjectError(); int VB$ActiveHandler = -2; Label_0009: VB$CurrentStatement = 2; Console.WriteLine("Hello!"); Label_0016: VB$CurrentStatement = 3; throw new Exception("Bong!"); Label_0023: VB$CurrentStatement = 4; Console.WriteLine("Next"); goto Label_009E; Label_0035: VB$ResumeTarget = 0; switch ((VB$ResumeTarget + 1)) { case 1: goto Label_0001; case 2: goto Label_0009; case 3: goto Label_0016; case 4: goto Label_0023; case 5: goto Label_009E; default: goto Label_0093; } Label_0059: VB$ResumeTarget = VB$CurrentStatement; switch (((VB$ActiveHandler > -2) ? VB$ActiveHandler : 1)) { case 0: goto Label_0093; case 1: goto Label_0035; } } catch (object obj1) when (?) { ProjectData.SetProjectError((Exception) obj1); goto Label_0059; } Label_0093: throw ProjectData.CreateProjectError(-2146828237); Label_009E: if (VB$ResumeTarget != 0) { ProjectData.ClearProjectError(); } }
So you can see it's just putting the entire region in a try/catch block, and then using a switch table to jump back to the appropriate line. It has a "Current Statement" variable to track the last successful line to execute before an exception may have been thrown, and then switches to the next line on the exception path.
The switch table may seem evil at first, but remember that in native code, all those IfFailGoto() checks to propagate return results also add up to a lot of switching code. In this case, the branches are at least optimized into a single switch table as opposed to scattered branch code.
Comments
Anonymous
April 12, 2008
"It may be tempting for C++ developers.." Erm, don't you mean: "It may be tempting for <em>Win32</em> developers.." Let's not assume every C++ programmer writes Win32 code. Contrary to popular belief there are other platforms and APIs used by these guys ;)Anonymous
April 12, 2008
I didn't think my original statement made the assumption you mention here. my qualifier "or any error handling strategy based on return codes" was an attempt to make it clear that I recognize there are other APIs then Win32.Anonymous
April 14, 2008
By the way, this part of disassembled code demonstrates a bug in .NET Reflector: Label_0035: VB$ResumeTarget = 0; switch ((VB$ResumeTarget + 1))