{-# LANGUAGE TemplateHaskell, QuasiQuotes #-} -- | -- Module : Language.C.Inline.ObjC.Marshal -- Copyright : [2013] Manuel M T Chakravarty -- License : BSD3 -- -- Maintainer : Manuel M T Chakravarty -- Stability : experimental -- Portability : non-portable (GHC extensions) -- -- Objective-C-specific marshalling functions. -- -- FIXME: Some of the code can go into a module for general marshalling, as only some of it is ObjC-specific. module Language.C.Inline.ObjC.Marshal ( -- * Auxilliary functions determineVarType, checkTypeName, -- * Determine corresponding foreign types of Haskell types haskellTypeToCType, haskellTypeNameToCType, -- * Marshaller types HaskellMarshaller, CMarshaller, -- * Compute bridging types and marshallers generateHaskellToCMarshaller, generateCToHaskellMarshaller ) where -- common libraries import Foreign.C as C import Foreign.C.String as C import Foreign.Marshal as C import Foreign.StablePtr as C import Language.Haskell.TH as TH import Language.Haskell.TH.Syntax as TH -- quasi-quotation libraries import Language.C.Quote as QC import Language.C.Quote.ObjC as QC import Text.PrettyPrint.Mainland as QC -- friends import Language.C.Inline.Error -- Auxilliary functions -- -------------------- -- Check that the given TH name is that of a Haskell variable and determine its type. -- determineVarType :: TH.Name -> Q TH.Type determineVarType vname = do { vinfo <- reify vname ; case vinfo of VarI _ ty _ _ -> return ty nonVarInfo -> do { reportErrorAndFail QC.ObjC $ "expected '" ++ show vname ++ "' to be a variable name, but it is " ++ show (TH.ppr nonVarInfo) } } -- Check that the given TH name is that of a Haskell type constructor. -- checkTypeName :: TH.Name -> Q () checkTypeName tyname = do { tyinfo <- reify tyname ; case tyinfo of TyConI (DataD {}) -> return () TyConI (NewtypeD {}) -> return () TyConI (TySynD {}) -> return () nonTyInfo -> do { reportErrorAndFail QC.ObjC $ "expected '" ++ show tyname ++ "' to be a type name, but it is " ++ show (TH.ppr nonTyInfo) } } -- Determine foreign types -- ----------------------- -- Determine the C type that we map a given Haskell type to. -- haskellTypeToCType :: QC.Extensions -> TH.Type -> Q QC.Type haskellTypeToCType lang (ListT `AppT` (ConT char)) | char == ''Char = haskellTypeNameToCType lang ''String haskellTypeToCType lang (ConT tc) = haskellTypeNameToCType lang tc haskellTypeToCType _lang ty = return [cty| typename HsStablePtr |] -- Determine the C type that we map a given Haskell type constructor to — i.e., we map all Haskell -- whose outermost constructor is the given type constructor to the returned C type.. -- haskellTypeNameToCType :: QC.Extensions -> TH.Name -> Q QC.Type haskellTypeNameToCType ObjC tyname | tyname == ''String = return [cty| typename NSString * |] | tyname == ''() = return [cty| void |] haskellTypeNameToCType _lang tyname = return [cty| typename HsStablePtr |] -- Determine marshallers and their bridging types -- ---------------------------------------------- -- Constructs Haskell code to marshal a value (used to marshal arguments and results). -- -- * The first argument is the code referring to the value to be marshalled. -- * The second argument is the continuation that gets the marshalled value as an argument. -- type HaskellMarshaller = TH.ExpQ -> TH.ExpQ -> TH.ExpQ -- Constructs C code to marshal an argument (used to marshal arguments and results). -- -- * The argument is the identifier of the value to be marshalled. -- * The result of the generated expression is the marshalled value. -- type CMarshaller = TH.Name -> QC.Exp -- Generate the type-specific marshalling code for Haskell to C land marshalling for a Haskell-C type pair. -- -- The result has the following components: -- -- * Haskell type after Haskell-side marshalling. -- * C type before C-side marshalling. -- * Generator for the Haskell-side marshalling code. -- * Generator for the C-side marshalling code. -- generateHaskellToCMarshaller :: TH.Type -> QC.Type -> Q (TH.TypeQ, QC.Type, HaskellMarshaller, CMarshaller) generateHaskellToCMarshaller hsTy cTy | cTy == [cty| typename NSString * |] = return ( [t| C.CString |] , [cty| char * |] , \val cont -> [| C.withCString $val $cont |] , \argName -> [cexp| [NSString stringWithUTF8String: $id:(show argName)] |] ) | cTy == [cty| typename HsStablePtr |] = return ( [t| C.StablePtr $(return hsTy) |] , cTy , \val cont -> [| do { C.newStablePtr $val >>= $cont } |] , \argName -> [cexp| $id:(show argName) |] ) | otherwise = reportErrorAndFail ObjC $ "cannot marshal '" ++ TH.pprint hsTy ++ "' to '" ++ prettyQC cTy ++ "'" -- Generate the type-specific marshalling code for Haskell to C land marshalling for a C-Haskell type pair. -- -- The result has the following components: -- -- * Haskell type after Haskell-side marshalling. -- * C type before C-side marshalling. -- * Generator for the Haskell-side marshalling code. -- * Generator for the C-side marshalling code. -- generateCToHaskellMarshaller :: TH.Type -> QC.Type -> Q (TH.TypeQ, QC.Type, HaskellMarshaller, CMarshaller) generateCToHaskellMarshaller hsTy cTy | cTy == [cty| typename NSString * |] = return ( [t| C.CString |] , [cty| char * |] , \val cont -> [| do { str <- C.peekCString $val; C.free $val; $cont str } |] , \argName -> let arg = show argName in [cexp| ({ typename NSUInteger maxLen = [$id:arg maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; char *buffer = malloc (maxLen); if (![$id:arg getCString:buffer maxLength:maxLen encoding:NSUTF8StringEncoding]) *buffer = '\0'; buffer; }) |] ) | cTy == [cty| typename HsStablePtr |] = return ( [t| C.StablePtr $(return hsTy) |] , cTy , \val cont -> [| do { C.deRefStablePtr $val >>= $cont } |] , \argName -> [cexp| $id:(show argName) |] ) | cTy == [cty| void |] = return ( [t| () |] , [cty| void |] , \val cont -> [| $cont $val |] , \argName -> [cexp| $id:(show argName) |] ) | otherwise = reportErrorAndFail ObjC $ "cannot marshall '" ++ prettyQC cTy ++ "' to '" ++ TH.pprint hsTy ++ "'"