%
% (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
-> CgM HDecl
cgMethod i cconv result params offs mb_prim =
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
| optCoalesceIsomorphicMethods -> return emptyDecl
_
| isHidden -> return emptyDecl
| otherwise -> do
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
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
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
-> String
-> [QualName]
-> Maybe Bool
-> IfaceType
-> Bool
-> Bool
-> Maybe Name
-> Maybe Name
-> CallConv
-> Int
-> Id
-> 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)
in
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)
enumName =
case idOrigName methId of
_:'e':'m':'o':'t':'e':'N':'e':'x':'t':_ -> "Next"
n -> n
elt_ty = removePtr (paramType (head results))
sz_of = szType elt_ty
write_elt = refUnmarshallType stubMarshallInfo elt_ty
args =
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]
(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'
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
(in_tys_1, res_ty) =
constrainIIDParams
(paramToHaskellType par_deps isServer isAuto False)
(paramToHaskellType res_deps isServer isAuto True)
real_params
results
in_tys =
(if isJust mb_prim then
((toHaskellBaseMethodTy False prim_params result):)
else
id) in_tys_1
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 False
par_deps (findParamTy params_and_result)
allocateOutParams (raw_inout_deps++raw_out_deps)
(findParam params_and_result)
(removeDependees raw_inout_deps outs)
marshallParams True False isServer (removeDependents in_deps real_ins)
marshallParams True 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}
}
(real_params, par_deps) = findParamDependents True pars
(_, in_deps) = findParamDependents True ins
(real_ins, _) = findParamDependents False ins
(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
= 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
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
= 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 ls
in
case sz_allocs of
[] -> allocPointerTo ty
(DepNone:_) -> allocPointerTo ty
(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'
]
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)
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_t'
| sz_t < lONG_SIZE = lONG_SIZE
| 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)
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
(_, _, 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 ||
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
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)
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)
fun_nm = mname ++ '_':idName f
meth_name =
case mb_prim `mplus` mb_prim_nm of
Nothing -> mkPrimitiveName fun_nm
Just x -> x
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)
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
| 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)
(zip ps params)
\end{code}