Hello World in IronRuby - The Macho Way

 May 22, 2011  |   Filed under: software development  |   Tags: .NET, MSIL, Programming, Ruby


Those of you who know me know that I'm a big Ruby fan. I've been doing as much Ruby as I can squeeze in for the past 6 years, which hasn't been that much since I usually get paid for doing .NET (C# / VB.NET).

Given this you might think I would jump on IronRuby since it mixes my passion for Ruby with my day-to-day work of developing on the .NET platform. However I haven't really explored IronRuby simply because I didn't see any benefits over using standard Ruby. I'm not saying there aren't any benefits I'm just saying that there where none for me.

Lately however I've been going deeper and deeper into the rabbit hole of programming languages. I've started looking at things like language semantics and complier design. I've played around with several different well-known and obscure languages and I've started going down on the lower levels of framework internals.

From this point of view back-ends become extremely interesting. What does your compiler emit - Machine code, Java bytecode, MSIL?

I have great respect for both Java bytecode and MSIL and think that virtual platforms are the way to go, I mean who needs machine code year 2011 anyway, right?

When given a choice between the JVM and CLR I find the CLR much more pleasant to work with. Maybe it's because of my history with .NET that I find the CLR fits me better than the JVM.

With all this in mind I have finally found a really awesome use for IronRuby, emitting IL! Sure this could be done in C# or VB.NET but for the heck of it and just because I prefer to work in Ruby I did it in IronRuby.

I leave you with a final example of how to do Hello World in IronRuby the macho way. This code emits Hello World IL and creates a HelloWorld.exe that executes on .NET/Mono.

require 'mscorlib'

# Aliases for some of the .NET types we will use
AssemblyName = System::Reflection::AssemblyName
CurrentDomain = System::AppDomain.CurrentDomain
AssemblyBuilderAccess = System::Reflection::Emit::AssemblyBuilderAccess
TypeAttributes = System::Reflection::TypeAttributes
MethodAttributes = System::Reflection::MethodAttributes
OpCodes = System::Reflection::Emit::OpCodes
BindingFlags = System::Reflection::BindingFlags

# Define assembly, type and main method. This is required by all programs
assembly_name = AssemblyName.new("HelloWorld")

assembly = CurrentDomain.DefineDynamicAssembly(assembly_name, AssemblyBuilderAccess.RunAndSave)
assembly_module = assembly.DefineDynamicModule("HelloWorld.exe".to_clr_string, "HelloWorld.exe".to_clr_string)

program_type = assembly_module.DefineType("Program", TypeAttributes.Class)
main_method = program_type.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static)

# Get the il generator for the main method
il_gen = main_method.GetILGenerator

# Emit hello world to the main method
il_gen.Emit(OpCodes.Ldstr, "Hello world!")

write_line_params = System::Array.of(System::Type).new([System::String.to_clr_type])
console_type = System::Console.to_clr_type

write_line_method = console_type.GetMethod("WriteLine", BindingFlags.Public | BindingFlags.Static, nil, write_line_params, nil)

il_gen.Emit(OpCodes.Call, write_line_method)
il_gen.Emit(OpCodes.Ret)

# Create the program and save it to disk
program_type.CreateType()
assembly.SetEntryPoint(main_method)
assembly.Save("HelloWorld.exe")