X++ to CIL

Dynamics AX 2012 is able to generate Common Intermediate Language from X++. That allows much more code running in Common Language Runtime and also switching between X++ runtime and CLR can be greatly reduced, both having positive impact on performance.

Many developers see it as a bit of black magic, which sometimes causes avoidable difficulties. That’s why I would like to share few findings. This article describes some basic terms and where the CIL code generated from X++ is stored. Later I want to write more about the actual translation from X++ to CIL.

CIL

CIL (Common Intermediate Language), formerly known as MSIL (Microsoft Intermediate Language) or sometimes simply IL is a low-level language used by .NET Framework (and Mono). The runtime (CLR) doesn’t work directly with high-level languages like C#, it works with CIL and compilers of high-level languages simply generate CIL. The runtime handles CIL in the same way regardless it was generated from C#, Visual Basic, IronPython or from X++.

When a piece of CIL code is used for the first time, CLR optimize it for the particular processor a compiles it to native code.

Generating CIL from X++

Some X++ code is run as CIL by default (batch jobs, web services) and you can explicitly run other X++ code in CIL too (see X++ Compiled to .NET CIL). It’s always server-side code – the generated CIL is not available on client. Not all X++ code can be transformed to CIL – only types (classes, tables and enums) are supported, the code mustn’t use runbuf() method and so on.

To generate CIL, you typically use Generate Incremental CIL or Generate Full CIL buttons in the development workspace.

Assemblies and modules

As any other CIL code, CIL generated from X++ is saved in an assembly. Assemblies are basic building blocks of .NET applications grouping types together, providing additional information (manifest), versioning and so on. They may take form of an executable file (.exe) or a class library (.dll).

But unlike most assemblies, the assembly generated from X++ code consists of a high number of files. The DLL file doesn’t contain any types – it merely refers to other files known as modules, and all these files together make up a single logical unit. The advantage of this approach is that a module is loaded only if any type from that module is needed. As you’ll see, there is a lot of code generated from X++, so it makes a big difference if only a small part of it must be loaded.

Dynamics.Ax.Application

The assembly with CIL code generated from X++ can be found in bin\XppIL folder on AOS (for example c:\Program Files\Microsoft Dynamics AX\60\Server\MyAos\bin\XppIL). It consists of these files:

File Description
Dynamics.Ax.Application.dll Contains links to modules and information about where to find particular types.
Enums.netmodule Contains CIL enums generated from X++ enums.
InterfaceTypes.netmodule Contains interface types (e.g. Enumerable).
KernelTypes.netmodule Contains types defined under System Documentation node in AOT. Note that some important types are already defined as CLR types in other assemblies and are not generated from X++.
Dynamics.Ax.Application.dll{n}.netmodule
where {n} ∈ <0, 999>
These modules contain all other X++ types (i.e. classes and tables, including table maps and views).The total size is about 200 MB, so it’s really important that we don’t have to load everything to memory.

Decompiling

One important advantage (and sometimes disadvantage) of CIL code it that it’s relatively readable and it can be decompiled back to high-level languages, which is great for examining existing code and learning how exactly it works. In this case, you can see how your X++ is translated to CIL (note that most of AX kernel is written in native code, so this doesn’t apply to it).

You can use IL Disassembler to get CIL code from assemblies. To decompile CIL back to C#, use Reflector, for instance (unfortunately,  popular open-source program ILSpy doesn’t support multi-module assemblies).

To examine a specific type, you have to know in which module it can be found. One option is to look into the manifest in Dynamics.Ax.Application.dll – it contains references like this:

.class extern public Dynamics.Ax.Application.Dialog
{
    .file Dynamics.Ax.Application.dll11.netmodule
    .class 0x02000025
}

Use text search to find the requested type; you can see module name in the .file property.

Or you can follow the way used by AX for distributing types to modules. Take an object ID, divide it by 10, round it down, take last three digits, remove leading zeros and use this number in name of the .netmodule file. For example: AifExceptionMap table has ID 100029. After dividing by ten and rounding down, you get 10002. Last three digits are 002, therefore AifExceptionMap can be found in Dynamics.Ax.Application.dll2.netmodule file. Of course, this is an implementation detail that may change at any time (the number of modules is even configurable), but knowing it surely can’t harm.