CHAPTER 18 UNDERSTANDING CIL AND THE ROLE OF DYNAMIC ASSEMBLIES
Declaring Local Variables in CIL
Let’s first check out how to declare a local variable. Assume you wish to build a method in CIL named
MyLocalVariables() that takes no arguments and returns void. Within the method, you wish to define
three local variables of type System.String, System.Int32, and System.Object. In C#, this member would
appear as follows (recall that locally scoped variables do not receive a default value and should be set to
an initial state before further use):
public static void MyLocalVariables()
{
string myStr = "CIL code is fun!";
int myInt = 33;
object myObj = new object();
}
If you were to construct MyLocalVariables() directly in CIL, you could author the following:
.method public hidebysig static void
MyLocalVariables() cil managed
{
.maxstack 8
// Define three local variables.
.locals init ([0] string myStr, [1] int32 myInt, [2] object myObj)
// Load a string onto the virtual execution stack.
ldstr "CIL code is fun!"
// Pop off current value and store in local variable [0].
stloc.0
// Load a constant of type "i4"
// (shorthand for int32) set to the value 33.
ldc.i4 33
// Pop off current value and store in local variable [1].
stloc.1
// Create a new object and place on stack.
newobj instance void [mscorlib]System.Object::.ctor()
// Pop off current value and store in local variable [2].
stloc.2
ret
}
As you can see, the first step taken to allocate local variables in raw CIL is to make use of the .locals
directive, which is paired with the init attribute. Within the scope of the related parentheses, your goal
is to associate a given numerical index to each variable (seen here as [0], [1], and [2]). As you can see,
each index is identified by its data type and an optional variable name. After the local variables have
been defined, you load a value onto the stack (using the various load-centric opcodes) and store the
value within the local variable (using the various storage-centric opcodes).
676