Before reading this article, I highly recommend reading the previous part:
AbstractThe primary goal of this article is to exhibit the mechanism of defining (syntax and semantics) the entire typical Object Oriented Programming “terms” like namespace, interface, fields, class and so on, in particular CIL metadata programming perspectives without having the IntelliSense support because we are practicing IL code authoring using common editors that unfortunately, don't leverage such features. IL coding enables the developers to directly implement the typical functionality of the C# language using its opcode instructions, for instance creating and consuming DLL files or invoking methods of an unmanaged DLL into IL code without relying on Visual Studio IDE. Such inner understandings of IL coding could be beneficial, especially for code optimization, debugging and reverse engineering, in terms of malicious code detection and subverting essential security mechanisms.
Field Metadata
Fields are data locations for diverse .NET types, for instance numeric, character and decimal. A field can defined using a .field directive, has three types of information- name, signature and access modifier (flag). Moreover, fields in .NET framework could be categorized as a value type or reference type; hence, it is also important to recognize the owner of the field such as TypeRef, TypeDef, or ModuleRef.
- .field <flags> <type> <name>
The field flags determine the accessibility scope of the field inside or outside the assembly. In fact, a flag does categorize further as accessibility (public, private), contract (static, initonly, literal), and reserved (marshal, rtspecialname). The type indicates the kind of data such as strings, character, or numeric that will be stored at a specific location.
The fields could be either global (inside class type) or local (inside function scope). Hence, the following code snippet is showing the data fields that are defined in the class-level scope.
Syntax 1: Fields declaration in IL code:
-
- .field private int32 iVal
-
-
- .field private float32 fVal
-
-
- .field private string sVal
-
-
- .field private char cVal
In the case of class-level data initialization, the default value can be directly assigned to the variable as in the following:
-
-
- .field private int32 iVal= int32(50)
- .field private string sVal= “Ajay”
The data fields that are defined inside the method body are considered to be “local data” and defined using a .locals directive. Here, we are defining a local integer type value as in the following:
- .local init ([0] int32 x)
Hence the following code demonstrates some common operation by specifying both local variables and global variable (outside the method) as in the following.
Listing 1: Fields declaration in IL code
- ..
- .module cilFields.exe
-
- .class private auto ansi beforefieldinit cilFields.fldsDemo
- extends [mscorlib]System.Object
- {
- .field private int32 x
- .method public hidebysig instance void testCal() cil managed
- {
-
- .maxstack 2
- .locals init ([0] int32 z, [1] int32 y)
- IL_0000: nop
- IL_0001: ldc.i4.s 50
- IL_0003: stloc.1
- IL_0004: ldarg.0
- IL_0005: ldfld int32 cilFields.fldsDemo::x
- IL_000a: ldloc.1
- IL_000b: add
- IL_000c: stloc.0
- IL_000d: ldstr "Fields Demo:: Calculation is {0}"
- IL_0012: ldloc.0
- IL_0013: box [mscorlib]System.Int32
- IL_0018: call void [mscorlib]System.Console::WriteLine(string, object)
- IL_001d: nop
- IL_001e: ret
- }
- }
Properties MetadataProperties enable strict control of access to the internal state of an object. It behaves like a public field and the notation to access a property is the same such as a public field on the instance. A property is a shorthand notation used to read and write fields. The .property directive is employed to define a property by use of the related .get and .set directives as in the following.
Syntax 2: Properties declaration in IL code:
- .property instance int32 iVal()
- {
- .get instance int32 NamespaceName.Class::get_iVal()
- .set instance void Namespace.Class::set_iVal(int32)
- }
Listing 2: Properties declaration in IL code
- ...
-
- .class private auto ansi beforefieldinit cilProperties.cPrptDemo extends [mscorlib]System.Object
- {
- .field private string 'Color__Field'
-
- .method public hidebysig specialname instance string get_Color() cil managed
- {
-
- .maxstack 1
- .locals init (string V_0)
- IL_0000: ldarg.0
- IL_0001: ldfld string cilProperties.cPrptDemo::'Color__Field'
- IL_0006: stloc.0
- IL_0007: br.s IL_0009
-
- IL_0009: ldloc.0
- IL_000a: ret
- }
-
- .method public hidebysig specialname instance void set_Color(string 'value') cil managed
- {
- .maxstack 8
- IL_0000: ldarg.0
- IL_0001: ldarg.1
- IL_0002: stfld string cilProperties.cPrptDemo::'Color__Field'
- IL_0007: ret
- }
-
- .method public hidebysig instance void Display() cil managed
- {
-
- .maxstack 8
- IL_0000: nop
- IL_0001: ldstr "Property Demo::Color is {0}"
- IL_0006: ldarg.0
- IL_0007: call instance string cilProperties.cPrptDemo::get_Color()
- IL_000c: call void [mscorlib]System.Console::WriteLine(string,object)
- IL_0011: nop
- IL_0012: ret
- }
-
- ..
- .property instance string Color()
- {
- .get instance string cilProperties.cPrptDemo::get_Color()
- .set instance void cilProperties.cPrptDemo::set_Color(string)
- }
- ..
- }
Namespace A namespace is a consortium of related .NET types, such as class, interface and so on contained in an assembly, particularly employed to fully qualify a class name. Moreover, a single assembly can have more than one namespace definition. A namespace in IL coding is declared using the .namespace directive in the following way.
Syntax 3: Namespace declaration:
- .namespace testNamespace
- {
- ...
-
- }
Namespace could be declared nested like class type, since one namespace can contains the definition of another and so on.
- .namespace parent
- {
- ...
-
-
- .namespace child
- {
- ...
-
- }
- }
-
- .namespace parent.child { }
The important point to remember is that namespaces are neither considered to be metadata nor referenced by IL tokens. Examine the following metadata where there is no entry of metadata and the token pertains to the namespace:
Class MetadataThe Class type in IL code is defined using a .class directive and implicitly obtains the entry of the .NET System.Object base class entry as well as class should be specified by its full name, even if it resides in the same assembly.
Syntax 4: Class declaration:
- .namespace testNamespace
- {
- ...
-
-
- .class public myClass
- {
-
- }
- }
As we know, C# code controls the visibility of fields, methods, classes and property types using various keywords like public, private, abstract, sealed and so on. Hence, IL code too has such keywords that are counterparts to control the availability of types inside or outside the assembly. The following table gives a brief description of these IL code attributes.
Table 1: Visibility attributes IL code
Constructor MetadataA constructor invokes class fields and is defined using .ctor and .cctor directives in the IL code, where .ctor represents an instance-level constructor while .cctor epitomizes static-level constructors. Moreover, it is always implicitly treated as void indeed, due to not returning a value. Here, the following code shows a default class constructor.
Syntax 5: Parameterless Constructor declaration:
- .method public hidebysig specialname rtspecialname instance void .ctor() cil managed {}
In the previous code, it is mandatory to prefix the specialname and rtspecialname attributes that uniquely identify each constructor definition in the IL code. In case of initializing the class fields using a constructor, for example invoking an integer variable, the specification should be as in the following:
Syntax 6: Parameter Constructor declaration:
- .field private int32 iValue
-
- .method public hidebysig specialname rtspecialname instance void .ctor(int32 i) cil managed
- {
-
- }
Here, we are defining a class constructor that handles strings as a parameter and invoked later during class instantiation as in the following:
Listing 3: Constructor declaration in IL code:
- ..
- .module cilConstructor.exe
-
- .class public auto ansi beforefieldinit cilConstructor.EntryPint
- extends [mscorlib]System.Object
- {
- ..
-
- .method public hidebysig specialname rtspecialname
- instance void .ctor() cil managed
- {
-
- .maxstack 8
- IL_0000: ldarg.0
- IL_0001: call instance void [mscorlib]System.Object::.ctor()
- IL_0006: ret
- }
- }
Interface MetadataAn interface is similar to a classic COM interface, defined as descriptor of properties and methods of a class type. Moreover, an interface can't offer the implementation of these exposed items except the static members; in fact, an interface can only implement another interface. An interface is neither derived from another type like class, nor can't be any other type derive from it. The interface shouldn't be sealed and methods defined in an interface must be marked as virtual. Interface types are defined using the .class directive in IL code as in the following:
Syntax 7: Interface declaration:
- .namespace testNamespace
- {
- ...
-
-
- .class public interface myInterface
- {
-
- }
- }
The interface items exposed or implemented in any class implements keywords. Here, we must specify the full name of the interface as in the following:
Syntax 8: Interface implementation in class:
- .class public myClass implements testNamespace.myInterface
- {
-
- }
Listing 4: Interface declaration in IL code:
- ..
- .class interface public abstract auto ansi cilInterface.ITestInterface
- {
- .method public hidebysig newslot abstract
- virtual instance void sqrt(float64 i) cil managed { }
- }
-
- .class public auto ansi beforefieldinit cilInterface.cInrfDemo
- extends [mscorlib]System.Object
- implements cilInterface.ITestInterface
- {
- .method public hidebysig newslot virtual final
- instance void sqrt(float64 i) cil managed
- {
-
- .maxstack 2
- .locals init ([0] float64 cal)
- IL_0000: nop
- IL_0001: ldarg.1
- IL_0002: call float64 [mscorlib]System.Math::Sqrt(float64)
- IL_0007: stloc.0
- IL_0008: ldstr "Interface Demo:: Sqrt is {0}"
- IL_000d: ldloc.0
- IL_000e: box [mscorlib]System.Double
- IL_0013: call void [mscorlib]System.Console::WriteLine(string,object)
- IL_0018: nop
- IL_0019: ret
- }
- ..
- }
Structure MetadataStructures are user defined types that contain any number of data fields and members that operate on these fields. The structure type must be defined as sealed and belongs to a value type of CTS structure, hence implicitly derived from System.ValueType. A .class directive defines a structure as in the following:
Syntax 9: Structure Declaration:
- .namespace testNamespace
- {
- ...
-
-
- .class public sealed myStructure
- {
-
- }
- }
The following program defines a structure type that has one integer type and a method that performs some operation on the defined variable as in the following:
Listing 5: Structure declaration in IL code:
- ..
- .module cilStructure.exe
-
- .class private sequential ansi sealed beforefieldinit cilStructure.sTest
- extends [mscorlib]System.ValueType
- {
- .field public int32 y
- .method public hidebysig instance void square() cil managed
- {
-
- .maxstack 8
- IL_0000: nop
- IL_0001: ldarg.0
- IL_0002: ldc.i4.4
- IL_0003: stfld int32 cilStructure.sTest::y
- IL_0008: ldstr "Square is {0}"
- IL_000d: ldarg.0
- IL_000e: ldfld int32 cilStructure.sTest::y
- IL_0013: ldarg.0
- IL_0014: ldfld int32 cilStructure.sTest::y
- IL_0019: mul
- IL_001a: box [mscorlib]System.Int32
- IL_001f: call void [mscorlib]System.Console::WriteLine(string,object)
- IL_0024: nop
- IL_0025: ret
- }
- }
-
Enum MetadataThe Enum also is also a value type in the CLR. It must therefore, be marked with the sealed keyword in the IL code and is specified using the .class directive as in the following:
Syntax 10: Enum Declarations:
- .namespace testNamespace
- {
- ...
-
-
- .class public sealed myEnum
- {
-
- }
- }
The enumerator typically contains constant fields that must be defined with a value within the range of the underlying type. Here, the following code illustrates enumerators by defining the three constant values as Red, Green and Blue.
Listing 6: Enumerator declaration in IL code:
- ..
- .module cilEnum.exe
-
- .class public auto ansi sealed cilEnum.eColor extends [mscorlib]System.Enum
- {
- .field public specialname rtspecialname int32 value__
- .field public static literal valuetype cilEnum.eColor Red = int32(0x00000014)
- .field public static literal valuetype cilEnum.eColor Green = int32(0x00000032)
- .field public static literal valuetype cilEnum.eColor Blue = int32(0x00000050)
- }
- ..
Generics MetadataA generic allows us to build unique types that are converted into closed types at runtime. We can build generic classes that contain any integer, string, or objects types. Generics are far superior to their counterpart collection classes, such as Arrays because they offer ultimate type safety, especially when boxing and unboxing operations and from a performance point of view.
Generics are defined using a single tick ( ` ) in IL coding followed by a numeric value that represents the number of type parameters in the generic.
Syntax 11: Generics Declarations:
- .namespace testNamespace
- {
- ...
-
-
- .newobj instance void class [mscorlib]
- System.Collection.Generic.List`1<int32>::.ctor()
-
- }
The previous IL code would be mapped to its corresponding C# code as in the following, where we are defining a generic type that accepts an integer at runtime as in the following:
- List<int> gObj= new List<int>();
Similarly, the following code snippet implements a generic class that accepts integers as the type parameter at runtime and yields the addition of an added number eventually without even bothering about the type conversion at runtime.
Listing 7: Generic declaration in IL code
- ..
- .module cilGenerics.exe
-
- .class private auto ansi beforefieldinit cilGenerics.cGenrcDemo extends [mscorlib]System.Object
- {
- .method public hidebysig instance void Addition() cil managed
- {
-
- .maxstack 4
- .locals init ([0] class [mscorlib]System.Collections.Generic.List`1<int32> iCal)
- IL_0000: nop
- IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
- IL_0006: stloc.0
- IL_0007: ldloc.0
- IL_0008: ldc.i4.s 10
- IL_000a: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
- IL_000f: nop
- IL_0010: ldloc.0
- IL_0011: ldc.i4.s 20
- IL_0013: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
- IL_0018: nop
- IL_0019: ldstr "Generic Demo::Addition is {0}"
- IL_001e: ldloc.0
- IL_001f: ldc.i4.0
- IL_0020: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<int32>::get_Item(int32)
- IL_0025: ldloc.0
- IL_0026: ldc.i4.1
- IL_0027: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<int32>::get_Item(int32)
- IL_002c: add
- IL_002d: box [mscorlib]System.Int32
- IL_0032: call void [mscorlib]System.Console::WriteLine(string, object)
- IL_0037: nop
- IL_0038: ret
- }
- ..
- }
Inheritance MetadataInheritance of types is a way in which the derived type guarantees support of all of the type contracts of the base class type. In addition, the derived type usually provides additional functionality or specialized behavior. In IL code, a derived class inherits the base class contracts through the extends keyword as in the following.
Syntax 12: Inheritance Declarations:
- .namespace testNamespace
- {
- ...
-
-
- .class public auto ansi beforefieldinit child_class_name extends base_class
- }
The following sample demonstrates the inheritance implementation over the Father class that serves as a base class to the Child class. Therefore, the child class can use all of the father class functionality as well as add new features.
Listing 8: Inheritance declaration in IL code
- ..
- .module cilInheritance.exe
-
- .class public auto ansi beforefieldinit cilInheritance.Father
- extends [mscorlib]System.Object
- {
- .method public hidebysig instance void FatherMethod() cil managed
- {
-
- .maxstack 8
- IL_0000: nop
- IL_0001: ldstr "this property belong to Father"
- IL_0006: call void [mscorlib]System.Console::WriteLine(string)
- IL_000b: nop
- IL_000c: ret
- }
- ..
- }
-
- .class public auto ansi beforefieldinit cilInheritance.Child
- extends cilInheritance.Father
- {
- .method public hidebysig instance void ChildMethod() cil managed
- {
-
- .maxstack 8
- IL_0000: nop
- IL_0001: ldstr "this property belong to Child"
- IL_0006: call void [mscorlib]System.Console::WriteLine(string)
- IL_000b: nop
- IL_000c: ret
- }
- ..
- }
Polymorphism MetadataThe .NET framework re-defines the existing method specification depending on the specific needs using polymorphism in which it overrides the base class virtual method specification. A virtual method definition can be marked by the
newslot attribute in the base class only which creates a new virtual method for defining the class and any classes derived from it. The important point to remember is that the
newslot attribute would be specified in the derived class as in the following:
Syntax 13: Polymorphism Declarations:
-
-
- .method public hidebysig newslot virtual
- Instance void method_name() cil managed
-
-
-
- .method public hidebysig virtual
- Instance void method_name() cil managed
The following sample is manipulating the numeric parameters of a method Calculation() inner operation using polymorphism. In the base class, it is doing an addition operation; where as in the derived class, it hides the base class method operability by multiplying the same arguments of the method.
Listing 9: Polymorphism declaration in IL code
- ..
- .module cilPolymorphism.exe
-
- .class public auto ansi beforefieldinit cilPolymorphism.cBase extends [mscorlib]System.Object
- {
- .method public hidebysig newslot virtual
- instance void Calculation(int32 x,int32 y) cil managed
- {
-
- .maxstack 2
- .locals init ([0] int32 z)
- IL_0000: nop
- IL_0001: ldarg.1
- IL_0002: ldarg.2
- IL_0003: add
- IL_0004: stloc.0
- IL_0005: ldstr "Addition is {0}"
- IL_000a: ldloc.0
- IL_000b: box [mscorlib]System.Int32
- IL_0010: call void [mscorlib]System.Console::WriteLine(string,object)
- IL_0015: nop
- IL_0016: ret
- }
- ..
- }
-
- .class public auto ansi beforefieldinit cilPolymorphism.cChild extends cilPolymorphism.cBase
- {
- .method public hidebysig virtual
- instance void Calculation(int32 x,int32 y) cil managed
- {
-
- .maxstack 2
- .locals init ([0] int32 z)
- IL_0000: nop
- IL_0001: ldarg.1
- IL_0002: ldarg.2
- IL_0003: mul
- IL_0004: stloc.0
- IL_0005: ldstr "Multification is {0}"
- IL_000a: ldloc.0
- IL_000b: box [mscorlib]System.Int32
- IL_0010: call void [mscorlib]System.Console::WriteLine(string,object)
- IL_0015: nop
- IL_0016: ret
- }
- ..
- }
SynopsisThis article provided a comprehensive overview of IL coding and syntax. Like the higher language C#, we got a thorough understanding of how to code various inherent types of the CLR using IL opcodes and an analysis of the corresponding metadata generated. We have apparently dug deeper into the coding mechanism of typical CLR programming constructs like constructors, structure, generics and moreover, the object orient programming implementations such as inheritance, interface, encapsulation and polymorphism, in terms of CIL coding.