Show/Hide Toolbars

XSharp

Navigation: X# Documentation > X# Tips and Tricks

Special classes and code generated by the compiler

Scroll Prev Top Next More

The compiler may generate some special classes for optimization. Some of these classes are generated by Roslyn (such as the classes for Lambda expressions or the state machines for asynchronous code). Others are generated by the X# compiler.

Below are some examples of these classes (that you can see if you open a X# compiler assembly with a tool such as IlSpy)

 

 

Class

Purpose

Xs$PSZLiteralsTable

This class is generated by the compiler if you have code in your application that looks like this:

 
LoadLibrary(PSZ(_CAST, "RICHED20.DLL")) // inside GUI classes

 

Since we cannot "know" at compile time what the lifetime of the PSZ must be we create a static field in this class and assign the generated PSZ value (a value type) to this field. As a result this PSZ will be "alive" during the whole lifetime of your application.

If you know that the PSZ will not be needed after the call to the WIN32 api then you are better off replacing the PSZ(_CAST with a String2Psz(). This will ensure that the PSZ value is destroyed when the function that creates it finishes.
The following code:

FUNCTION TestMe() AS VOID
  LoadLibrary(PSZ(_CAST, "RICHED20.DLL"))  
  RETURN

generates the following

public unsafe static void TestMe()
{
  VOWin32APILibrary.Functions.LoadLibrary((IntPtr)(void*)Xs$PSZLiteralsTable._$psz_$0);
}

and the following PSZ table:

internal static class Xs$PSZLiteralsTable
{
  internal static readonly __Psz _$psz_$0 = new __Psz("RICHED20.DLL");
}

As you can see the PSZ value is stored in the table. Please note that every PSZ variable contains a pointer to static memory allocated with the String2Mem function in the runtime. So these static memory blocks are allocated for the whole lifetime of your application.

 

If you change the code to use String2Psz() instead

FUNCTION TestMe() AS VOID
  LoadLibrary(String2Psz("RICHED20.DLL"))  
  RETURN

then the result will be:

 

public unsafe static void TestMe()
{
  List<IntPtr> pszList = new List<IntPtr>();
  try
  {
      VOWin32APILibrary.Functions.LoadLibrary((IntPtr)(void*)new __Psz(CompilerServices.String2Psz("RICHED20.DLL", pszList)));
  }
  finally
  {
      CompilerServices.String2PszRelease(pszList);
  }
}

as you can see the compiler has now generated a local variable (a list of IntPtr) which is passed to a runtime function at the end that takes care of deleting the allocated memory when the function finishes. To ensure that a try .. finally was added.

Xs$SymbolTable

This class is generated by the compiler if you are using literal symbols in your code. For each symbol in your app there will be a field in the class. Inside the System classes there are 21 literal symbols as you can see when you decompile its code:

internal static class Xs$SymbolTable

{
  internal static readonly __Symbol _init = new __Symbol("INIT");
  internal static readonly __Symbol _concurrencycontrol = new __Symbol("CONCURRENCYCONTROL");
  internal static readonly __Symbol _notify = new __Symbol("NOTIFY");
.
.
  internal static readonly __Symbol _unknown = new __Symbol("UNKNOWN");
  internal static readonly __Symbol _resourcestring = new __Symbol("RESOURCESTRING");
}

This is very similar to the way how symbols are handed in Visual Objects.
When the first symbol in an assembly is used then all the symbols are created and after that using literal symbols is very fast. Symbols are stored in a static table in the runtime and the symbol value contains only the offset in this table. Comparing 2 symbols is like comparing 2 numbers and therefore very fast.

<AssemblyName>.Functions

Dotnet does not know the concept of functions or global variables. The X# compiler is therefore creating a static class in each assembly that contains static methods for each of the functions or procedures in your code.

The name of this class is derived from the name of your output assembly:

MyFile.DLL will contain a class MyFile.Functions

MyFile.EXE will contain a class MyFile.Exe.Functions

If your output assembly name contains embedded dots then these dots will be replaced with underscore characters in the functions class name:

MyApp.Main.EXE will contain a classname MyApp_Main.EXE.Functions

Functions$<ModuleName>$

Whenever your code uses STATIC FUNCTION, STATIC DEFINE, STATIC GLOBAL (whose visibility is within the same file only) then the compiler generates a separate class for each modulde (PRG file) where the name of the PRG file is used for the <Modulename>, so the file Start.Prg in Application1.exe will result in a class name

Application1.Exe.Functions$Start$

$PCall$<FunctionName>$<suffix>

If your code contains PCALL() constructs then the compiler will generate a special delegate with a name based on the method/function name and will make this delegate a nested object inside the type where the PCALL() is used. So a PCALL() inside a function will result in a nested delegate inside the Functions class and a PCALL() in a method of the Window class will result in a nested delegate inside the Windows class.

The return type and parameter names and types of the delegates are derived from the function declaration for the typed pointer that you are passing to PCall().

 
For example The VOGUIClasses assembly contain a $PCall$DeleteTrayIcon$430 inside the Window class and a $PCall$__InitFunctionPointer$28 inside the Functions class.
If you look at the original code in the __InitFunctionPointer procedure then it looks like this:

IF !PCALL(gpfnInitCommonControlsEx, @icex)

The resulting code looks like this:

 

              if (!$PCallGetDelegate<$PCall$__InitFunctionPointer$28>(gpfnInitCommonControlsEx)(&icex))

 

The $PCallGetDelegate function is a special compiler generated function that looks like this:

[CompilerGenerated]
internal static T $PCallGetDelegate<T>(IntPtr p)
{
  return (T)(object)Marshal.GetDelegateForFunctionPointer(p, typeof(T));
}

In short: it takes a function pointer (p) and Gets a delegate of type T. This delegate is then used to call the API function.

Please don't worry if you don't get this. It took us a while to create this ourselves too !

 

$PCallNative$<FunctionName>$<suffix>

This is a delegate generated for PCallNative constructs. The return type is the type of the generic argument and the parameter types are derived from the types of the arguments. The parameter names are $param1, $param2 etc.

So the following code inside a Test function

  LOCAL p AS IntPtr        
  P := GetProcAddress(hDLL, "MyFunc")
  PCallNative<INT> (p,1,2,3)

Will generate a delegate like this:

[CompilerGenerated]
internal delegate int $PCallNative$Test$0(int $param1, int $param2, int $param3);

Functions.$Init1
Functions.$Init2

Functions.$Init3

Functions.$Exit
<Module>.$AppInit()
<Module>.$AppExit()

These special methods inside the function class are generated to call Init and Exit procedures. See the topic about startup code for more information about this

<Module>.RunInitProcs()

This special method inside the <Module> class is generated by the compiler and will be called at runtime when you are dynamically loading assemblies using the XSharpLoadLibrary() function. This takes care of calling all init procedures when a DLL is loaded dynamically.

<>ClassName

Special classes that start with a <> prefix are generated by the Roslyn compiler for lambda expressions and codeblocks.

If you look at the VORDDClasses assembly you will find many examples of these. You may have to set ILSpy to show IL instead of C# or XSharp code, because otherwise these classes will be hidden by the tool.

If you look at the RDD classes in C# mode it will looks like this:
 

RddClasses1

 

If you switch ILSpy to IL mode it looks like this:

 

RddClasses2

 

As you can see there are now quite some nested classes inside the DbServer class. The <>c class contains codeblocks that do not need to access local variables from functions or methods.  In the DbServer class this class has some 25 methods, each of which is a codeblock.

The classes with the name <>c_DisplayClass<nn> contain codeblocks that need access to local variables from the function or method where they are defined.

The compiler has detected this and has moved the local variables out of the function/method and made them fields in a compiler generated class, so the codeblocks can access them. In Clipper and VO these were called "detached locals".

 

For example  DisplayClass56_0 has the variables for the Average function:

.class nested private auto ansi sealed beforefieldinit '<>c__DisplayClass56_0'
  extends [mscorlib]System.Object
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
      01 00 00 00
  )
  // Fields
  .field public int32 iCount
  .field public class [XSharp.RT]XSharp.__Array acbExpr
  .field public class [XSharp.RT]XSharp.__Array aResults
  .field public valuetype [XSharp.RT]XSharp.__Usual cbKey
  .field public valuetype [XSharp.RT]XSharp.__Usual uValue
 
  // Methods
  .method public hidebysig specialname rtspecialname
      instance void .ctor () cil managed
  {
 
} // end of class <>c__DisplayClass56_0

The codeblocks inside the Average method apparently access 5 locals variables (iCount, acbExpr, aResults, cbKey and uValue).

 

If you look inside the Average() method of DbServer you will see a codeblock such as

 
SELF:__DBServerEval( { || iCount += 1, __IterateForSum( acbExpr, aResults ) }.......)

 

 

If you look in the decompiled code for Average() (in C# mode) you will see something like this:

__DbServerEval(new
                  {
                      Cb$Eval$ = (<>F<__Usual>)delegate
                      {
                          iCount++;
                          VORDDClasses.Functions.__IterateForSum(acbExpr, aResults);
                          return default(__Usual);
                      },
                      Cb$Src$ = "{ || iCount += 1, __IterateForSum( acbExpr, aResults ) }"
                  },...........................)

The whole {} after the new is an anonymous codeblock expression
The Cb$Eval$ field in this expression is a delegate that contains the code for the codeblock.

The CB$Src field in this expression includes the source for the codeblock so at runtime you will be able to see the source of the compiler time codeblock (this was introduced in build 2.3.0)

 

The actual body of the codeblock (the part from iCount++ until return default(__Usual)) is in reality stored as a method of <>c__DisplayClass56_0. And all the variables that are needed inside this codeblock are not really stored as variables inside Average() but they are stored as fields of <>c__DisplayClass56_0.

 

Please don't worry if you don't get this. It took us a while to understand and create this ourselves too !

Xs$Args

Whenever your code contains functions or methods with the so called CLIPPER calling convention, then X# compiler will create code that handles the parameters in a special way: For example the function Str() in the runtime. This is declared with the following parameters:
 
FUNCTION Str(nNumber,nLength,nDecimals) AS STRING

 

The compiler sees this as CLIPPER calling convention because all 3 of the parameters are optional.

 

The C# version of the IL code generated for this function is:

 

[ClipperCallingConvention(new string[] { "nNumber", "nLength", "nDecimals" })]
public static string Str([CompilerGenerated] params __Usual[] Xs$Args)
{
  int num = (Xs$Args != null) ? Xs$Args.Length : 0;
  __Usual nNumber = (num >= 1) ? Xs$Args[0] : __Usual._NIL;
  __Usual nLength = (num >= 2) ? Xs$Args[1] : __Usual._NIL;
  __Usual nDecimals = (num >= 3) ? Xs$Args[2] : __Usual._NIL;

As you can see the function now has a single argument, an array of usuals.
The parameter names are stored in an attribute of type ClipperCallingConvention. This attribute is used by the intellisense inside Visual Studio and XIDE so the parameter names can be shown.

Inside the body of the generated method the compiler now declares a variable that has the length of the array (the number of arguments passed, which you can also request at runtime with PCount()). The compiler also generates a local variable with the same name as the parameter and initializes each variable with either the value passed (0 based array elements) or with NIL.

In the body of the method you will see a try finally. In the finally clause there is the following code:

finally
  {
      if (num >= 2)
      {
          Xs$Args[1] = nLength;
      }
  }

The reason for this code is that somewhere inside Str() nLength has been assigned. Str() does not know if the variable was passed by value or by reference.  If the value was passed by reference then the array element inside Xs$Args must be updated, which is exactly what happens here.

The code that calls Str() is now responsible for assigning back the value from the array to its local, when that value is passed by reference