Before reading this article, I highly recommend reading the previous part:
Abstract
The primary goal of this article is to exhibit the mechanism of defining (syntax and semantics) the entire typical Object-Oriented Programming “terms” like a 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 be defined using a .field directive, which has three types of information- name, signature, and access modifier (flag). Moreover, fields in the .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, characters, 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 variables (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 Metadata
Properties enable strict control of access to the internal state of an object. It behaves like a public field and the notation to access property is the same 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 contain 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 Metadata
The 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, the 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 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 Metadata
A 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 Metadata
An interface is similar to a classic COM interface, defined as a 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 derived 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 Metadata
Structures 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 Metadata
The 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 Metadata
A 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 object 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 Metadata
Inheritance of types is a way in which the derived type guarantees the 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 Metadata
The .NET framework re-defines the existing method specification depending on the specific needs using polymorphism in which 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
- }
- ..
- }
Synopsis
This 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.