% % (c) The Foo Project, University of Glasgow, 1998 % % @(#) $Docid: Feb. 9th 2003 15:04 Sigbjorn Finne $ % @(#) $Contactid: sof@galois.com $ % Handling the marshalling of dependent arguments/fields. \begin{code}
module MarshallDep 
	( marshallDependents
	, unmarshallDependents
	, freeDependent
	
	) where

import qualified AbstractH as Haskell (Expr)
import AbsHUtils
import CoreIDL
import CoreUtils

import MarshallMonad
import MarshallUtils
import MarshallType
import MarshallCore

import BasicTypes
import LibUtils

import List  ( nubBy )
import Maybe ( mapMaybe, fromMaybe, fromJust, isJust )
import Monad ( when )

\end{code} %* % \section[marshall-dep]{Marshalling dependent parameters} % %* \begin{code}
marshallDependents :: Bool
		   -> Bool
                   -> DependInfo
		   -> (Name -> Type)
		   -> Mm ()
marshallDependents inStruct forServer ls lookup_ty = do
  sequence (map marshallDep ls)
  return ()
 where
  {-
   Marshall the field members/parameters. A field/param
   is either a depender or a dependee (never both.)
  -}
  marshallDep (_, [])   = return () -- no dependencies - no code to generate here.
  marshallDep (i, deps) = marshallDependent inStruct forServer i lookup_ty deps
\end{code} This function assumes that the dependencies are correct, that is, well formed, no duplicates nor conflicting attributes were used when the dependency information was computed. foo([in]int *len, [in,size_is(*len)]char* ls); It performs the following tasks: - creates let bindings for the dependees, i.e., let len = length ls - marshalls the dependers, i.e., ls <- m_list s_Char len w_Char ls - in preparation for calling the external function, allocate the dependees, i.e., len <- m_ref (allocOutPointer sizeofInt32) w_Int32 len \begin{code}
marshallDependent :: Bool
		  -> Bool
		  -> Id
		  -> (Name -> Type)
		  -> [Dependent]
		  -> Mm ()
marshallDependent _ _ _ _ [Dep SwitchIs [DepVal{}]] = return ()
marshallDependent inStruct forServer i lookup_ty deps' = do
 addCode (hLets dep_binds)
 (if inStruct && isArrayTy (removeNames ty) then
     return ()
  else 
     addCode (bind (funApply marshall_list [real_nm]) real_nm))
 when (not forServer) (dep_ptrs  >> return ())
 return ()
  where
   nm	         = idName i
   ty	         = lookup_ty nm
   marshall_list = marshallList inStruct
   				True
                                ty lookup_ty
				   (varName m_list)
   				   trans_start_posns 
			   	   trans_end_posns 
				   alloc_sizes
   (trans_start_posns, trans_end_posns, alloc_sizes) 
                 = computeArrayConstraints False{-marshalling-} deps

   -- size information may be in part be specified as part of
   -- the (array) type, i.e.,  [size_is(len,)] char arr[][20];
   -- 
   -- we push this info into the dependency list here. It really
   -- should be done as part of desugaring. (ToDo.)
   deps         = 
    case ty of
      Array _ es -> 
         case es of
	  []      -> deps'
	  [e]     ->
            case break (isSizeIs) deps' of
	     (_,[]) -> ((Dep SizeIs [exprToDep e]):deps')
	     (as,(Dep SizeIs ds):bs) -> as++(Dep SizeIs (combine ds (exprToDep e))):bs
             _      -> error "MarshallDep.marshallDependent: expected a SizeIs attribute"		
          [e1,e2] -> [ Dep FirstIs [exprToDep e1]
	  	     , Dep LastIs  [exprToDep e2]
		     ] ++ deps'
          _      -> error "MarshallDep.marshallDependent: expected at most two attributes"
          where

	   combine (DepNone:ds) d = d:ds
	   combine d       _ = d

	   exprToDep e = 
	     case (findFreeVars e) of
	       []    -> DepVal Nothing e
	       (v:_) -> DepVal (Just v) e

      _ -> deps'

   size_deps = filter (\d -> sizeOrLength d && 
			     hasNonConstantExprs d) deps

   dep_binds   = nubBy (\ a b -> isVarsEq (fst a) (fst b)) $
		 concat       $ 
		 map toBinder size_deps
   dep_ptrs    = sequence       (
                 map allocPtr   $
		 nubBy (\ (DepVal (Just a) _)
		 	  (DepVal (Just b) _) -> a == b) $
		 filter isDeref $
   		 concat         $
		 map (\ (Dep _ ls) -> ls) size_deps)

   real_nm     = mkHVar i

   -- partial solution
   allocPtr (DepVal (Just v) (Unary Deref e)) =
	addCode (bind (funApply (marshallType stubMarshallInfo (lookup_ty v))
				[coreToHaskellExpr e]) (var v))
   allocPtr _ = error "MarshallDep.marshallDependent.allocPtr: unexpected value"

   isDeref (DepVal (Just _) (Unary Deref _)) = True
   isDeref _ = False

   -- convert a dependency list       
   toBinder (Dep _ ls) = mapMaybe toBinds ls
     where
      toBinds (DepVal (Just v) e@(Var _)) =
        Just ( var v, subst v len (coreToHaskellExpr e))
	where
	 len = mkLength (lookup_ty v)
         
      toBinds (DepVal (Just v) e) = 
         Just (var v, subst v' len (coreToHaskellExpr (solve v (Var v') e)))
         where
	  v'  = v ++ "'"
          len = mkLength (lookup_ty v)
         
      toBinds _ = Nothing

   mkLength to_ty = 
      -- if the external function expects a [unique] pointer to the
      -- value holding the length, wrap a Just around the length of the list.
     case to_ty of
        Pointer Unique _ _ -> just length_expr
	_		   -> length_expr
    where
      length_expr = 
	  coerceTy intTy (removePtrs to_ty) $
          case removeNames ty of 
	      -- if the depender is a wide string, use appropriate
	      -- length function.
	      -- ToDo: add a "Type -> QualName" function which
	      --  returns the name of the length computing function
	      --  to use for the given Core type.
	    WString{} -> funApp lengthWString [var nm]
	    _         -> funApp lengthName    [var nm]



\end{code} \begin{code}
unmarshallDependents :: Bool  -- working inside a struct/union?
		     -> Bool  -- dealing with [out] parameters?
	             -> DependInfo
		     -> (Name -> Type)
		     -> Mm ()
unmarshallDependents inStruct is_out ls lookup_ty = do
 marshall_dependees
 sequence (map unmarshallDep ls)
 return ()
  where
   deps = concat (map snd ls)

    {-
     Marshal the field members/parameters. A field/param
     is either a depender or a dependee (never both.)
    -}
   unmarshallDep (_, [])    = return ()   -- no dependencies on this one, just continue.
   unmarshallDep (i, deps1) =
       case (findPtrType True (idAttributes i)) Void of
         Pointer Ptr _ _ -> return ()
	 _               -> unmarshallDependent inStruct is_out i lookup_ty deps1
			       -- split off into separate function for clarity.

   marshall_dependees = sequence (map toBinds code)

   code = nubBy theSame  $
	  concatMap (\ (Dep _ ds) -> filter nonConstantDep ds) $
	  filter (\d -> sizeOrLength d && 
	  		hasNonConstantExprs d &&
			not (isResult d)) deps

    -- HACK, need to take into account scoping, so that we don't
    -- run the risk of not unmarshalling dependent arguments named
    -- "result"!
   isResult (Dep _ xs) = any isRes xs
     where
      isRes (DepVal (Just "result") _) = True
      isRes _			       = False

   -- the actions we're creating here are only responsible for fishing
   -- out values from pointers, so we only need to do this once.
   theSame (DepVal (Just a) _) (DepVal (Just b) _) = a == b
   theSame _ _ = False 

   nonConstantDep (DepVal (Just _) _) = True
   nonConstantDep _		      = False

   toBinds (DepVal (Just v) (Var _))
       = let v'   = v
	     ty   = lookup_ty v'
         in
         addCode (bind (funApply (unmarshallType stubMarshallInfo ty) [var v']) (var v'))
   toBinds (DepVal (Just v) e) = 
         let 
             v'    = v ++ "'"
	     ty    = lookup_ty v
	 {-
	   For a case like the following: 
		    void foo([out]int *len,[out,size_is(*len+2)]char* ps[]);

	   we want to generate unmarshalling code for ps that gets at the
           value of len:
 
            len <- ((u_ref r_Int32) len)
            let len' = (len + 2)
            ps <- u_list s_Addr 0 (fromIntegral len') ....
 
	  the code below generates the first two lines, binding the value
	  read out of 
	  
	 -}    
	
         in do
         addCode (bind (funApply (unmarshallType stubMarshallInfo ty) [var v]) (var v))
	 addToEnv v v'
	 addCode (hLet (var v') (subst v (var v) (coreToHaskellExpr e)))

   toBinds _ = error "MarshallDep.unmarshallDependents.toBinds: unexpected value"

{-
 This function assumes that the dependencies are correct, that is,
 well formed, no duplicate nor conflicting attributes were used
 when the dependency information was computed.
-}
unmarshallDependent :: Bool
		    -> Bool
		    -> Id 
		    -> (Name -> Type) 
		    -> [Dependent] 
		    -> Mm ()
unmarshallDependent _ _ _ _ [Dep SwitchIs _] = return ()
unmarshallDependent inStruct is_out i lookup_ty deps' = do
 unmarsh <- unmarshallList inStruct
 			   True{-at top-level-}
                           ty 
 			   lookup_ty
                           (trans_start_posns)
			   (trans_end_posns)
			   (alloc_sizes)
 let 
      -- in the case of [out] parameters, de-reference the 
      -- the pointer to get at the goods. This is only done
      -- when the [out] parameter was (at least) a pointer to
      -- a pointer to something. If not, then the [out] pointer 
      -- points to the piece of a memory (we've already allocated)
      -- and are now ready to unmarshal.
     unmarsh' 
       | is_out && allocated_space_for = funApp r_ref [unmarsh]
       | otherwise = unmarsh

     unmarsh_and_free
       | is_out   =
	funApp doThenFree 
	       [ fromMaybe (varName trivialFree) (freeDependentE i lookup_ty deps')
	       , unmarsh'
	       ]
       | otherwise = unmarsh'

 addCode (bind (funApply unmarsh_and_free [nm_var]) nm_var)
  where
   nm	         = idName i
   tentative_ty  = lookup_ty nm
   ty        
     | should_peel = removePtr tentative_ty
     | otherwise   = tentative_ty

   nm_var        = mkHVar i

   {-
     Determine whether we had to allocate space for an [out] pointer.
     If we did, we need to deref this pointer before unmarshalling -- see above.
   -}
   allocated_space_for =
        is_out && 
	let
	 (_, _, cs1) = computeArrayConstraints False{-marshalling-} deps'
	in
	case cs1 of
	  (DepNone:_) -> True
	  _           -> False

   should_peel 
    | not is_out = False
    | otherwise  =
        case cs of
	  (DepNone:_) -> True
	  _	      -> False

{-
   -- size information may be in part be specified as part of
   -- the (array) type, i.e.,  [size_is(len,)] char arr[][20];
   -- 
   -- we push this info into the dependency list here. It really
   -- should be done as part of desugaring. (ToDo.)
   deps         = 
    case ty of
      Array _ es -> 
         case es of
	  []      -> deps'
	  [e]     ->
            case break (isSizeIs) deps' of
	     (as,[]) -> ((Dep SizeIs [exprToDep e]):deps')
	     (as,(Dep SizeIs ds):bs) -> as++(Dep SizeIs (combine ds (exprToDep e))):bs
          [e1,e2] -> [ Dep FirstIs [exprToDep e1]
	  	     , Dep LastIs  [exprToDep e2]
		     ] ++ deps'
          where

	   combine (DepNone:ds) d = d:ds
	   combine d       _ = d

	   exprToDep e = 
	     case (findFreeVars e) of
	       []    -> DepVal Nothing e
	       (v:_) -> DepVal (Just v) e

      _ -> deps'
-}

   (as, bs, cs) = computeArrayConstraints True{-unmarshalling-} deps'

   (trans_start_posns, trans_end_posns, alloc_sizes)
     | should_peel = (tail as, tail bs, tail cs) -- peel off the toplevel pointer for [out] params
     | otherwise   = (as, bs, cs)

\end{code} \begin{code}
marshallList :: Bool
	     -> Bool
             -> Type
	     -> (Name -> Type)
	     -> Haskell.Expr
	     -> [DepVal]{-start index of transmits, one for each dim.-} 
	     -> [DepVal]{-end index of transmits-}
	     -> [DepVal]{-size to allocate (for each dimension)-}
	     -> Haskell.Expr
marshallList inStruct topLev ty _ _ [] [] [] = marshallElts inStruct topLev True ty
marshallList inStruct topLev ty lookup_ty marshaller
	     (_:starts) (_:ends) (_:sz_allocs)
 | (isPointerTy r_ty && not (isVoidPointerTy r_ty)) || isArrayTy r_ty  =
   funApply marshaller
            [ szType ty'
            , marshallList inStruct False ty' lookup_ty ref_marshaller starts ends sz_allocs
	    ]
 | otherwise	= marshallElts inStruct topLev True ty
  where
   r_ty = removeNames ty
   ref_marshaller = funApp w_list [varName alloc_list]
   
   alloc_list 
      | isStringTy ty'  || 
        isPointerTy ty' || 
	isArrayTy ty'	    = true
      | otherwise	    = false

   ty'    = removePtrAndArray r_ty

marshallList _ _ _ _ _ _ _ _ = error "MarshallDep.marshallList: the impossible happened"

unmarshallList :: Bool
	       -> Bool
               -> Type
	       -> (Name -> Type)
	       -> [DepVal]{-start index of transmits, one for each dim.-} 
	       -> [DepVal]{-end index of transmits-}
	       -> [DepVal]{-size to allocate (for each dimension)-}
	       -> Mm Haskell.Expr
unmarshallList inStruct topLev ty _ [] [] []  = return (marshallElts inStruct topLev False ty)
unmarshallList inStruct topLev ty l_ty
               (_:starts) (_:ends) (sz:sz_allocs)
 | (isPointerTy r_ty && not (isVoidPointerTy r_ty)) ||
   isArrayTy r_ty  = do
     rest <- unmarshallList inStruct False ty' l_ty starts ends sz_allocs
     len  <- mkLengthExpr sz l_ty
     return (funApp u_list [ szType ty'
			   , var "0"
			   , len
			   , rest
			   ])

 | otherwise	= return (marshallElts inStruct topLev False ty)
 where
   r_ty   = removeNames ty

   ty'    = removePtrAndArray r_ty
unmarshallList _ _ _ _ _ _ _ = error "MarshallDep.unmarshallList: the impossible happened"

mkLengthExpr :: DepVal -> (Name -> Type) -> Mm Haskell.Expr
mkLengthExpr sz lookup_ty = 
 case sz of
   DepNone           -> return nothing
   DepVal Nothing e  -> return (coerceTy intTy word32Ty (coreToHaskellExpr e))
   DepVal (Just v) e -> do
	 mb_nm <- lookupName v
	 mNm   <- getMethodName
	 let 
	   nm  = 
	     case mb_nm of
	       Nothing 
	         | v == "result" && isJust mNm -> 
		 	outPrefix ++ fromJust mNm
	         | otherwise ->
		        error ("MarshallDep.mkLengthExpr: unbound variable ('" ++
			       nm ++ "') encountered in length_is() attribute")
	       Just x -> x

	   ty  = lookup_ty v
	   h_e = subst v (var nm) (coreToHaskellExpr e)


	   coerce = coerceTy (removePtrs ty) word32Ty
				      
           {-
	    In the case the length is given via a [unique] pointer,
	    we will have at this stage unmarshalled it to a Maybe value.
	    Convert the Maybe value into a length here.
	   -}
	 case ty of
	   Pointer Unique _ _ -> return (
				funApp fromMaybeName
				       [ var "0"
				       , funApp mapName [lam [patVar "x"] 
				       			     (coerce (var "x")), h_e]
				       ])
	   Pointer Ptr _ _  -> error "mkLengthExpr: Ptr - no can do."
	   _		    -> return (coerce h_e)

marshallElts :: Bool -> Bool -> Bool -> Type -> Haskell.Expr
marshallElts inStruct topLev marshalling ty
   | marshalling && topLev = marshallType      mInfo ty
   | marshalling           = refMarshallType   mInfo ty
   | topLev                = unmarshallType    mInfo ty
   | otherwise		   = refUnmarshallType mInfo ty
 where
  mInfo = stubMarshallInfo{forStruct=inStruct,forRef=True}
\end{code} When freeing up values that have been classified and marshalled as dependent [in] params, we need to make sure we free the entire structure that has been previously allocated. @freeDependent@ takes care of this, by, in effect, by reconstructing what kind of pointer / array value that the 'dependent arg' marshaller previously constructed. \begin{code}
freeDependent :: Id -> (Name -> Type) -> [Dependent] -> Mm ()
freeDependent i lookup_ty deps = 
   case freeDependentE i lookup_ty deps of
     Nothing -> return ()
     Just f  -> addCode (bind_ (funApply f [real_nm]))
 where
  real_nm   = mkHVar i

freeDependentE :: Id -> (Name -> Type) -> [Dependent] -> Maybe Haskell.Expr
freeDependentE i lookup_ty deps = free_list
 where
  free_list = freeList ty lookup_ty trans_start_posns trans_end_posns alloc_sizes
  (trans_start_posns, trans_end_posns, alloc_sizes)
            = computeArrayConstraints False{-not unmarshaling-} deps

  ty = lookup_ty (idName i)


freeList :: Type
	 -> (Name -> Type)
	 -> [DepVal]{-start index of transmits, one for each dim.-} 
	 -> [DepVal]{-end index of transmits-}
	 -> [DepVal]{-size to allocate (for each dimension)-}
	 -> Maybe Haskell.Expr
freeList ty _  []    []    []    = freeElts (removePtrAndArray ty)
freeList ty _  (_:_) (_:_) (_:_)
 | (isPointerTy ty || isArrayTy ty) && needsFreeing ty' =
   Just $ (varName free)
{-
   funApp f_list
          [ szType ty'
	  , length_of sz
          , fromMaybe (varName trivialFree) (freeList ty' lookup_ty starts ends sz_allocs)
	  ]
-}
 | otherwise = freeElts ty
  where
   ty'       = removePtrAndArray ty

{-
   length_of (DepVal Nothing  e) = coreToHaskellExpr e
   length_of (DepVal (Just v) e) =
	coerceTy (removePtrs (lookup_ty v)) word32Ty (coreToHaskellExpr e)
-}

freeList _ _ _ _ _ = error "MarshallDep.freeList: the impossible happened"

freeElts :: Type -> Maybe Haskell.Expr
freeElts ty =
  case ty of
   Sequence{}  -> Just $ varName free 
      --Just $ funApp f_list [ szType t, freeElts' t ] -- wrong.
   Fixed{}          -> error "not implemented yet."
   SafeArray t      -> Just $ funApp f_list [ szType t, freeElts' t]
   Array Void (d:_) -> Just $ funApp f_list [ szType (Pointer Ptr True Void)
					    , coreToHaskellExpr d
					    , mkEltFreer (Pointer Ptr True Void)
					    ]
   Array t (d:_) -> Just $ funApp f_list   [ szType t, coreToHaskellExpr d, freeElts' t]
   String{}	 -> Just $ varName f_string
   WString{}	 -> Just $ varName f_wstring
   Pointer _ _ Iface{}    -> Nothing
   Pointer _ _ Void       -> Just $ varName free
   Pointer Ref _ (Char _) -> Just $ varName f_string
   Pointer pt _ pty
      | pt == Ref    -> Just $ funApp f_ref    [ freeElts' pty ]
      | pt == Unique -> Just $ funApp f_unique [ freeElts' pty ]
      | otherwise    -> Just $ varName f_ptr 
   _	| needsFreeing ty -> Just (mkEltFreer ty)
        | otherwise	  -> Nothing
  where
   freeElts' t   = fromMaybe (varName trivialFree) (freeElts t)

   mkEltFreer ety = varName (mkMarshaller freePrefix ety)

\end{code} Constants referring to library marshallers: \begin{code}
m_list, w_list, u_list, f_list :: QualName
m_list   = prefix marshallPrefix   (mkQVarName hdirectLib list)
w_list   = prefix marshallRefPrefix   (mkQVarName hdirectLib list)
u_list   = prefix unmarshallPrefix (mkQVarName hdirectLib list)
f_list   = prefix freePrefix (mkQVarName hdirectLib list)

{-
r_list :: QualName
r_list   = prefix unmarshallRefPrefix (mkQVarName hdirectLib list)
-}

f_string :: QualName
f_string = prefix freePrefix (mkQVarName hdirectLib stringName)

f_unique, r_ref, f_ref, f_wstring :: QualName
f_unique = prefix freePrefix   (mkQVarName hdirectLib unique)
r_ref    = prefix unmarshallRefPrefix (mkQVarName hdirectLib ref)
f_ref    = prefix freePrefix   (mkQVarName hdirectLib ref)
f_wstring = prefix freePrefix   (mkQVarName comLib wstring)

f_ptr :: QualName
f_ptr  = free
\end{code}