% % (c) The Foo Project, University of Glasgow, 1999 % % @(#) $Docid: Dec. 9th 2003 09:07 Sigbjorn Finne $ % @(#) $Contactid: sof@galois.com $ % Higher-level marshalling code - working over Core IDL constructs. (That's the Official Line - the Real Reason for this module is that it avoids creating a mutual dependency between MarshallType and MarshallDep.) \begin{code}
module MarshallCore
       (
         toHaskellMethodTy
       , toHaskellTy
       , paramToHaskellType

       , mkHStructDef
       , mkHEnumDef
       , mkHUnionDef
       , mkCUnionDef

       , toHaskellBaseTy
       , toBaseTy
       , toHaskellBaseMethodTy

       , mkMarshaller
       
       , autoTypeToHaskellTy
       , autoTypeToQName
       , mbAutoTypeToHaskellTy
       
       , constrainIIDParams
       
       ) where

import qualified AbstractH as Haskell ( Type, ConDecl, Context )
import AbsHUtils

import CoreIDL
import CoreUtils

import MarshallUtils

import BasicTypes
import Attribute
import Literal
import LibUtils
import PpCore
import Utils    ( notNull, trace )
import Opts
import TypeInfo ( TypeInfo(..) )

import Maybe
import List     ( nub )

\end{code} Converting a interface method signature into its corresponding Haskell type. This means taking into consideration the presence of [out] parameters plus dependent arguments etc. \begin{code}
toHaskellMethodTy :: Bool
		  -> Bool
		  -> Bool
	          -> Maybe Haskell.Type
		  -> [Param]
		  -> Result
		  -> (Haskell.Type, Maybe Haskell.Context)
toHaskellMethodTy isPure isServer isAuto mb_iface_ty params result 
  = case generaliseTys (mb_io_res_ty: the_param_tys) of
     ((r:ps), mb_c) -> (funTys ps r, mb_c)
     _              -> error "MarshallCore.toHaskellMethodTy: unexpected result"		       
  where
   mb_io_res_ty
     | isPure    = the_res_ty
     | otherwise = io the_res_ty
     
   (pars, _, _, _,res)     = binParams params
   (real_params, par_deps) = findParamDependents False pars
   (_, res_deps)           = findParamDependents False res

   the_param_tys =
     case mb_iface_ty of
       Nothing -> param_tys
       Just x  -> param_tys ++ [x]

   (param_tys, res_tys) = 
       constrainIIDParams (paramToHaskellType par_deps isServer isAuto False)
       			  (paramToHaskellType res_deps isServer isAuto True)
			  real_params
			  res_params

   res_params = removeDependees res_deps res

   the_res_ty =
     tuple (
      case (resultOrigType result) of
	t  | isHRESULTTy t && not optKeepHRESULT -> res_tys
	   | not (isVoidTy (removeNames t)) -> (res_tys ++ [toHaskellTy False (resultOrigType result)])
	   | otherwise   -> res_tys)
     

constrainIIDParams :: (Param -> Haskell.Type)
		   -> (Param -> Haskell.Type)
		   -> [Param]
		   -> [Param]
		   -> ([Haskell.Type], [Haskell.Type])
constrainIIDParams paramToType resultToType params res 
  | optUseIIDIs = (param_tys, res_tys)
  | otherwise   = (param_tys_vanilla, res_tys_vanilla)
 where
   toIIDTyVar p ty = 
      case lookup (idName (paramId p)) iidIs_vars of
       Just x -> replaceTyVar (mkTyCon iUnknown [uniqueTyVar ('i':show x)]) ty
       _      -> toIIDTyVarRes p ty

   toIIDTyVarRes p ty =
       case findAttribute "iid_is" (idAttributes (paramId p)) of
	  Just (Attribute _ [ParamVar v]) -> 
	     case lookup v iidIs_vars of
	        Just x -> replaceTyVar (uniqueTyVar ('i':show x)) ty
		_ -> ty
          _ -> ty
	    
   iidIs_vars = zip (nub (catMaybes (map isIIDDep res)))
   		    [(0::Int)..]
     where
      isIIDDep p = 
        case findAttribute "iid_is" (idAttributes (paramId p)) of
	  Just (Attribute _ [ParamVar v]) -> Just v
	  _ -> Nothing

   param_tys = zipWith toIIDTyVar    params param_tys_vanilla
   res_tys   = zipWith toIIDTyVarRes res    res_tys_vanilla
   
   param_tys_vanilla = map paramToType  params
   res_tys_vanilla   = map resultToType res

\end{code} @toHaskellTy@ takes care of implementing the T[] translation scheme. It differs from @toHaskellBaseTy@ in that we're mapping to the user-level representation of the IDL type in Haskell, not the type of its marshalled/packed representation. \begin{code}
toHaskellTy :: Bool -> Type -> Haskell.Type
toHaskellTy isGround ty =
 case ty of
   Integer sz s    -> mkIntTy sz s
   Float sz        -> mkFloatTy sz
   Char signed
     | optJNI      -> tyWord16
     | otherwise   -> mkCharTy signed
   WChar           -> tyWChar
   Bool            -> tyBool
   Void            -> tyUnit
   Octet           -> tyWord8
   Any             -> tyAddr
   Object          -> tyAddr
   StablePtr	   -> tyStable
   FunTy _ res ps  -> 
   	case (toHaskellMethodTy False isGround False Nothing ps res') of
	  (t, Nothing) -> t
	  (t, Just c)  -> ctxtTyApp c t
     where
      res' = res{ resultType     = removePtr (resultType res)
	        , resultOrigType = removePtr (resultOrigType res)
	        }
 
   String _ isUnique _ -> (if isUnique then tyMaybe else id) tyString
   WString isUnique _  -> (if isUnique then tyMaybe else id) tyWString
   Sequence t _ _      -> tyList (toHaskellTy isGround t)
   Fixed{}             -> error "not implemented yet."
   Name _ _ _ _ (Just o@Iface{}) _ -> toHaskellTy isGround o
   Name _ _ _ _ _ (Just ti) ->
   	case mkTyConst (haskell_type ti) of
	   t | optCom && isTyVar t -> 
			if isGround then
			   mkTyConst vARIANT
			else
			   ctxtTyApp (ctxtClass variantClass [t]) t
	     | otherwise           -> t
   Name nm _ md _ _ _ -> tyQConst (fmap mkHaskellTyConName md) (mkHaskellTyConName nm)
   SafeArray t 
     | isGround  -> mkTyConst sAFEARRAY
     | otherwise -> tyQCon autoLib "SafeArray" [toHaskellTy isGround t]
   Array Void _ -> tyList (toHaskellTy isGround (Pointer Ptr True Void))
   Array t _ 
     | optJNI    -> mkTyCon jArray [toHaskellTy isGround t]
     | otherwise -> tyList (toHaskellTy isGround t)

   Pointer Unique isExp (Iface nm md _ _ _ _)
     | optCom && isExp -> tyQCon prelude "Maybe" [tyQCon md nm [mkTyConst groundInterface]]
     | optCom          -> tyQCon md nm [iface_ptr_ty_arg]
{-
   Pointer Unique isExp (Iface nm mod _ attrs _ _) 
     | optJNI && attrs `hasAttributeWithName` "jni_iface_ty" -> 
     	        let i = tyVar "a" in
		mkTyCon jObject
			[ ctxtTyApp (ctxtClass (mkQualName mod nm) [mkTyCon jObject [i]]) i]

     | optSubtypedInterfacePointers -> 
		     -- Pointer to anything interface'ish is an interface pointer. Period.
                     tyQCon prelude "Maybe" [tyQCon mod nm [mkTyConst groundInterface]]
		     --tyQCon mod nm [iface_ptr_ty_arg]
     | otherwise		    -> tyQCon prelude "Maybe" [tyQConst mod nm]
-}
{- moved down
   Pointer _ isExp (Iface nm mod _ attrs _ _)
     | optCorba         -> tyQCon  mod nm [iface_ptr_ty_arg]
	-- what's the IU/ID bit? Needed for processing AutoPrim.idl without
	-- a hitch. ToDo: remove it.
     | optHaskellToC && not (nm `elem` ["IUnknown", "IDispatch"]) -> tyQConst mod nm
     | optJNI && attrs `hasAttributeWithName` "jni_iface_ty" -> 
     	        let i = tyVar "a" in
		mkTyCon jObject
			[ ctxtTyApp (ctxtClass (mkQualName mod nm) [mkTyCon jObject [i]]) i ]
     | optSubtypedInterfacePointers -> tyQCon  mod nm [iface_ptr_ty_arg]
     | otherwise		    -> tyQConst mod nm
-}
   Pointer Unique _ (Iface nm md _ attrs _ _) 
     | optJNI && attrs `hasAttributeWithName` "jni_iface_ty" -> 
     	        let i = tyVar "a" in
		mkTyCon jObject
			[ ctxtTyApp (ctxtClass (mkQualName md nm) [mkTyCon jObject [i]]) i]

     | optSubtypedInterfacePointers -> 
		     -- Pointer to anything interface'ish is an interface pointer. Period.
                     tyQCon prelude "Maybe" [tyQCon md nm [mkTyConst groundInterface]]
		     --tyQCon md nm [iface_ptr_ty_arg]
     | otherwise		    -> tyQCon prelude "Maybe" [tyQConst md nm]
   Pointer _ _ (Iface nm md _ attrs _ _)
     | optCorba         -> tyQCon  md nm [iface_ptr_ty_arg]
	-- what's the IU/ID bit? Needed for processing AutoPrim.idl without
	-- a hitch. ToDo: remove it.
     | optHaskellToC && not (nm `elem` ["IUnknown", "IDispatch"]) -> tyQConst md nm
     | optJNI && attrs `hasAttributeWithName` "jni_iface_ty" -> 
     	        let i = tyVar "a" in
		mkTyCon jObject
			[ ctxtTyApp (ctxtClass (mkQualName md nm) [mkTyCon jObject [i]]) i ]
     | optSubtypedInterfacePointers -> tyQCon  md nm [iface_ptr_ty_arg]
     | otherwise		    -> tyQConst md nm
   Pointer pt _ (Name _ _ _ _ _ (Just ti))
     | pt /= Ptr && is_pointed ti ->
        (\ x -> 
	 if pt == Unique {-&& not (isVARIANTTy x)-} then 
	    tyQCon prelude "Maybe" [x] 
	 else x) $
   	case mkTyConst (haskell_type ti) of
	   t | optCom && isTyVar t -> 
			if isGround then
			   mkTyConst vARIANT
			else
			   ctxtTyApp (ctxtClass variantClass [t]) t
	     | otherwise           -> t

   Pointer pt _ t
     | pt /= Ptr && isFunTy t -> toHaskellTy isGround t

   Pointer Ptr _ (Name _ _ _ (Just as) _ _) 
     | as `hasAttributeWithName` "foreign" -> tyForeignObj

   Pointer _ _ t | isVoidTy t || (isConstructedTy t && isReferenceTy t) -> tyAddr
   Pointer pt _ t
      | pt == Ref || (optHaskellToC && isIfaceTy t) ->
		if isIfaceTy t then
		   toHaskellTy isGround (getIfaceTy t)
		else
		   toHaskellTy isGround t
      | pt == Unique ->
	     tyQCon prelude "Maybe" $
	     case t of
	       Void -> [tyAddr]
               _  
	       --  optDeepMarshall  -> [tyPtr (toHaskellTy isGround ty)]
	        | optCom && isIfaceTy t -> [toHaskellTy isGround (getIfaceTy t)]
		| otherwise	   -> [toHaskellTy isGround t]   -- is this right?

      | isVariantTy t -> toHaskellTy isGround t
      | otherwise -> -- assumed to be a pure/raw pointer
             tyPtr (toHaskellTy isGround t)
   Struct i _ _     -> tyConst' i
   Union u _ _ _ _  -> tyConst' u
   CUnion u  _ _    -> tyConst' u
   UnionNon u _     -> tyConst' u
   Enum   i _ _     -> tyConst' i
   Iface{}          -> toHaskellIfaceTy ty
   _		    -> error ("toHaskellTy: "++showCore (ppType ty))
   where
    iface_ptr_ty_arg = tyVar "a"

    tyConst' i = tyQConst 
		    (idHaskellModule i)
		    (mkHaskellTyConName (idName i))

\end{code} %* % Mapping