% % (c) The Foo Project, University of Glasgow, 1998 % % @(#) $Docid: Nov. 24th 2003 10:04 Sigbjorn Finne $ % @(#) $Contactid: sof@galois.com $ % The marshalling of an IDL method \begin{code} module MarshallMethod ( cgMethod , cgProperty , findParamDependents , removeDependers , removeDependees , removeDependents , allocateOutParams , marshallParams , freeInParamStorage , primDecl , mkResult ) where import BasicTypes import Literal ( iLit, Literal(..), IntegerLit(..) ) import AbstractH ( HDecl ) import qualified AbstractH as Haskell ( Expr, Type ) import NativeInfo ( lONG_SIZE ) import AbsHUtils import Attribute import Env ( lookupEnv, replaceElt ) import Opts ( optKeepHRESULT, optUseDispIDs, optCoalesceIsomorphicMethods, optPrefixIfaceName, optSubtypedInterfacePointers, optHaskellToC, optIgnoreHiddenMeths, optIgnoreRestrictedMeths, optGenDefs, optExplicitIPointer, optHugs, optDualVtbl, optLongLongIsInteger, optCorba, optNoShareFIDs, optCom ) import CoreIDL import CoreUtils ( DependInfo, DepVal(..), findParamTy, findParam, isSimpleTy, addrTy, mkHaskellVarName, mkHaskellTyConName, mkIfaceTypeName, removePtrAndArray, computeArrayConstraints, removePtrAll, isVoidTy, isPtrPointerTy, resultParam, lookupDepender, mkParam, toCType, keepValueAsPointer, isHRESULT, removePtr, sizeAndAlignModulus, binParams, tyFun, word32Ty, iUnknownTy, iPointerParam, isIntegerTy, isIfaceTy ) import CgMonad import MarshallMonad import MarshallType ( marshallType , unmarshallType , refUnmarshallType , allocPointerTo , coreToHaskellExpr , mbFreeType , szType , coerceTy , coerceToInt ) import MarshallCore ( toHaskellTy , paramToHaskellType , toHaskellBaseTy , toHaskellBaseMethodTy , autoTypeToHaskellTy , constrainIIDParams ) import MarshallDep import MarshallUtils import MarshallAuto import LibUtils ( comLib , iDispatch , iUnknown , ioExts , outPrefix , autoLib , allocBytes , fromIntegralName , fromMaybeName , mapName , mkPrimitiveName , mkPrimExportName , invokeAndCheck , primInvokeIt , invokeAndCheck , invokeIt , marshallPrefix , withForeignPtrName , check2HR , checkHR ) import Maybe ( fromMaybe, isJust, fromJust ) import Monad ( when, mplus ) import Utils ( concMaybe ) \end{code} \begin{code} cgMethod :: Id -> CallConv -> Result -> [Param] -> Maybe Int -> Maybe Name -- name which implements the external call. -> CgM HDecl cgMethod i cconv result params offs mb_prim = -- fetch some state out of the monad and go.. getDeclName $ \ mname -> do dname <- getDllName iface <- getIfaceName objFlag <- getInterfaceFlag forClient <- getClientFlag isIEnum <- getIEnumFlag methNo <- getMethodNumber offs inh <- getIfaceInherit hasIso <- (if (not optCoalesceIsomorphicMethods) then return Nothing else isIsomorphicMethod (idOrigName i) result params) let isObj = objFlag /= StdFFI isAuto = case objFlag of ComIDispatch isDual -> not isDual || (not optDualVtbl && permissibleAutoSig result params) _ -> False isServer = not forClient && not (attrs `hasAttributeWithName` "source") case hasIso of Just False -- already generated code for isomorphic method, so -- don't generate code for this one. | optCoalesceIsomorphicMethods -> return emptyDecl _ | isHidden -> return emptyDecl -- ToDo: support this for binary interfaces?? | otherwise -> do {- methods that are marked with call_as() are only used when generating proxy/stub code for remoting. We use them when in 'COM mode' too, since these methods have at times attributes that are more helpful to us. [No, afraid not - the signature of the remoting method does not have to be isomorphic to the local method. See comment in the desugaring code.] In non-COM mode, call_as() is used as a means to avoid having to have a 1-1 mapping between Haskell function stub names and the name of the external entry point to invoke. -} let meth_i = i meth_nm = ieValue (mkHaskellVarName (idName meth_i)) export_decl = addExport meth_nm enum_meth_ok = isIEnum && not isServer && isIEnumOK export_decl (flg, prim_decl, mb_prim2) <- primDecl isObj isServer (not enum_meth_ok && not isAuto) i dname mname cconv r_ty params let decl' = mkMethod iface mname inh hasIso objFlag enum_meth_ok isServer mb_prim mb_prim2 cconv methNo meth_i result params decl = helpString `andDecl` decl' case (isAuto || enum_meth_ok || isJust mb_prim) of True -> return decl -- the primitives are elsewhere! False -> do needStubs flg hasPrims return ( decl `andDecl` prim_decl ) where r_ty = resultType result attrs = idAttributes i isHidden = attrs `hasAttributeWithName` "ignore" || (( optIgnoreHiddenMeths || optIgnoreRestrictedMeths ) && attrs `hasAttributeWithNames` ["hidden", "restricted"]) helpString = helpStringComment i {- Some typelibs are in such a pitiful state, so we have to make sure that the enum method is in a good enough shape. -} isIEnumOK = case idName i of _:'e':'x':'t':_ -> right_shape _:'e':'m':'o':'t':'e':'N':'e':'x':'t':_ -> right_shape _ -> True where right_shape = [In,Out,Out] == map (paramMode) params \end{code} The function that does the real work of generating a stub. It is currently a mess, and is scheduled for a cleanup/rewrite Real Soon. \begin{code} mkMethod :: Name -- interface the method belongs to -> String -- it's module -> [QualName] -- interface inheritance (name) chain -> Maybe Bool -> IfaceType -> Bool -> Bool -> Maybe Name -> Maybe Name -> CallConv -> Int -- method number (in vtbl) -> Id -- method Id / name. -> Result -> [Param] -> HDecl mkMethod iface mname inh hasIso objFlag isIEnum isServer mb_prim mb_prim_nm cconv methNo methId result params | isAuto = auto_tysig `andDecl` auto_def | isIEnum = enum_tysig `andDecl` enum_def | otherwise = m_tysig `andDecl` m_def where r_ty = resultType result isObj = objFlag /= StdFFI (isAuto, isDual) = case objFlag of ComIDispatch isD -> (not isD || (not optDualVtbl && permissibleAutoSig result params), isD) _ -> (False, False) m_name = mkHaskellVarName name name = idName methId m_tysig = mkTypeSig m_name in_tys result_ty m_def = funDef m_name in_pats m_rhs auto_tysig = mkTypeSig m_name in_tys (funTy i_pointer_ty (returnType (tuple res_ty))) enum_def = funDef m_name in_pats enum_rhs enum_tysig = mkTypeSig m_name enum_type_args enum_type_res enum_elt_ty = toHaskellTy True (removePtrAndArray (paramType (head results))) (enum_type_args, enum_type_res) = let (res_type:is) = (result_ty:in_tys) --OLD: (r_ty:is) = relabelTypes (result_ty:in_tys) in {- Bad boy, go sit in the corner. -} case name of _:'e':'x':'t':_ -> (is, funTy i_pointer_ty (io (tyList enum_elt_ty))) _:'e':'m':'o':'t':'e':'N':'e':'x':'t':_ -> (is, funTy i_pointer_ty (io (tyList enum_elt_ty))) _ -> (is, res_type) enum_rhs = funApp enum_fun args where enum_fun = mkQVarName comLib ("enum" ++ enumName) {- When MIDL generates a typelib for the following [object,...] interface IA : IUnknown { [local] HRESULT f (); [call_as(f)] HRESULT remoteF(); }; The type library gets a method named "remoteF". I'm not sure that this is right, but what can you do? -} enumName = case idOrigName methId of _:'e':'m':'o':'t':'e':'N':'e':'x':'t':_ -> "Next" n -> n -- peel off two levels of indirection (one for the [out] pointer) elt_ty = removePtr (paramType (head results)) sz_of = szType elt_ty write_elt = refUnmarshallType stubMarshallInfo elt_ty args = -- too beautiful, man.. case name of _:'e':'x':'t':_ -> sz_of : write_elt : the_args _:'e':'m':'o':'t':'e':'N':'e':'x':'t':_ -> sz_of : write_elt : the_args _ -> the_args the_args = case (map (mkHVar.paramId) meth_params) of [] -> [] [x] -> [x] -- the library impl of Skip() and Next() expects a Word32 as first -- arg, so make sure that's the case here. (x:xs) -> coerceTy (paramType (head meth_params)) word32Ty x : xs auto_def = funDef m_name in_pats auto_rhs auto_rhs = funApp call_wrapper the_args where fun_id | with_dispids = integerLit dispid | otherwise = stringLit (idOrigName methId) with_dispids = optUseDispIDs && has_dispid call_wrapper = classifyCall methId with_dispids params result the_args | optExplicitIPointer = args ++ [var "iptr"] | otherwise = args args = fun_id : (hList (map marshallVariantParam ins)): map unmarshallVariantParam (results) has_dispid = isJust mb_dispid mb_dispid = getDispIdAttribute (idAttributes methId) (Just dispid) = mb_dispid (pars, ins,outs,inouts,res) = binParams params param_names = map (idName.paramId) params returnType t | isPure = t | otherwise = io t isPure = (idAttributes methId) `hasAttributeWithName` "pure" meth_params = case mb_prim of Nothing -> meth_params' Just _ -> prim_param:meth_params' -- extend the param list if the 'result' parameter, so that -- we can use the result in dependent argument expression ( -- e.g., [out,length_is(result)]int* f, ... params_and_result = params ++ [resultParam (resultType result)] (prim_params, meth_params', result_ty) | isAuto = ( [] , real_params ++ (if optExplicitIPointer then [iptr_param] else []) , funTy i_pointer_ty (returnType (tuple res_ty)) ) | isObj || isIEnum = ( mptr_param:iptr_param:params , real_params ++ [iptr_param] , funTy i_pointer_ty (returnType (tuple res_ty)) ) | otherwise = ( params , real_params , returnType (tuple res_ty) ) i_pointer_ty | optSubtypedInterfacePointers && not isIsoMethod = tyCon (mkHaskellTyConName (mkIfaceTypeName iface)) [tyVar "a"] | (isAuto || isDual) && isIsoMethod && optSubtypedInterfacePointers = mkTyCon iDispatch [tyVar "a"] | isIsoMethod && optSubtypedInterfacePointers = mkTyCon iUnknown [tyVar "a"] | isAuto && isIsoMethod = mkTyConst iDispatch | optSubtypedInterfacePointers = tyCon (mkHaskellTyConName (mkIfaceTypeName iface)) [tyVar "a"] | otherwise = tyConst (mkHaskellTyConName iface) isIsoMethod = fromMaybe False hasIso -- building the Haskell type of the method/function. (in_tys_1, res_ty) = constrainIIDParams (paramToHaskellType par_deps isServer isAuto False) (paramToHaskellType res_deps isServer isAuto True) real_params results {- res_ty = map (paramToHaskellType res_deps isServer isAuto True) results -} in_tys = -- if the primitive method is passed in as arg, prefix it -- to the parameter list. (if isJust mb_prim then ((toHaskellBaseMethodTy False prim_params result):) else id) in_tys_1 -- map (paramToHaskellType par_deps isServer isAuto False) real_params in_p_tys = map (\ p -> ( idName (paramId p) , toParamPrimTy isServer p ) ) prim_params iptr_param = iPointerParam iface mptr_param = mkParam "methPtr" In (Pointer Ptr True Void) prim_param = mkParam (fromJust mb_prim) In (tyFun cconv result prim_params) in_pats = map (varPat.mkHVar.paramId) meth_params meth_result = mkResult results unsafeWrap e | isPure = funApp (mkQVarName ioExts "unsafePerformIO") [e] | otherwise = e m_rhs = unsafeWrap $ runMm (Just (mkHaskellVarName (idName methId))) param_names meth_result $ do marshallDependents False{-not inside struct-} False{-not for server proxies-} par_deps (findParamTy params_and_result) -- in and in-out params allocateOutParams (raw_inout_deps++raw_out_deps) (findParam params_and_result) (removeDependees raw_inout_deps outs) marshallParams True{-marshall-} False isServer (removeDependents in_deps real_ins) marshallParams True{-marshall-} False isServer (removeDependers raw_inout_deps raw_inouts) setupMethodCall isObj methNo iface inh result in_p_tys (thePrimCall isObj mname mb_prim mb_prim_nm methId result prim_params) freeInParamStorage in_deps ins okResult isObj methId result unmarshallOutParams isServer (removeDependers (out_deps ++ inout_deps) real_outs) when (not ignoreResult) (unmarshallResult isServer methId{idName=outPrefix++idName methId} r_ty) marshallParams False False isServer (removeDependers (inout_deps++out_deps) real_inouts) unmarshallDependents False True out_deps (findParamTy params_and_result) unmarshallDependents False False inout_deps (findParamTy params_and_result) (results, ignoreResult) = let results' | isAuto = (outs ++ inouts) | otherwise = real_res in case r_ty of Void -> (results', True) _ | (isHRESULT result && not optKeepHRESULT) || ((idAttributes methId) `hasAttributeWithName` "hs_ignore_result") -> (results', True) | otherwise -> (results' ++ [res_param], isSimpleTy r_ty && not (isIfaceTy r_ty)) res_param = let p = mkParam (outPrefix ++ name) Out r_ty in p{ paramOrigType=resultOrigType result , paramId=(paramId p){idAttributes=idAttributes methId} } -- replace attributes. (real_params, par_deps) = findParamDependents True pars (_, in_deps) = findParamDependents True ins (real_ins, _) = findParamDependents False ins -- For the [out] params, there's a wondrous special case: -- [out,size_is(x),length_is(*px)]void* pv -- -- Here we don't want to unmarshall pv into a list (it's -- really a chunk of mem), but want to consider pv to -- be a dependent argument for the purposes of allocating -- a chunk of memory to pass in. So, use a pair of DependInfos, -- one to use for marshalling ('out_deps') and another -- to use when allocating out-param storage ('raw_out_deps.') (real_outs', out_deps) = findParamDependents True outs (_, raw_out_deps) = findParamDependents False outs real_outs = filter (not.(`hasAttributeWithName` "ptr").idAttributes.paramId) real_outs' (real_inouts, inout_deps) = findParamDependents False inouts (raw_inouts, raw_inout_deps) = findParamDependents True inouts (real_res, res_deps) = findParamDependents True res \end{code} Generating code for dispinterface properties is real straightforward, \begin{code} cgProperty :: Id -> Type -> Id -> Id -> CgM HDecl cgProperty i ty seti geti = do if_name <- getIfaceName let prop_iface_ty | optSubtypedInterfacePointers = tyCon if_name [tyVar "a"] | otherwise = tyConst if_name get_prop_tysig = mkTypeSig get_prop_name [prop_iface_ty] (io (prop_ty Out)) set_prop_tysig = mkTypeSig set_prop_name [prop_ty In, prop_iface_ty] (io tyUnit) prop_ty kind = autoTypeToHaskellTy kind ty get_prop_decl = funDef get_prop_name [] get_prop_rhs set_prop_decl = funDef set_prop_name [varPat (var "prop")] set_prop_rhs get_prop_name = if_prefix ++ idName geti set_prop_name = if_prefix ++ idName seti if_prefix | optPrefixIfaceName = mkHaskellVarName (mkIfaceTypeName if_name) ++ "_" | otherwise = "" get_prop_rhs = funApp getProp [ prop_id, hList [] , marshallVariant "out" ty ] set_prop_rhs = funApp setProp [ prop_id , hList [funApply (marshallVariant "in" ty) [var "prop"]] ] prop_id | optUseDispIDs && has_dispid = integerLit d_id | otherwise = stringLit (idName i) setProp = mkQVarName autoLib ("propertySet" ++ dispid) getProp = mkQVarName autoLib ("propertyGet" ++ dispid) has_dispid = isJust mb_dispid mb_dispid = getDispIdAttribute (idAttributes i) (Just d_id) = mb_dispid dispid | optUseDispIDs = "ID" | otherwise = "" return ( get_prop_tysig `andDecl` get_prop_decl `andDecl` set_prop_tysig `andDecl` set_prop_decl ) \end{code} \begin{code} marshallParams :: Bool -> Bool -> Bool -> [Param] -> Mm () marshallParams marsh don'tFree isServer ps = do sequence (map marshallParam ps) return () where marshallParam p | isVoidTy ty || isSimpleTy ty || keepValueAsPointer ty -- keep pointers to complex types -- external. = return () | otherwise = let nm = idName (paramId p) nm' = "in__" ++ nm pats | isIntegerTy ty = tuplePat [patVar (nm ++ "_hi"), patVar (nm ++ "_lo")] | otherwise = patVar nm pats' | isIntegerTy ty = tuplePat [patVar (nm' ++ "_hi"), patVar (nm' ++ "_lo")] | otherwise = patVar nm' in -- The hack to prefix the value with res__ in the proxy/server case -- requires that the same thing is done in MarshallServ.marshallMethod.meth_result -- -- Ditto for in__ prefixing the result of unmarshaling proxy args; -- MarshallServ.marshallMethod needs to be in on this (less-than-tasteful) game. if isServer then if marsh then let res__nm = var ("res__" ++ nm) in if (paramMode p == InOut) then addCode (bind_ (funApply (marshaller p ty) [ var nm, res__nm ])) else addCode (bind (funApply (marshaller p ty) [res__nm]) res__nm) else if (paramMode p == InOut) then addCode (genBind (funApply (marshaller p ty) [var nm]) pats') else addCode (genBind (funApply (marshaller p ty) [var nm]) pats) else addCode (genBind (funApply (marshaller p ty) [var nm]) pats) where ty = paramType p marshaller p | marsh = marshallType stubMarshallInfo{forInOut=(paramMode p == InOut), forProxy=isServer} | otherwise = unmarshallType stubMarshallInfo{ forInOut=(paramMode p == InOut) , doFree=not (don'tFree || don'tFree_t) , forProxy=isServer } where don'tFree_t = (idAttributes (paramId p)) `hasAttributeWithName` "nofree" unmarshallOutParams :: Bool -> [Param] -> Mm () unmarshallOutParams isServer ls = do sequence (map (unmarshallOutParam isServer) ls) return () unmarshallOutParam :: Bool -> Param -> Mm () unmarshallOutParam isServer p | isVoidTy ty || keepValueAsPointer ty || isPtrPointerTy ty {- doesn't make sense. (optHaskellToC && isAbstractTy ty && not (isAbstractFinalTy ty)) -} = return () | otherwise = addCode (bind (funApply (unmarshallType stubMarshallInfo{ forInOut=(paramMode p == InOut) , doFree=not don'tFree , forProxy=isServer} ty) [nm]) nm) where ty = paramType p nm = var (idName (paramId p)) don'tFree = (idAttributes (paramId p)) `hasAttributeWithName` "nofree" unmarshallResult :: Bool -> Id -> Type -> Mm () unmarshallResult _ _ Void = return () unmarshallResult isServer i ty = addCode (bind (funApply (unmarshallType stubMarshallInfo{doFree= not don'tFree, forProxy=isServer} ty) [nm]) nm) where nm = var (idName i) don'tFree = (idAttributes i) `hasAttributeWithName` "nofree" allocateOutParams :: DependInfo -> (Name -> Param) -> [Param] -> Mm () allocateOutParams deps lookup_param params = do sequence (map allocate params) return () where allocate p | isVoidTy ty = return () | otherwise = addCode (bind allocOut (var nm)) where i = paramId p ty = paramType p ty' = removePtrAll ty nm = idName i allocOut = case (lookupDepender deps i) of Nothing -> allocPointerTo ty Just ls -> let (_,_,sz_allocs) = computeArrayConstraints False{-not unmarshaling-} ls in -- allocate space big enough to hold what function is going -- to return back, i.e., -- [out]int* -> allocBytes s[Int32] (returning pointer to it.) -- [out]int** -> allocBytes s[t*] -- -- Note: if the [out] param is a constructed type, we don't need to -- to allocate space for the objects pointed to by any embedded pointers -- (that's the task of the callee.) -- [ No need to plug the pointers with NULLs either, AFAIK. -- sof 5/98 ] -- -- -- ToDo: assert that the ty is of a pointer or array nature. case sz_allocs of [] -> allocPointerTo ty (DepNone:_) -> allocPointerTo ty -- The next case is wrong, even if we've got -- [size_is(*e)] pinned onto an [out] parameter, -- we'll need to allocate enough space to hold -- (*e) elements. --(DepVal _ (Unary Deref _):_) -> allocPointerTo addrTy (DepVal Nothing e:_) -> case ty' of Pointer _ _ Void -> funApp allocBytes [coerceToInt e] _ -> funApp allocBytes [ binOp Mul (funApp fromIntegralName [szType ty']) (coerceToInt e) ] (DepVal (Just v) e:_) -> let coerce = varName fromIntegralName h_e = coreToHaskellExpr e in case paramType (lookup_param v) of Pointer Unique _ _ -> funApp allocBytes [binOp Mul (funApp fromIntegralName [szType ty']) (funApp fromMaybeName [ var "0" , funApp mapName [coerce, h_e] ])] _ -> let e' = coerceToInt e in case ty' of Pointer _ _ Void -> funApp allocBytes [e'] _ -> funApp allocBytes [ binOp Mul (funApp fromIntegralName [szType ty']) e' ] -- parameter list has all (in)out parameters plus function result. mkResult :: [Param] -> Haskell.Expr mkResult ps = case ps of [p] | isVoidTy (paramType p) -> ret unit | otherwise -> ret (mkVal p) _ -> ret (tup (map mkVal ps)) where mkVal p = var (idName (paramId p)) \end{code} Create the FFI declaration for the foreign function/object method we're interfacing to. If it's a (COM) method, we give it two extra Addr arguments, one contains the address of the method, the other the interface pointer. If we end up wanting to perform an indirect callout \begin{code} primDecl :: Bool -> Bool -> Bool -> Id -> String -> String -> CallConv -> Type -> [Param] -> CgM (Bool, HDecl, Maybe Name) primDecl isObj isServer trySharing f dname mname cc res params | isServer = do let sig = mkTySig param_h_tys res_hs_ty mb_res <- lookupDynStub sig case mb_res of Just (True,r) | not optNoShareFIDs -> return (False, emptyDecl, Just r) _ -> do when (not optNoShareFIDs) (addDynStub server_nm sig True) return (False, fexport cc Nothing server_nm server_ty, Nothing) | not isObj = return (needs_wrapper, prim cc loc_spec prim_nm prim_ty needs_wrapper c_ty_args c_res_ty, Nothing) | otherwise = do let sig = mkTySig param_h_tys res_hs_ty mb_res <- lookupDynStub sig case mb_res of Just (False,r) | not optNoShareFIDs && trySharing && not has_structs -> do return (False, emptyDecl, Just r) _ -> do when (trySharing && not has_structs) (addDynStub prim_nm sig False) return ( has_structs , primcst cc prim_nm prim_ty has_structs c_ty_args c_res_ty , Nothing ) where nm = idName f orig_nm = idOrigName f attrs = idAttributes f the_dname = case (findAttribute "dllname" attrs) of Just (Attribute _ [ParamLit (StringLit s)]) -> s _ -> dname loc_spec = case concMaybe (findAttribute "call_as" attrs) (findAttribute "entry" attrs) of Nothing -> (the_dname, Nothing, orig_nm, Nothing) Just (Attribute _ [ParamVar v]) -> (the_dname, Nothing, v, Nothing) Just (Attribute _ [ParamLit (IntegerLit (ILit _ x))]) -> let stub_nm = mkPrimitiveName (show x) in (the_dname, Just x, stub_nm, sz) Just (Attribute _ [ParamLit (StringLit v)]) -> (the_dname, Nothing, v, Nothing) _ -> (the_dname, Nothing, orig_nm, Nothing) {- Hugs' primitives live in an thoroughly flat namespace - if you have got two primitive decls called "prim_foo", the first that's loaded, is used throughout. Better not let that happen here, so we prepend the module name. [I've submitted a fix for this for Hugs98; hopefully it will be included..] 8/99 - add module prefix on the non-Hugs side too ; otherwise we run into trouble when linking object files that contain identically named stubs. -} prim_nm = mkPrimitiveName (mname ++ '_':nm) needs_wrapper = has_structs || case loc_spec of (_, Just _, _, _) -> True _ -> False has_structs = any (fst) ls ls@(c_res_ty:c_ty_args) = map (isStruct.toCType) (res:param_tys') where isStruct (Left x) = (False, x) isStruct (Right x) = (True, x) sz | not optGenDefs = Nothing | otherwise = case cc of Stdcall -> let stuff = map ((sizeAndAlignModulus Nothing).paramType) params p_sz = foldl (al_param) 0 stuff al_param siz (sz_t,_) = let --sz' = align sz modu -- hmm sz_t' | sz_t < lONG_SIZE = lONG_SIZE -- everything that's less than word size -- is rounded up to be exactly that. | otherwise = sz_t in (siz + sz_t') in Just p_sz _ -> Nothing server_nm = mkPrimExportName nm server_ty = case generaliseTys [prim_ty] of ([t], mb) -> mbCtxtTyApp mb (funTy t (io tyAddr)) prim_ty = funTys param_hs_tys (io res_hs_ty) {- We're using toHaskellBaseTy here to map to the *primitive* Haskell representation of an IDL type. -} param_hs_tys | isServer = tyPtr (uniqueTyVar "a") : param_h_tys | isObj = if optCom || attrs `hasAttributeWithName` "finaliser" then tyAddr : tyAddr : param_h_tys else tyAddr : tyAddr : param_h_tys | otherwise = param_h_tys (param_h_tys, _) = constrainIIDParams (toPtrTy .(toParamPrimTy isServer)) (toPtrTy .(toParamPrimTy isServer)) params outs -- param_h_tys = map (toPtrTy.toParamPrimTy isServer) params (_, _, outs, _, _) = binParams params param_tys' | isServer = addrTy:p_tys | isObj = addrTy:iUnknownTy:p_tys | otherwise = p_tys where p_tys = map paramType params res_hs_ty = toHaskellBaseTy True res toParamPrimTy isServer p | pattrs `hasAttributeWithName` "foreign" = tyForeignObj | otherwise = toHaskellBaseTy (isResult || isServer) (paramType p) where pattrs = idAttributes (paramId p) mode = paramMode p isResult = mode == Out || -- mode == InOut || paramDependent p \end{code} If we're compiling a COM method, we need to swizzle the function pointer out of the interface pointer in order to perform the actual call. @setMethodCall@ does this, dereferencing the i-pointer (assume it is in scope as @iptr@), binding the function pointer to @methPtr@. \begin{code} setupMethodCall :: Bool -> Int -> String -> [QualName] -> Result -> [(Name, Haskell.Type)] -> (Haskell.Expr, Maybe Haskell.Expr) -> Mm () setupMethodCall isObj methNo ifaceName inh result param_tys methCall | not isObj = addCode ( binder mCall ) | isHRESULT result = addCode ( binder (funApp invokeAndCheck [ lam [varPat methPtr, varPat iptr] mCall , offset , iptr ])) | otherwise = addCode ( binder invokeMethod ) where (mCall0, mbRes) = methCall mCall = unravel mCall0 invokeMethod | optHaskellToC || optCorba = funApp primInvokeIt [ lam [varPat methPtr, varPat iptr] mCall , offset , funApp m_iptr [iptr] ] | otherwise = funApp invokeIt [ lam [varPat methPtr, varPat iptr] mCall , offset , iptr ] m_iptr = case inh of [] -> prefix marshallPrefix (mkQVarName Nothing ifaceName) (x:_) -> prefix marshallPrefix x -- unwrap the ForeignPtrs. unravel cont = foldr (\ (f,_) acc -> funApp withForeignPtrName [var f, lam [patVar f] acc]) cont fs where fs = filter (isFOTy.snd) param_tys binder = case mbRes of Nothing -> bind_ Just v -> (\ m n -> bind m v n) -- fp; dontcha just love it! iptr = var "iptr" methPtr = var "methPtr" offset = lit (iLit methNo) \end{code} Call the primitive. Assume that all arguments have been marshalled into an appropriate form and that their marshalled representations are in scope as the names given in the parameter list. \begin{code} thePrimCall :: Bool -> String -> Maybe Name -> Maybe Name -> Id -> Result -> [Param] -> (Haskell.Expr, Maybe Haskell.Expr) thePrimCall isComMeth mname mb_prim mb_prim_nm f res params | isVoidTy r_ty || (isComMeth && isHRESULT res) = (f_app, Nothing) | otherwise = (f_app, Just r_res) where f_app = funApp (mkVarName meth_name) args r_ty = resultType res r_res = var (outPrefix ++ idName f) args = foldr mkArg [] params mkArg p acc = case paramType p of Integer LongLong _ | optHugs && optLongLongIsInteger -> var (nm ++ "_lo") : var (nm ++ "_hi") : acc _ -> var nm : acc where nm = idName (paramId p) -- see primDecl comment. fun_nm = mname ++ '_':idName f meth_name = case mb_prim `mplus` mb_prim_nm of Nothing -> mkPrimitiveName fun_nm Just x -> x {- The predicate may look a bit odd - check the HRESULT return code if it is *not* an object method. The reason for this is that the HRESULT return code have already been checked for by special object method call wrappers. If the result has the attribute [usesgetlasterror], then in the event of error, we fish out the return code and msg by calling GetLastError() - Win32 specific, although we could give [usesgetlasterror] a valid interpretation on most Unices too (use errno.) -} okResult :: Bool -> Id -> Result -> Mm () okResult isObj f res | attrs `hasAttributeWithName` "error_handler" = case findAttribute "error_handler" attrs of Just (Attribute _ [ParamLit (StringLit s)]) -> addCode (bind_ (funApp (toQualName s) [r_res])) _ -> return () | isObj = return () | attrs `hasAttributeWithName` "usesgetlasterror" = addCode (bind_ (funApp check2HR [r_res])) | not (isHRESULT res) = return () | otherwise = addCode (bind_ (funApp checkHR [r_res])) where r_res = var (outPrefix ++ idName f) {- Note: for method Ids, the renaming stage has transferred all attributes from the result type onto the method Id, so we don't need to fish out the result type attributes here. -} attrs = idAttributes f \end{code} In order to toss some of the input parameters over the fence, the stub function may have to marshall 'em and store them in an external, non-moveable heap. Before returning from the stub, we make sure that any such allocations are freed up. We currently don't assume the presence of a marshalling arena for the allocation of marshalled values, so we cannot free the allocated memory in one fell swoop. \begin{code} freeInParamStorage :: DependInfo -> [Param] -> Mm () freeInParamStorage dep_info ps = do sequence (map freeParam ps) return () where freeParam p -- special fall-thru case for [sequence, length_is()] - sigh. | isJust dep_res && not has_seq = freeDependent i (findParamTy ps) deps | attrs `hasAttributeWithName` "nofree" = return () | otherwise = case (mbFreeType (paramType p)) of Nothing -> return () Just e -> addCode (bind_ (funApply e [var (idName (paramId p))])) where i = paramId p attrs = idAttributes i has_seq = hasSeqAttribute attrs dep_res = lookupDepender dep_info i (Just deps) = dep_res \end{code} \begin{code} isIsomorphicMethod :: String -> Result -> [Param] -> CgM (Maybe Bool) isIsomorphicMethod nm res params = do env <- getIsoEnv case lookupEnv env nm of Nothing -> return Nothing Just alts -> case break (match) alts of (_,[]) -> return Nothing (as,(flg,r,ps):bs) -> do setIsoEnv (replaceElt env nm ((False,r,ps):as++bs)) return (Just flg) where len_params = length params match (_,r,ps) = resultType r == resultType res && len_params == length ps && all (\ (p1,p2) -> paramMode p1 == paramMode p2 && paramType p1 == paramType p2) -- ToDo: check attributes too. (zip ps params) \end{code}