Monday, April 16, 2007

Ain't Numeric

Today I learned something: don't trust a compiler. I repeat: don't ever trust a compiler. It might be doing some magic behind your back.

A while ago I was assigned a bug, at first it looked really easy, but no matter what I tried I couldn't reproduce it, and I was positive the reporter was experiencing the problem. So I just left the bug standing there, and of course, eventually it hit me.

Quiz: If you compile the following source code, what will the IL look like?

Class AintNumeric
    Shared Sub Main
    End Sub
End Class

The problem was that this printed "False" on Mono and "True" on MS. I compiled the code with vbnc, ran the code on Mono and it printed "True". I ran it on MS and it printed "True". I disassembled the program to see if the compiler was doing something wrong, and it wasn't. I put Console.WriteLines in MS.VB.Information.IsNumeric and they all confirmed that the function was working like it should. Just to check I compiled the program with vbc, ran it on MS and it printed "True".

You might have seen what I missed, but I'll tell you anyway: I didn't run the MS compiled program on Mono. My bad, the reported of the bug explicitly wrote that he did it, but I honestly didn't think it would change anything. vbnc was compiling everything correctly, wasn't it? 

Well no, and now here is the answer to the quiz:

.method public static void Main() cil managed {
    .maxstack 8
    L_0000: ldstr "0"
    L_0005: call bool     [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Versioned::IsNumeric(object)
    L_000a: call void [mscorlib]System.Console::WriteLine(bool)
    L_000f: ret

The Microsoft VB8 compiler changes every method call to Microsoft.VisualBasic.Information.IsNumeric to Microsoft.VisualBasic.CompilerServices.Versioned.IsNumeric. And of course, that function was buggy in Mono...

Now you may think about it and find this is some really bad behaviour, but it's all because of backwards compatibility. When VB 8 came out they added support for unsigned types, which is the reason behind this: The old version of IsNumeric returns false for unsigned numeric types... and since that couldn't change it because it would break backwards compatibility, and they didn't want to add another function handling the new data types (and telling everybody they had to use the new funcion), they created some compiler magic.

And yes, the bug is fixed and vbnc does the same magic now.

No comments:

Post a Comment