9

I have the following code

    using System;

class Pankaj
{
    public static int Main()
    {
        int returnValue=0;
        try
        {
            return returnValue;
            throw new Exception();

        }
        catch(Exception ex){
            return returnValue;
        }
        finally
        {
            returnValue++;
        }
        return returnValue;
    }
}

THE MSIL generated of the above code is :

.method public hidebysig static int32  Main() cil managed
{
  .entrypoint
  // Code size       18 (0x12)
  .maxstack  2
  .locals init (int32 V_0,
           int32 V_1)
  IL_0000:  ldc.i4.0
  IL_0001:  stloc.0
  .try
  {
    .try
    {
      IL_0002:  ldloc.0
      IL_0003:  stloc.1
      IL_0004:  leave.s    IL_0010
    }  // end .try
    catch [mscorlib]System.Exception 
    {
      IL_0006:  pop
      IL_0007:  ldloc.0
      IL_0008:  stloc.1
      IL_0009:  leave.s    IL_0010
    }  // end handler
  }  // end .try
  finally
  {
    IL_000b:  ldloc.0
    IL_000c:  ldc.i4.1
    IL_000d:  add
    IL_000e:  stloc.0
    IL_000f:  endfinally
  }  // end handler
  IL_0010:  ldloc.1
  IL_0011:  ret
} // end of method Pankaj::Main

I have following questions:

  1. Why the try catch is again included inside the try block.
  2. It looks like leave.s the last line in try and catch block is point to finally i.e. IL_0010 but at line IL_0010 its ldloc.1 which I believe means load local variable 1 on the stack, then how its pointing to finally block. Is it something like at location 1 we have address of finally block.
  3. If I throw or return something from the catch block then how come the call statement falls to the finally block, it's already returned from the catch block but still the finally block gets executed.
svick
  • 236,525
  • 50
  • 385
  • 514
Pankaj
  • 1,446
  • 4
  • 19
  • 31
  • Note how try/catch/finally doesn't generate any MSIL opcodes at all. The directives identify *zones* of opcodes. Which is a pretty accurate representation of the way it is implemented under the hood, it generates a table of addresses. The CLR finds the catch and finally clause code by looking in that table. Indexing it with the address of the instruction that threw the exception. – Hans Passant Dec 22 '14 at 12:35

2 Answers2

11

Why the try catch is again included inside the try block.

Not sure on this one. It may just be the way that ildasm chooses to decompile it. ECMA-335 says there are restrictions on how SEHClause elements can be specified after a TryBlock, but I haven't found those restrictions yet.

It looks like leave.s the last line in try and catch block is point to finally i.e. IL_0010 but at line IL_0010 its ldloc.1 which I believe means load local variable 1 on the stack, then how its pointing to finally block. Is it something like at location 1 we have address of finally block.

No, that's jumping to after the finally block - to return the value, effectively. It doesn't help that you've got lots of return statements all returning the same thing, as well as unreachable code, but I believe the point is basically just to move the ret outside the try and catch. I think the compiler is effectively setting up an extra local variable for the return value.

If I throw or return something from the catch block then how come the call statement falls to the finally block, it's already returned from the catch block but still the finally block gets executed.

That's how both C# and IL are defined - the finally block will be executed however you exit the block.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • okay ... .locals init (int32 V_0,int32 V_1) Looks like its initializing value for two integer variable, I have just a single integer variable whats other for – Pankaj Dec 22 '14 at 11:37
  • @Pankaj: I've just edited my answer - I think there may be a restriction around returning from `.try` blocks in IL, and the compiler is just setting up an extra local variable for both of the return statements. If you return different things from the `try` than from the `catch`, you'll see they get copied into that extra local. – Jon Skeet Dec 22 '14 at 11:42
  • so its like -> Main() -> initialize returnvalue ->then at IL_0004 jump to IL_0010 -> at IL_0010 load on stack local variable 1 (which in this case is returnValue) -> IL_0011 i.e. return, and 0 is returned. where in this execution path finally gets called – Pankaj Dec 22 '14 at 11:49
  • @Pankaj: Yes, I believe so. – Jon Skeet Dec 22 '14 at 11:50
2

Jon Skeet has already answered the last two questions, so I'll just focus on the first.

Why the try catch is again included inside the try block.

There are a couple of reasons for this:

  • Placing the catch handler inside the try block associated with the finally handler means that the finally will happen even if an exception is thrown inside the catch block (which I believe is required by the C# specification - though I don't have a direct reference to where it says so).
  • The CLI specification has some strict rules about overlapping exception handling regions, and these rules preclude having the catch and finally blocks from protecting the same code without having the finally block also protect the catch block or the catch block also protect the finally block (The specification discusses it in more general terms than this, you can find the details in partition 1, section 12.4.2.7 of ECMA-335).
Brian Reichle
  • 2,798
  • 2
  • 30
  • 34