Skip to content
This repository has been archived by the owner on Nov 3, 2023. It is now read-only.

inconsistent behavior when using ctype with boxing object #34

Open
rolfbjarne opened this issue May 22, 2018 · 0 comments
Open

inconsistent behavior when using ctype with boxing object #34

rolfbjarne opened this issue May 22, 2018 · 0 comments

Comments

@rolfbjarne
Copy link
Member

rolfbjarne commented May 22, 2018

Submitted by a community member on 2013-11-15 16:11 UTC

when moving my code to mono, I found an inconsistent behavior when ctype vb.net, similar as (TYPE)obj in c# between Microsoft .net framework and mono. the basic case is following,

Public Module main
    Public Function cast(Of t)(ByVal i As Object, ByRef o As t) As Boolean
        If i Is Nothing Then
            Console.WriteLine("cast as Nothing")
            o = Nothing
            Return True
        Else
            Try
                o = DirectCast(i, t)
                Console.WriteLine("cast as DirectCast")
                Return True
            Catch
                Try
                    o = CType(i, t)
                    Console.WriteLine("cast as CType")
                    Return True
                Catch
                    o = Nothing
                    Console.WriteLine("cast failed")
                    Return False
                End Try
            End Try
            '1. may not be able to CType(i, t)
            '2. may not be able to GetType(t).IsValueType?
            'On Error Resume Next
            'o = CType(i, t)
            'Return Not o Is Nothing OrElse GetType(t).IsValueType
        End If
    End Function

    Public Function cast(Of t)(ByVal i As Object) As t
        Dim o As t
        If cast(Of t)(i, o) Then
            Return o
        Else
            Return Nothing
        End If
    End Function

    Public Sub Main()
        Dim i As Int64 = 100
        Console.WriteLine(CType(i, Byte))
        Console.WriteLine(CType(i, SByte))
        Console.WriteLine(CType(i, Int32))
        Console.WriteLine(CType(i, UInt32))
        Console.WriteLine(CType(i, Int64))
        Console.WriteLine(CType(i, UInt64))

        Console.WriteLine("----")

        Console.WriteLine(cast(Of Byte)(i))
        Console.WriteLine(cast(Of SByte)(i))
        Console.WriteLine(cast(Of Int32)(i))
        Console.WriteLine(cast(Of UInt32)(i))
        Console.WriteLine(cast(Of Int64)(i))
        Console.WriteLine(cast(Of UInt64)(i))

        Console.WriteLine("----")

        Console.WriteLine("CType(i, Object) Is Nothing ? " + Convert.ToString(CType(i, Object) Is Nothing))
    End Sub
End Module

when calling the cast function surely it's not necessary to do the casting in this way, but it's a part of generic classes, while I do not know the exactly type when it is been used.>, the Int64 i should be boxed as an object, and in this situation, the ctype behavior is inconsistent.
following the output in .net and mono
.net
100
100
100
100
100
100
----
cast as CType
100
cast as CType
100
cast as CType
100
cast as CType
100
cast as DirectCast
100
cast as CType
100
----
CType(i, Object) Is Nothing ? False

mono
100
100
100
100
100
100
----
cast failed
0
cast failed
0
cast failed
0
cast failed
0
cast as DirectCast
100
cast failed
0
----
CType(i, Object) Is Nothing ? False

it looks like CType function failed to detect the real object type after boxing.
the compile command is pretty simple as vbc ctype.vb.
the .net version i am running is 3.5, but i also tested it on 4.0, with same output.
the mono version i am running is

Mono JIT compiler version 2.10.8.1 (Debian 2.10.8.1-5ubuntu1)
Copyright (C) 2002-2011 Novell, Inc, Xamarin, Inc and Contributors. www.mono-project.com
        TLS:           __thread
        SIGSEGV:       altstack
        Notifications: epoll
        Architecture:  x86
        Disabled:      none
        Misc:          softdebug
        LLVM:          supported, not enabled.
        GC:            Included Boehm (with typed GC and Parallel Mark)

please kindly let me know if it's a known issue, thank you in advanced.


Xamarin Bugzilla comment 1 by Zoltan Varga [MSFT] on 2013-11-16 02:49 UTC

Could you attach the compiled executable ?

Xamarin Bugzilla comment 2 by a community member on 2013-11-16 05:01 UTC

Created attachment 5456
source code w/ binary

Xamarin Bugzilla comment 3 by Zoltan Varga [MSFT] on 2013-11-16 18:10 UTC

-> vb

Xamarin Bugzilla comment 4 by a community member on 2013-11-18 07:11 UTC

as i have tried boxing + ctype, it's more likely the bug is coming from generics instead of runtime.

Xamarin Bugzilla comment 5 by Zoltan Varga [MSFT] on 2013-11-18 09:52 UTC

The problem is probably in the Microsoft.VisualBasic.CompilerServices.Conversions::ToGenericParameter<!!0>(object) method which is called by the compiled executable to do the casting.

Xamarin Bugzilla comment 6 by a community member on 2013-11-25 09:05 UTC

is there anyway i can help to resolve the issue?
thank you in advanced.

Xamarin Bugzilla comment 7 by Zoltan Varga [MSFT] on 2013-11-25 10:01 UTC

The vb compiler/class libs are not heavily maintained nowdays, so it might take some time before this gets fixed.

Xamarin Bugzilla comment 8 by a community member on 2013-11-25 23:23 UTC

yes, as a dev myself, it would be great if i can help from source level directly, any information about how to start?

Xamarin Bugzilla comment 9 by Zoltan Varga [MSFT] on 2013-11-26 02:07 UTC

https://github.com/mono/mono-basic

Xamarin Bugzilla comment 10 by a community member on 2013-11-27 10:16 UTC

