In part 1, part 2 and part 3 of this article series I have shown you some problems that we found in existing code (in our own code as well, and we thought that that code was perfect). Today we will discuss a few other problems that we found.

Wrong order of arguments in indexed ASSIGN in the SDK

Have you ever tried to change the caption of a tab page in a Vulcan app that uses the GUI classes with something like that? :

SELF:oDCMyTabControl:TabCaption[#MyTabPage] := "New caption"

If you have tried that, then you must have noticed that this code does not work in Vulcan (it does not work in VO either), the tab page remains unchanged! X# revealed why, it is because the TabCaption ACCESS/ASSIGN pair has been declared incorrectly in VO (and subsequently also in Vulcan):

ACCESS TabCaption(symTabName)
ASSIGN TabCaption(symTabName , cCaption) CLASS TabControl

This is an indexed ACCESS/ASSIGN, and in the ASSIGN declaration one parameter is the name (as a SYMBOL) of the tab page caption to change and the other is the new caption. But the order in which the parameters have been entered is incorrect! The first parameter should be the new value to be assigned, followed by the index expression(s). This is revealed by the X# compiler which reports:

error XS9039: The parameters for the Access and Assign declarations must match by name and type. The first ASSIGN parameter is the value, the other parameters are the indexers and must be the same as the ACCESS parameters.

So this ASSIGN should have been defined as:

ASSIGN TabCaption(cCaption , symTabName) CLASS TabControl

After making that change in the SDK code, now changing the caption of a tab page via code is working perfectly!

Weird problem with _winNMDATETIMESTRING in the VOSDK

This is by far the strangest problem we have found with the help of X#, while trying to compile the original SDK from VO. You can see it by navigating through the repo explorer of ANY version of VO to the CommCtrl module of the Win32 API Library, press CTRL+M to open all the entities of that module in the editor and search for “_winNMDATETIMESTRING”. This will point you to that chunk of code in the editor:

DEFINE DTN_USERSTRING := (DTN_FIRST + 2) // the user has entered a string
MEMBER _winNMDATETIMESTRING ALIGN 1
MEMBER nmhdr IS _winNMHDR
MEMBER pszUserString AS PSZ
MEMBER st IS _winSYSTEMTIME
MEMBER dwFlags AS DWORD

Something looks very bizarre here, doesn’t it? At least it looked bizarre to our compiler, which reported a parser error when compiling that code in X#. After a few minutes fearing that the unexpected Athens snow and cold had made our eyes “see” things that were not there, we realized what had really happened: Many, many years ago, the original authors of the VO SDK had accidentally typed “MEMBER _winNMDATETIMESTRING ALIGN 1” instead of “STRUCTURE _winNMDATETIMESTRING ALIGN 1”, causing the whole structure to get somehow embedded as part of the DEFINE DTN_USERSTRING entity above! The result of this is that the _winNMDATETIMESTRING STRUCTURE is not actually available in the SDK and this problem has been carried over also to Vulcan, where this structure is not available either. Of course this is not something of huge importance, but it was a pretty weird problem that X# revealed and I wanted to share it with you!

Typo in ShellWindow:Dispatch() of the VO SDK

This is another problem in the existing VO SDK, you can see it by opening the Dispatch() method of the ShellWindow class in the GUI Classes, again in any version of VO (or in the Vulcan SDK). In about the middle part of that entity, you will see this code:

CASE (uMsg == WM_SETCURSOR)
     IF (oEvt:wParam) != (DWORD(_CAST, hwndClient))
          lclient := FALSE
     ELSE
          lclient := TRUE
     ENDIF
     IF lHelpOn
          IF lhelpcursorOn
                lHelpEnable := TRUE
          ELSE
                lHelpEnable := FALSE
          ENDIF
     ELSE
          lHelpEnable := FALSE
     ENDIF
     SELF:__HandlePointer(oEvt, lHelpCursorOn, lclient)
     RETURN SELF:EventReturnValue

When compiling this in X#, the compiler reports: warning XS0219: The variable 'lHelpEnable' is assigned but its value is never used. That made us look more closely to the code and realize there is another typo, the 2nd argument passed to the __HandlePointer() method should be “lHelpEnable”, not “lHelpCursorOn” as it is now. So this is another thing that must be corrected in the VO SDK.

Miscellaneous potential problems revealed by X#

Most of the issues in code revealed by X# that were described above are critical ones, as in most cases they lead to serious problems when executing it at runtime. But by no means are those the only problems revealed by the compiler. X# also warns us about many other, less critical, things in our code, like inconsistent accessibility (for example when a PUBLIC method has a parameter of a type that is INTERNAL), class fields or events that are left (accidentally?) uninitialized and much more. I personally found many dozens of them when compiling XIDE in x#! Those warnings are for up to the developer to decide if he/she wants to follow the compiler’s advice to adjust the code or not. Describing all those cases would probably require a whole book, but maybe some of them could be covered in another blog article in the future.

In part 1 and part 2 of this article series I have shown you some problems that we found in existing code (in our own code as well, and we thought that that code was perfect). Today we will discuss an important other problem, that has to do with WIn32 interoperability.

Win32 Callback functions

There exist many functions in the Win32 API that require a callback function to return their results to, or use it for some other reason. Probably the most known one is the EnumWindows() function, which enumerates all top-level windows managed by the OS and it can be used like this (VO code – please don’t mind the style, it’s for minimum line length):

FUNCTION Start() AS INT
EnumWindows(@EnumWindowsCallback() , 0)
WAIT
RETURN 0

// this is the callback function:
FUNCTION EnumWindowsCallback(hWnd AS PTR , lParam AS INT) AS LOGIC
LOCAL
pszString := MemAlloc(100) AS PSZ
IF
GetWindowText(hWnd,pszString,100) != 0
     ? pszString
ENDIF
MemFree(pszString)
RETURN TRUE

There are a lot of such callback functions used in the VOSDK and in a lot of VO-programmers’ code as well. And this code can work in .Net, too, but unfortunately not without some changes, because the calling convention of the function when compiled in .Net is different to what the OS expects. Making those changes (see below) is not too difficult, but the real hard part is to locate those places (possibly among 100,000s of other lines of code) where callbacks are used.

Read more: Bugs revealed by X# in existing code - Part 3

In part 1 of this article series I have shown you 2 problems that we found in existing code (in our own code as well, and we thought that that code was perfect). Today we will discuss 3 other problems we have seen quite often. And yes, I found this in our own code as well.

Missing Assignment

Another common problem in existing code revealed by X# is with expressions that were unintentionally left incomplete, as demonstrated in this cut down sample:

#using System.Collections.Generic

FUNCTION Start() AS VOID
LOCAL
aMyDictionary AS Dictionary<STRING,STRING>
aMyDictionary := Dictionary<STRING,STRING>{}
aMyDictionary["myKey"] // incomplete code
// intended code was:
// aMyDictionary["myKey"] := “myValue”
? aMyDictionary["myKey"]

Vulcan did not report any error or warning about this, leading again to unintended behavior of the code at runtime (a runtime exception). Fortunately X# reports an error though: error XS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement which points us to the missing assignment in the code.

Read more: Bugs revealed by X# in existing code - Part 2

Last week I was on a holiday with my family in Normandy in France.
Some of the touristic highlights that we visited were of course the D-Day beaches Omaha, Utah, Juno, Gold and Sword where the allied forces started the liberation of the Northern part of Europ on June 6, 1944.
Near those beaches we also visited the war cemetaries where thousands of solders from the US, Britain, Canada and other countries were buried. This was very impressive as you can imagine.

The thought about these (mostly) young men that have given their lives to help liberate Europe, makes you feel very grateful and sad at the same time. You know that each person has left behind parents, or wife and children who have had to miss their loved sons or husbands and fathers.

At the same time the number of casualties was of course much smaller than the number of people that were killed by the occupiers. Compared to these numbers, the number of deceased allied soldiers was relatively low.

This reminded me of the famous quote from Spock in Vulcan "The needs of the wany outweigh the needs of the few, or the one".

I am sure that these soldiers and their families did not have something like that in mind, but I am glad that the allied leaders have chosen to liberate Europe, even when they could know that this would mean serious losses of allied lives.

Read more: The Needs of the Many...

As we announced a couple weeks ago, we have recently (to be precise, that was in Athens, at 20:56 of December 29th, 2016  smile achieved a major milestone for X#: Now the compiler can successfully compile all *valid* existing Vulcan code, or at least all existing code that we have tested the compiler with. And thanks to many of you who have provided us literally many 100s of thousands of lines of your code for our testing, we have tested the compiler against A LOT of existing code!

Of course we have tested the compiler also with our own code, like the source code for XIDE for example, which was previously written in Vulcan and even tested it with existing VO code that has not been ported to Vulcan at all. In the process, the stricter and more powerful X# compiler has revealed several problems in existing code (in the VO SDK, in the Vulcan version of the SDK, in ours and in other developers’ code), some of which should have never been allowed to compile before at all.
We have collected a of such problems, and we want to present them to you so you can learn from it

Assignment made to the same variable

This is by far the most common issue in existing code revealed by the X# compiler. Here is a simple case:

FUNCTION Absolute(nValue AS INT) AS INT
LOCAL
nRetValue AS INT
IF
nValue > 0
     nValue := nValue // typo, should be nRetValue := nValue
ELSE
     nRetValue := - nValue
END IF
RETURN
nRetValue

VO and Vulcan do not report an error or warning on this, while X# does report a warning: warning XS1717: Assignment made to same variable; did you mean to assign something else?

Read more: Bugs revealed by X# in existing code - part 1

We are reaching the end of the process to implement the BYOR (Bring Your Own Runtime) support and we are working on some of the more obscure things inside the compiler: implicit and explicit conversions and the related compiler options, such as /vo4 and /vo7.

Numeric comparisons

If you would ask any normal person which number is bigger: -1 or any arbitrary positive number, then most people will say that -1 is smaller. Now ask the same thing to a computer and you may receive diferent and unexpected answers. Consider the following example program:

FUNCTION Start AS VOID
    LOCAL dwValue AS DWORD
    LOCAL liValue AS LONG
    liValue := -1
    dwValue := 1
    ? liValue < dwValue
    RETURN

Compile this code in Vulcan and it will compile without warnings, but the result will be that liValue (-1) is NOT smaller than dwValue (1).

Read more: Dilemma: how compatible do you want to be?

This is a question, a lot of you are asking when they start the current version of Visual Studio Integration of XSharp; and I think you must have some explanations about it.

First, Intellisense is maybe not the right term to use, because it does cover a lot of features: So, you will have to make the distinction between Autocompletion, Parameters tooltips, Goto entity, Peek definition, Code snippets, …

And unfortunately, all that doesn’t comes automatically in VS : we have to provide the services.

Most of that was done by a LanguageService in VisualStudio, prior the 2015 version; but since then, when you look at the MSDN documentation, it is now related as legacy. Most of all, the vNext generation of Visual Studio is also introducing a new way of managing the project system, and in order to keep in touch with right technology, we must now implement these features as MEF.

MEF, the Managed Extensibility Framework, is feature introduced with .NET 4.0 that allows you to Import or Export classes as a provider, and have these automatically used by the consumer: Here, Visual Studio.

Ok, very interesting but Where is my Intellisense ?

Not too far, in fact : In order to provide Autocompletion, we must know what you are typing, in which context. So we need load references of your project and (using reflection) extract all information (Class/Method/Property/Parameters/…), then walk all your source code, and build a database of Elements using the XSharp compiler.

By the way, one element is how the Database is built : C++ is using an external database using SQLite, C# is using an In-Memory model, so we have to deal with speed and memory usage : We currently have made the choice to also have an In-Memory model, but may reconsider that if some users are running low on memory with big projects.

Creating and managing that was a bit long, but we now have a strong model and the first step will be soon available.

whereismyintellisense

In this Blog article I would like to share some of the dilemmas that we met when creating X#. Especially some dilemmas where we discovered that the VO/Vulcan compiler allowed things that other languages, such as C# and VB, did not allow.

Case 1

CLASS ParentClass
    VIRTUAL METHOD OnTest() AS STRING
        ? 'parent'
    RETURN 'parent'
END CLASS
CLASS ChildClass INHERIT ParentClass
    METHOD OnTest() AS STRING
        ? 'child'
    RETURN 'child'
END CLASS
FUNCTION Start() AS VOID
    LOCAL o AS ParentClass
    o:= ChildClass{ }
    ? o:OnTest()
RETURN

When you compile this code without any "special" compiler options what would you expect ?

Read more: Dilemmas: when is code correct ?