IL perversions: throwing and catching strings
Inspired by Mohamed Mahmoud’s blog posting How to: Create Interfaces with Static Methods via IL? I wrote another sick example on IL (Intermediate Language) to show you how different is the world behind compilers. Tonight I have enough of exceptions, I want to throw some strings! Let’s do it!
Throwing exception in IL
As a first thing take a look at the following code written in IL. In short this code defines class with two methods. Run() is entry point and it is run automatically when compiled assembly is executed from command line. ThrowSomething() is method that throws exception. This exception is caught and program terminates without any errors. I borrowed this code from Vijay Mukhi’s IL book, Chapter 10 “Exception Handling”.
.assembly StringMess {}
.class private auto ansi Program extends [mscorlib]System.Object
{
.method public hidebysig static void Run() il managed
{
.entrypoint
.locals (class [mscorlib]System.Exception V_0)
.try
{
call void Program::ThrowSomething()
ldstr "Bye"
call void [mscorlib]System.Console::WriteLine
(class System.String)
leave.s IL_001e
}
catch [mscorlib]System.Exception
{
stloc.0
ldstr "Exception was thrown!"
call void [mscorlib]System.Console::WriteLine
(class System.String)
leave.s IL_001e
}
IL_001e: ldstr "Finish"
call void [mscorlib]System.Console::WriteLine
(class System.String)
ret
}
.method public hidebysig static void ThrowSomething()
il managed
{
newobj instance void [mscorlib]System.Exception::.ctor()
throw
}
}
Everybody who is familiar with C# should be able to understand this code at least in some parts. Now let’s see what is the output of this method. As image on right shows it is nothing special. We threw exception, it was handled and program terminated normally. Just like we expected.
Let’s throw some strings now
Now let’s modify this code so it should do something we expect to end up with error. I add one more catch clause. This additional catch catches String. To test it I will do exactly what this catch expects – I will throw out the String. Here is the code.
.assembly StringMess {}
.class private auto ansi Program extends [mscorlib]System.Object
{
.method public hidebysig static void Run() il managed
{
.entrypoint
.locals (class [mscorlib]System.Exception V_0)
.try
{
call void Program::ThrowSomething()
ldstr "Bye"
call void [mscorlib]System.Console::WriteLine
(class System.String)
leave.s IL_001e
}
catch [mscorlib]System.Exception
{
stloc.0
ldstr "Exception was thrown!"
call void [mscorlib]System.Console::WriteLine
(class System.String)
leave.s IL_001e
}
//
// CATCH STRING
//
catch System.String
{
stloc.0
ldstr "String was thrown"
call void [mscorlib]System.Console::WriteLine
(class System.String)
leave.s IL_001e
}
IL_001e: ldstr "Finish"
call void [mscorlib]System.Console::WriteLine
(class System.String)
ret
}
.method public hidebysig static void ThrowSomething()
il managed
{
//newobj instance void [mscorlib]System.Exception::.ctor()
//
// THROW STRING
//
ldsfld string [mscorlib]System.String::Empty
throw
}
}
We can expect that this code doesn’t compile, but it does. As a next thing we may expect that running this code some runtime error occurs, but no, there is no runtime error. We can expect that exception handling lands in catch for Exception… we can expect many things but the result is here.
I bet at least half of readers expected something else but yes, we just threw string and we also caught it. But why C# and VB.NET give us errors when we try to compile code like this? Well, it is a compiler level limitation. It is not related to CLR as we just saw.
Playing with Visual Studio
Let’s play now with Visual Studio. I created console application, compiled previous example as DLL and referenced it from my console application. I want to know what happens when string is thrown and caught. Does it affect compiler somehow?
using System;
namespace ConsoleExamples
{
class Program
{
static void Main(string[] args)
{
StringMess.Program.Run();
Console.ReadLine();
Console.ReadLine();
}
}
}
Well, it does not. Let’s try to call ThrowSomething() method now. Maybe something interesting happens.
using System;
namespace ConsoleExamples
{
class Program
{
static void Main(string[] args)
{
StringMess.Program.ThrowSomething();
Console.ReadLine();
}
}
}
Okay, now something happens. Our application is not able to handle the situation and it throws RuntimeWrappedException.
RuntimeWrappedException is located under System.Runtime.CompilerServices namespace and as documentation sais it is here to maintain compatibility between languages. The common language runtime (CLR) wraps objects that do not derive from Exception in a RuntimeWrappedException object. This is why we got exception and not completely crashed application.
TIP! If you want to find out more about IL there is very good book I can suggest: Expert .NET 2.0 IL Assembler by Serge Lidin. I have read this book and also the idea of this posting is taken from this book. If you are interested in my other postings about IL and compiling results then please take a look at Behind the Compiler category.
Okay, it’s couple of minutes over midnight here and it is time to throw some real strings, I guess. Good night!