thank you Zoltan, I will have a look soon.

Xamarin Bugzilla comment 11 by a community member on 2013-12-14 12:23 UTC

as far as my investigation, this bug is not from the Conversions class, but as i have moved the code from vb.net to c#, it works well,

using System;

public static class Program
{
    public static bool cast<T>(object i, out T o)
    {
        if(i == null)
        {
            Console.WriteLine("cast as null");
            o = default(T);
            return true;
        }
        else
        {
            try
            {
                o = (T)i;
                Console.WriteLine("cast as (T)");
                return true;
            }
            catch
            {
                try
                {
                    o = (T)(dynamic)i;
                    Console.WriteLine("cast as (T)(dynamic)");
                    return true;
                }
                catch
                {
                    o = default(T);
                    Console.WriteLine("cast failed");
                    return false;
                }
            }
        }
    }

    public static T cast<T>(object i)
    {
        T o;
        if(cast<T>(i, out o)) return o;
        else return default(T);
    }

    public static void Main()
    {
        long i = 100;
        Console.WriteLine((byte)i);
        Console.WriteLine((sbyte)i);
        Console.WriteLine((int)i);
        Console.WriteLine((uint)i);
        Console.WriteLine((long)i);
        Console.WriteLine((ulong)i);

        Console.WriteLine("----");

        Console.WriteLine(cast<byte>(i));
        Console.WriteLine(cast<sbyte>(i));
        Console.WriteLine(cast<int>(i));
        Console.WriteLine(cast<uint>(i));
        Console.WriteLine(cast<long>(i));
        Console.WriteLine(cast<ulong>(i));

        Console.WriteLine("----");
    }
}

so it is a VBRuntime specific issue, but i am still working on finding out the implementation of CType function

Xamarin Bugzilla comment 12 by Rolf Bjarne Kvinge [MSFT] on 2013-12-16 08:27 UTC

CType is not one function in particular, it's treated specially by the vb compiler depending no the types you're trying to convert.

In this particular case it's converted into a call to Microsoft.VisualBasic.CompilerServices.Conversions::ToGenericParameter, which you can find here: https://github.com/mono/mono-basic/blob/master/vbruntime/Microsoft.VisualBasic/Microsoft.VisualBasic.CompilerServices/Conversions.vb#L127

Xamarin Bugzilla comment 13 by a community member on 2013-12-16 10:07 UTC

Thank you for the information Rolf,
to be clear, the binary is built in Windows with vbc, instead of mono compiler. so it should have no dependencies to mono VB compiler.

but since the ToGenericParameter function is in runtime, it should be called during runtime.
i have had a look at the ToGenericParameter function, it directly calls DirectCast, which is AFAIK, not correct, since boxed object can not be direct cast to other types as the following logic shows.

Imports System

Public Module _Main
    Public Sub Main()
        Dim a As Integer = 100
        Dim b As Object = a
        Console.WriteLine(DirectCast(b, Integer))
        Console.WriteLine(DirectCast(b, Long))    'will fail w/ exception
    End Sub
End Module

so in this case, is it correct we should let ToGenericParameter to unbox the object if it's indeed a value type?

Xamarin Bugzilla comment 14 by a community member on 2013-12-23 07:13 UTC

so in this case, we could change the implementation of ToGenericParameter function from DirectCast to Convert.ChangeType. i have changed the test case a little bit, while since the ctype in mono is not working well, using Convert.ChangeType works. is there anyway i can push the change to mono?

Public Module main
    Public Function cast(Of t)(ByVal i As Object, ByRef o As t) As Boolean
        If i Is Nothing Then
            Console.WriteLine("cast as Nothing")
            o = Nothing
            Return True
        Else
            Console.WriteLine(i.GetType().FullName())
            Try
                o = DirectCast(i, t)
                Console.WriteLine("cast as DirectCast")
                Return True
            Catch
                Try
                    o = Convert.ChangeType(i, GetType(t))
                    Console.WriteLine("cast as Convert.ChangeType")
                    Return True
                Catch
                    Try
                        o = CType(i, t)
                        Console.WriteLine("cast as CType")
                        Return True
                    Catch
                        o = Nothing
                        Console.WriteLine("cast failed")
                        Return False
                    End Try
                End Try
            End Try
            '1. may not be able to CType(i, t)
            '2. may not be able to GetType(t).IsValueType?
            'On Error Resume Next
            'o = CType(i, t)
            'Return Not o Is Nothing OrElse GetType(t).IsValueType
        End If
    End Function

    Public Function cast(Of t)(ByVal i As Object) As t
        Dim o As t
        If cast(Of t)(i, o) Then
            Return o
        Else
            Return Nothing
        End If
    End Function

    Public Sub Main()
        Dim i As Int64 = 100
        Console.WriteLine(CType(i, Byte))
        Console.WriteLine(CType(i, SByte))
        Console.WriteLine(CType(i, Int32))
        Console.WriteLine(CType(i, UInt32))
        Console.WriteLine(CType(i, Int64))
        Console.WriteLine(CType(i, UInt64))

        Console.WriteLine("----")

        Console.WriteLine(cast(Of Byte)(i))
        Console.WriteLine(cast(Of SByte)(i))
        Console.WriteLine(cast(Of Int32)(i))
        Console.WriteLine(cast(Of UInt32)(i))
        Console.WriteLine(cast(Of Int64)(i))
        Console.WriteLine(cast(Of UInt64)(i))

        Console.WriteLine("----")

        Console.WriteLine("CType(i, Object) Is Nothing ? " +

        Convert.ToString(CType(i, Object) Is Nothing))
    End Sub
End Module

Reference: https://bugzilla.xamarin.com/show_bug.cgi?id=16282

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant