% % (c) The Foo Project, University of Glasgow, 1998 % % @(#) $Docid: Mar. 31th 2003 08:37 Sigbjorn Finne $ % @(#) $Contactid: sof@galois.com $ % Support for generating client stubs for Automation interfaces. \begin{code}
module MarshallAuto
       (
         marshallVariantParam
       , unmarshallVariantParam
       , marshallVariant
       , classifyCall
       , permissibleAutoSig
       ) where

import qualified AbstractH as Haskell
import AbsHUtils
import MarshallType ( coreToHaskellExpr )
import MarshallCore ( toBaseTy, autoTypeToQName, mbAutoTypeToHaskellTy )
import CoreIDL
import CoreUtils ( int32Ty, doubleTy, mkHaskellVarName, 
		   isHRESULTTy, isVoidTy, boolTy
		 )

import BasicTypes
import Attribute ( hasAttributeWithName, findAttribute )
import LibUtils ( autoLib )
import Opts  ( optOptionalAsMaybe
	     )
import Maybe ( isJust )
import Literal

\end{code} \begin{code}
marshallVariantParam :: Param -> Haskell.Expr
marshallVariantParam p = funApply real_marshaller [ var (idName (paramId p)) ]
 where
  m = paramMode p

  attrs = idAttributes (paramId p)

   -- taking [defaultvalue()]s into account (if any).
   -- Use 'marshallerMeth' if you don't care for this stuff.
  real_marshaller
   | m == In && attrs `hasAttributeWithName` "defaultvalue" = 
      case findAttribute "defaultvalue" attrs of
        Just (Attribute _ (ap:_))
	     | okLooking ap -> 
		    let
		      pt      = paramType p
		      base_ty = toBaseTy pt
			{-
			  Stuff like
			    [in,optional,defaultvalue(0)]IUnknown* ip
			    [in,optional,defaultvalue(0)]char ip
			  
			  won't work if we use the arg type to drive
			  marshalling, so catch this sep.
			-}
		      the_base_ty
		       | isIntLit expr    = int32Ty
		       | isBoolLit expr   = boolTy
		       | isDoubleLit expr = doubleTy
		       | otherwise        = base_ty
		       
		      def_val_marshaller
		       | not optOptionalAsMaybe = qvar autoLib "inDefaultValue"
		       | otherwise		= qvar autoLib "inMaybe"
                    in		       
		       funApply def_val_marshaller
			        [ funApply (marshallVariant kind the_base_ty)
					   [coreToHaskellExpr expr]
				, marshallerMeth
				]
	    where
	     okLooking (ParamLit  _) = True
	     okLooking (ParamExpr _) = True
	     okLooking _	     = False
	     
	     isIntLit e = 
	        case e of
		  Lit (IntegerLit{}) -> True
		  _		     -> False

	     isDoubleLit e = 
	        case e of
		  Lit (FloatingLit{}) -> True
		  _		      -> False

	     isBoolLit e = 
	        case e of
		  Lit (BooleanLit{}) -> True
		  _		     -> False

	     expr = 
	       case ap of
	         ParamLit  l -> Lit l
		 ParamExpr e -> e
		 _           -> error "MarshallAuto.marshallVariantParam.expr: unexpected parameter kind"

        _ 
	  | optOptionalAsMaybe ->
		       funApply (qvar autoLib "inMaybe")
			        [ qvar autoLib "noInArg"
				, marshallerMeth
				]
	      
	  | otherwise  -> marshallerMeth

   | has_optional && optOptionalAsMaybe =
       funApply (qvar autoLib "inMaybe")
	        [ qvar autoLib "noInArg"
		, marshallerMeth
		]
   | otherwise = marshallerMeth

  marshallerMeth
   | m == In && not optOptionalAsMaybe && has_optional = qvar autoLib "inVariant"
   | otherwise = marshallVariant kind (paramOrigType p)
     
  has_optional = attrs `hasAttributeWithName` "optional"

  kind =
   case m of
     In    -> "in"
     Out   -> "out"
     InOut -> "inout"

unmarshallVariantParam :: Param -> Haskell.Expr
unmarshallVariantParam p = 
  case m of
    InOut -> funApply expr [var (mkHaskellVarName (idName (paramId p)))]
    _     -> expr
 where
  expr = marshallVariant kind (paramType p)
  m    = paramMode p
  kind =
   case m of
     In    -> "in"
     Out   -> "out"
     InOut -> "inout"

marshallVariant :: String -> Type -> Haskell.Expr
marshallVariant pre ty = 
   let 
    qn = autoTypeToQName ty
    qv = prefix pre qn
   in
   qvar autoLib (qName qv)

\end{code} determine what kind of Automation library stub to call. \begin{code}
classifyCall :: Id -> Bool -> [Param] -> Result -> Haskell.VarName
classifyCall f useDISPID ps res
  | isPropGet      = mkQVarName autoLib ("propertyGet" ++ prop_arity ++ dispid)
  | isPropPutWeird = mkQVarName autoLib ("propertySetGet" ++ dispid)
  | isPropPut      = mkQVarName autoLib ("propertySet" ++ dispid)
  | otherwise      = mkQVarName autoLib (kind ++ dispid ++ arity_str)
  where
   kind
    | any hasRetValAttr ps ||
      (not (isHRESULTTy res_ty) && not (isVoidTy r_ty) &&
       all isInParam ps ) = "function"
    | otherwise = "method"

   dispid
    | useDISPID = "ID"
    | otherwise = ""

   r_ty   = resultType res
   res_ty = resultOrigType res

   hasRetValAttr p =
     (idAttributes (paramId p)) `hasAttributeWithName` "retval"

   attrs     = idAttributes f

   isPropPutWeird = isPropPut &&
		    any (\ p -> paramMode p == InOut) ps

   isInParam p = paramMode p == In

   isPropGet = attrs `hasAttributeWithName` "propget"
   isPropPut = attrs `hasAttributeWithName` "propput"    ||
               attrs `hasAttributeWithName` "propputref"

   prop_arity
     | arity <= (1::Int) = ""
     | otherwise	 = arity_str

   arity_str = show arity

   arity =
    case res_ty of
      Name "HRESULT" _ _ _ _ _ -> arity'
      Void -> arity'
      _    -> arity' + 1
    where
     arity' = length (filter isOutParam ps)

   isOutParam p = pm == Out || pm == InOut
     where pm = paramMode p

\end{code} \begin{code}
permissibleAutoSig :: Result -> [Param] -> Bool
permissibleAutoSig res ps = 
  (isVoidTy r_ty || isHRESULTTy r_ty || isJust (mbAutoTypeToHaskellTy In r_ty)) &&
  all isJust (map ((mbAutoTypeToHaskellTy In).paramType) ps)
 where
  r_ty = resultType res

\end{code}