Browse DevX
Sign up for e-mail newsletters from DevX


Down to the Metal: Managed Code Under the Hood (Part III) : Page 3

It's not until you get to the IL level that you see exactly how Microsoft made it possible for many languages to compile to the same runtime. In this last part of the IL Assembler series, you'll see how to write object-oriented IL code, declare fields, methods and properties and how to create object instances and access their members.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Defining the Class Members
After adding the forward declaration of the classes in your namespace you can start to implement the class members. You begin with the namespace and the class like just as before:

// CLASS MEMBERS DECLARATION .namespace Sphere { .class public auto ansi serializable beforefieldinit Sphere extends [mscorlib]System.Object {

Note that the preceding code is exactly the same as in the forward declaration.

Implementing Fields
Fields are extremely easy to implement. You simply add a .field directive and then specify the access modifier, type, and name, just as you do in any other high-level language:

.field private float64 radius

The preceding code adds a field called "radius" of type float64 with an access modifier of private.

There are several types of access modifiers in Intermediate Language Assembler; here's a complete list:

  • assembly
  • famandassem (family and assembly)
  • family
  • famorassem (family or assembly)
  • private
  • public
Author's Note: Most high-level languages use the keyword protected instead of family

Adding Constructors
Constructors are basically methods of your class like any other method; but they have a special meaning, and therefore not only have a special name, but also a few special directives.

.method public hidebysig specialname rtspecialname instance void .ctor() cil managed { .maxstack 2 ldarg.0 ldc.r8 0.0 call instance void Sphere.Sphere::.ctor(float64) ret } .method public hidebysig specialname rtspecialname instance void .ctor(float64 radius) cil managed { .maxstack 2 ldarg.0 call instance void [mscorlib]System.Object::.ctor() ldarg.0 ldarg.1 stfld float64 Sphere.Sphere::radius ret }

The above code specifies two constructors. The first one (which doesn't require a parameter) calls the second one which accepts one float64 argument for initializing the radius of your sphere. In a high-level language such as C# you'd do this with an initializerlist.

Before examining the internal code of the constructors take a look at the declaration directives:

  • .method—The following code block is a method.
  • public—The constructor has to be callable by everybody.
  • hidebysig—Short for "hide by signature"; this is ignored by the runtime.
  • specialname—The method name needs to be treated in a special way by some tool.
  • rtspecialname—The method name needs to be treated in a special way by the runtime.
  • instance—This method is an instance method and not a static one.
  • void—The return type of the constructor (Note: in most high-level languages constructors do not have any return type).
  • .ctor—The name of the method; .ctor is reserved for constructors.
  • (), (float64 radius)—The argument list of the constructor.
  • cil—This method contains common intermediate language code.
  • managed—This method contains code that can be managed by the runtime (no unsafe code).
Now, take a look at the implementation of the constructor:

ldarg.0 ldc.r8 0.0 call instance void Sphere.Sphere::.ctor(float64)

Because the instructor is an instance method, the runtime needs to know on which instance to call that method. The first argument always stores the reference to the instance; therefore the code contains a ldarg.0 call to load the reference. Then it loads a default value for the radius as a float64 with a value of 0.0, and finally calls the overloaded constructor using the radius value on the stack as the one required parameter. Note that you have to add the keyword instance before the method signature to tell the runtime that you are not calling a static method, but an instance method. Because this constructor calls another one it doesn't need to call the base class constructor first—the second method calls the base class constructor.

ldarg.0 call instance void [mscorlib]System.Object::.ctor() ldarg.0 ldarg.1 stfld float64 Sphere.Sphere::radius

As you can see, the code calls [mscorlib] System.Object::.ctor(), which is the constructor of the base class System.Object, to create the instance. Next, the argument passed for the radius needs to be stored in the appropriate field. The constructor does that using the command stfld, which means "store field".

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date