packages feed

clr-bindings-0.2.0: src/Clr/Bindings/Object/Internal.hs

{-# LANGUAGE TypeInType, TypeApplications, TypeSynonymInstances, MultiParamTypeClasses, FlexibleInstances, TypeFamilies #-}

module Clr.Bindings.Object.Internal
  ( typeGetType
  , typeIsAssignableFrom
  , T_Type
  , T_ToString
  , T_GetType
  ) where

import Clr
import Clr.Bridge
import Clr.Resolver
import Clr.TypeString

import Clr.Marshal

import Clr.Host
import Clr.Host.BStr
import Clr.Host.GCHandle

import Clr.Bindings.DynImports
import Clr.Bindings.BStr

import Data.Coerce
import Foreign.Ptr
import Foreign.ForeignPtr

import Data.Text as T

--
-- Synonyms for other types referenced here
--
type T_Type = T "System.Type" '[]

--
-- Object methods
--
type T_Equals              = T "Equals" '[]
type T_Finalize            = T "Finalize" '[]
type T_GetHashCode         = T "GetHashCode" '[]
type T_MemberwiseClone     = T "MemberwiseClone" '[]
type ReferenceEquals       = T "ReferenceEquals" '[]
type T_ToString            = T "ToString" '[]

--
-- Type members
--
type T_FullName            = T "FullName" '[]
type T_GetGenericArguments = T "GetGenericArguments" '[]
type T_GetMembers          = T "GetMembers" '[]
type T_IsAssignableFrom    = T "IsAssignableFrom" '[]
type T_Namespace           = T "Namespace" '[]

-- Members that share the same name on both Object & Type
type T_GetType             = T "GetType"  '[]

type instance SuperTypes T_object = '[ ]

--
-- Member instance declarations. Only are few are used internally, but the others must be declared also
-- as Haskell always exports & imports instances. The import generator must then skip over these.
--
type instance Members T_object = '[ T_ToString, T_GetType ]

-- Doesn't yet conver all members of System.Type. TODO
type instance Members T_Type   = '[ T_FullName
                                  , T_GetGenericArguments
                                  , T_GetMembers
                                  , T_GetType
                                  , T_IsAssignableFrom
                                  , T_Namespace              ]

type instance Candidates T_object T_ToString          = '[ '[ ] ]
type instance Candidates T_object T_GetType           = '[ '[ ] ]
type instance Candidates T_Type   T_GetType           = '[ '[ T_string ] ]
type instance Candidates T_Type   T_IsAssignableFrom  = '[ '[ T_Type ] ]

foreign import ccall "dynamic" makeObjToString          :: FunPtr (GCHandle t -> IO BStr) -> (GCHandle t -> IO BStr)
foreign import ccall "dynamic" makeObjGetType           :: FunPtr (GCHandle t -> IO (GCHandle (T "System.Type" '[]))) -> (GCHandle t -> IO (GCHandle (T "System.Type" '[])))
foreign import ccall "dynamic" makeTypeGetType          :: FunPtr (BStr -> IO (GCHandle T_Type)) -> (BStr -> IO (GCHandle T_Type))
foreign import ccall "dynamic" makeTypeIsAssignableFrom :: FunPtr (GCHandle T_Type -> GCHandle T_Type -> IO Bool) -> (GCHandle T_Type -> GCHandle T_Type -> IO Bool)

instance MethodResultI1 T_object T_ToString () where
  type ResultTypeI1 T_object T_ToString () = T_string

instance MethodResultI1 T_object T_GetType () where
  type ResultTypeI1 T_object T_GetType () = T_Type

instance MethodResultS1 T_Type T_GetType T_string where
  type ResultTypeS1 T_Type T_GetType T_string = T_Type

instance MethodResultI1 T_Type T_IsAssignableFrom T_Type where
  type ResultTypeI1 T_Type T_IsAssignableFrom T_Type = T_bool

instance MethodDynImportI1 T_object T_ToString () where
  methodDynImportI1 = makeObjToString

instance MethodDynImportI1 T_object T_GetType () where
  methodDynImportI1 = makeObjGetType

instance MethodDynImportS1 T_Type T_GetType T_string where
  methodDynImportS1 = makeTypeGetType

instance MethodDynImportI1 T_Type T_IsAssignableFrom T_Type where
  methodDynImportI1 = makeTypeIsAssignableFrom

--
-- System.Type.GetType(System.String)
--
typeGetType :: T.Text -> IO (Object T_Type)
typeGetType = invokeS @T_GetType @T_Type

--
-- System.Type.IsAssignableFrom(System.Type)
--
typeIsAssignableFrom :: Object T_Type -> Object T_Type -> IO (Bool)
typeIsAssignableFrom t1 t2 = invokeI @T_IsAssignableFrom t1 t2

--
-- Marshaling Object <-> GCHandle
--

-- clr-typed doesn't know of GCHandle as it is provided by clr-host
type instance BridgeTypeObject t = GCHandle t

-- Calling a CLR function from Haskell
instance {-# OVERLAPS #-} Marshal (Object t) (GCHandle t) where
  marshal (Object x) f = withForeignPtr x $ \x'-> f $ coerce x'

-- Calling a Haskell function from the CLR
instance {-# OVERLAPS #-} (TString t) => Marshal (GCHandle t) (Object t) where
  marshal x f = do
    x' <- newHandle x
    finalizer <- gcHandleFinalizer
    fp <- newForeignPtr finalizer (coerce x')
    f $ Object fp

-- Returning from a CLR function that was called from Haskell
instance {-# OVERLAPPING #-} (TString t) => Unmarshal (GCHandle t) (Object t) where
  unmarshal x = do
    finalizer <- gcHandleFinalizer
    fp <- newForeignPtr finalizer (coerce x)
    return $ Object fp

-- Returning from a Haskell function that was called by the CLR
instance {-# OVERLAPPING #-} (TString t) => Unmarshal (Object t) (GCHandle t) where
  unmarshal (Object x) = withForeignPtr x $ \x'-> newHandle $ coerce x'