{-# LANGUAGE GADTs, DataKinds, KindSignatures, PolyKinds, TypeFamilies, TypeOperators, RankNTypes, MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances, FlexibleContexts, ScopedTypeVariables, ExistentialQuantification #-} {-# LANGUAGE UndecidableInstances, TypeApplications, AllowAmbiguousTypes, TypeInType, TypeFamilyDependencies, FunctionalDependencies #-} module Clr.Bindings.Exception(catch) where import Clr import Clr.Bridge import Clr.Curry import Clr.Resolver import Clr.TypeString import Clr.Types import Clr.Bindings.BStr import Clr.Bindings.DynImports import Clr.Bindings.Object import Clr.Bindings.WrapImports import Clr.Host.BStr import Clr.Host.GCHandle import Clr.Marshal import Clr.MarshalF import Control.Concurrent import GHC.TypeLits import Data.Kind import Data.Typeable import Foreign.Ptr import Data.Text -- Synonyms to help prevent typos later on type T_Exception = T "System.Exception" '[] type T_Driver = T "Salsa.Driver" '[] type T_TryDelegate a = T "Salsa.TryDelegate" '[a] type T_CatchDelegate a ex = T "Salsa.CatchDelegate" '[a, ex] type T_RunCatchHandler a ex = T "RunCatchHandler" '[a, ex] -- Only 1 overload candidate for RunCatchHandler type instance Candidates T_Driver (T_RunCatchHandler a ex) = '[ '[ T_TryDelegate a, T_CatchDelegate a ex] ] -- We have 2 delegate types, 1 for the try handler instance Delegate (T_TryDelegate a) where type DelegateArgTypes (T_TryDelegate a) = '[] type DelegateResultType (T_TryDelegate a) = a -- And another for the catch handler instance Delegate (T_CatchDelegate a ex) where type DelegateArgTypes (T_CatchDelegate a ex) = '[ ex ] type DelegateResultType (T_CatchDelegate a ex) = a -- Foreign imports for non primitive results foreign import ccall "wrapper" wrapTryDelegateObj :: (IO (GCHandle a)) -> IO (FunPtr (IO (GCHandle a))) foreign import ccall "wrapper" wrapCatchDelegateObj :: (GCHandle ex -> IO (GCHandle a)) -> IO (FunPtr (GCHandle ex -> IO (GCHandle a))) foreign import ccall "dynamic" makeRunCatchHandlerObj :: FunPtr (GCHandle tryD -> GCHandle catchD -> IO (GCHandle a)) -> (GCHandle tryD -> GCHandle catchD -> IO (GCHandle a)) -- Foreign imports for string results foreign import ccall "wrapper" wrapTryDelegateBStr :: (IO BStr) -> IO (FunPtr (IO BStr)) foreign import ccall "wrapper" wrapCatchDelegateBStr :: (GCHandle ex -> IO BStr) -> IO (FunPtr (GCHandle ex -> IO BStr)) foreign import ccall "dynamic" makeRunCatchHandlerBStr :: FunPtr (GCHandle tryD -> GCHandle catchD -> IO BStr) -> (GCHandle tryD -> GCHandle catchD -> IO BStr) -- TODO: foreign imports for other prims -- Run catch handler is a static method of 2 args with this result type instance MethodResultS2 T_Driver (T_RunCatchHandler a ex) (T_TryDelegate a) (T_CatchDelegate a ex) where type ResultTypeS2 T_Driver (T_RunCatchHandler a ex) (T_TryDelegate a) (T_CatchDelegate a ex) = a -- Wrapper / Dyn import mappings for object result type instance {-# OVERLAPS #-} (IsPrimType a ~ 'False, BridgeType a ~ GCHandle a) => WrapperImport (T_TryDelegate a) where wrapperImport = wrapTryDelegateObj instance {-# OVERLAPS #-} (IsPrimType a ~ 'False, BridgeType a ~ GCHandle a, BridgeType ex ~ GCHandle ex) => WrapperImport (T_CatchDelegate a ex) where wrapperImport = wrapCatchDelegateObj instance {-# OVERLAPS #-} (IsPrimType a ~ 'False, BridgeType a ~ GCHandle a) => MethodDynImportS2 T_Driver (T_RunCatchHandler a ex) (T_TryDelegate a) (T_CatchDelegate a ex) where methodDynImportS2 = makeRunCatchHandlerObj -- Wrapper / Dyn import mappings for string result type instance {-# OVERLAPS #-} WrapperImport (T_TryDelegate T_string) where wrapperImport = wrapTryDelegateBStr instance {-# OVERLAPS #-} (BridgeType ex ~ GCHandle ex) => WrapperImport (T_CatchDelegate T_string ex) where wrapperImport = wrapCatchDelegateBStr instance {-# OVERLAPS #-} MethodDynImportS2 T_Driver (T_RunCatchHandler T_string ex) (T_TryDelegate T_string) (T_CatchDelegate T_string ex) where methodDynImportS2 = makeRunCatchHandlerBStr -- TODO: Wrapper / Dyn import mappings for other prims -- The function that is exported catch :: forall haskResult t_dTry t_dCatch t_handler t_ex t_args t_result b_try b_catch b_result . -- Types we know the most about ( t_ex `Implements` T_Exception ~ 'True , t_dTry ~ (T_TryDelegate t_result) , t_dCatch ~ (T_CatchDelegate t_result t_ex) , t_handler ~ (T_RunCatchHandler t_result t_ex) -- Common contraints between types , IsPrimType t_ex ~ 'False , BridgeType t_ex ~ GCHandle t_ex , TString t_result , TString t_ex , t_result ~ HaskToClr haskResult , BridgeType t_result ~ b_result , Unmarshal haskResult b_result , Unmarshal b_result haskResult -- Constraints for the try delegate , t_dTry ~ (T_TryDelegate t_result) , Delegate t_dTry , DelegateArity t_dTry ~ 0 , DelegateConstructorN 0 t_dTry , DelegateBridgeType t_dTry ~ b_try , DelegateResultType t_dTry ~ t_result , MarshalF 0 (IO haskResult) b_try -- Constraints for the catch delegate , t_dCatch ~ (T_CatchDelegate t_result t_ex) , Delegate t_dCatch , DelegateArity t_dCatch ~ 1 , DelegateConstructorN 1 t_dCatch , DelegateBridgeType t_dCatch ~ b_catch , DelegateResultType t_dCatch ~ t_result , MarshalF 1 (Object t_ex -> IO haskResult) b_catch -- Constraints for the RunCatchHandler static method , t_args ~ '[ t_dTry, t_dCatch ] , ResolveMember t_args (Candidates T_Driver t_handler) ~ t_args , MethodS 2 T_Driver t_handler t_args ) => IO haskResult -> (Object t_ex -> IO haskResult) -> IO haskResult catch tryF catchF = do tryD <- makeTryDelegate tryF catchD <- makeCatchDelegate catchF runCatchHandler (tryD, catchD) -- How to build a try delegate makeTryDelegate :: forall t_dTry b_try t_result haskResult . ( t_dTry ~ (T_TryDelegate t_result) , Delegate t_dTry , DelegateArity t_dTry ~ 0 , DelegateConstructorN 0 t_dTry , DelegateBridgeType t_dTry ~ b_try , DelegateResultType t_dTry ~ t_result , MarshalF 0 (IO haskResult) b_try , TString t_result ) => IO haskResult -> IO (Object (T_TryDelegate t_result)) makeTryDelegate f = delegate @t_dTry f -- How to build a catch delegate makeCatchDelegate :: forall t_dCatch b_catch t_result haskResult t_ex . ( t_ex `Implements` T_Exception ~ 'True , t_dCatch ~ (T_CatchDelegate t_result t_ex) , Delegate t_dCatch , DelegateArity t_dCatch ~ 1 , DelegateConstructorN 1 t_dCatch , DelegateBridgeType t_dCatch ~ b_catch , DelegateResultType t_dCatch ~ t_result , MarshalF 1 (Object t_ex -> IO haskResult) b_catch , TString t_result , TString t_ex , IsPrimType t_ex ~ 'False , BridgeType t_ex ~ GCHandle t_ex , t_result ~ HaskToClr haskResult , Unmarshal haskResult (BridgeType t_result) ) => (Object t_ex -> IO haskResult) -> IO (Object (T_CatchDelegate t_result t_ex)) makeCatchDelegate f = delegate @t_dCatch f -- The static method called to run this runCatchHandler :: forall b_result haskResult t_result t_ex t_args t_dTry t_dCatch t_handler . ( t_ex `Implements` T_Exception ~ 'True , t_dTry ~ (T_TryDelegate t_result) , t_dCatch ~ (T_CatchDelegate t_result t_ex) , t_handler ~ (T_RunCatchHandler t_result t_ex) , t_args ~ '[ t_dTry, t_dCatch ] , ResolveMember t_args (Candidates T_Driver t_handler) ~ t_args , MethodS 2 T_Driver t_handler t_args , BridgeType t_result ~ b_result , Unmarshal b_result haskResult ) => (Object (T_TryDelegate t_result), Object (T_CatchDelegate t_result t_ex)) -> IO haskResult runCatchHandler (tryDel, catchDel) = invokeS @t_handler @T_Driver @b_result @haskResult (tryDel, catchDel)