% % (c) The Foo Project, University of Glasgow, 1998 % % @(#) $Docid: Jun. 10th 2003 12:17 Sigbjorn Finne $ % @(#) $Contactid: sof@galois.com $ % MarshallType provides the following: - converting IDL types to Haskell ones. - functions for generating code that (un)marshalls a value of an IDL type. - marshalling dependent parameter types \begin{code} module MarshallType ( marshallType , unmarshallType , refMarshallType , refUnmarshallType , szType , allocPointerTo , freeType , mbFreeType , needsFreeing , coreToHaskellExpr , coerceTy , coerceToInt -- :: Expr -> Haskell.Expr ) where import Prelude hiding ( mod ) import BasicTypes import Attribute import Literal import LibUtils import Utils ( mapFromMb, trace, notNull ) import qualified AbstractH as Haskell ( Expr(..) ) --import qualified PpAbstractH as PPHaskell ( showAbstractH ) import AbsHUtils import CoreIDL import CoreUtils import PpCore ( showCore, ppType ) import Maybe ( fromMaybe, isJust, fromJust ) import MarshallCore import MarshallMonad import Opts ( optHaskellToC, optLongLongIsInteger, optCorba, optCom, optNoWideStrings, optNoDerefRefs ) import TypeInfo ( TypeInfo(..) ) \end{code} %* % Marshalling types % %* From a given IDL type t we may need to generate calls to functions that implement various marshalling operations over type t: - marshall, by-value and by-reference - unmarshall, by-value and by-reference - size of - allocate - release/free The following functions can be used to do this: \begin{verbatim} marshallType :: MarshallInfo -> Type -> Haskell.Expr refMarshallType :: MarshallInfo -> Type -> Haskell.Expr unmarshallType :: MarshallInfo -> Type -> Haskell.Expr refUnmarshallType :: MarshallInfo -> Type -> Haskell.Expr szType :: MarshallInfo -> Type -> Haskell.Expr -- alloc storage to hold a (fixed size) value of a particular type, -- returning a pointer to it. allocPointerTo :: Type -> Haskell.Expr \end{verbatim} \begin{code} marshallType :: MarshallInfo -> Type -> Haskell.Expr marshallType mInfo t = case t of Array at [] | forRef mInfo -> funApp w_list [ varName false, szType at, refMarshallType mInfo at ] | otherwise -> funApp m_list [ szType at, refMarshallType mInfo{forRef=True} at ] Array (Char _) [e] | forRef mInfo -> funApp w_bstring [ varName false, coreToHaskellExpr e ] Array at [e] | forRef mInfo -> funApp w_blist [ szType at , coreToHaskellExpr e , refMarshallType mInfo{forRef=True} at ] | otherwise -> funApp m_blist [ szType at , coreToHaskellExpr e , refMarshallType mInfo{forRef=True} at ] Array at [mi,ma] -> funApp m_blist [ szType at , binOp Sub (coreToHaskellExpr ma) (coreToHaskellExpr mi) , refMarshallType mInfo{forRef=True} at ] Pointer _ _ ty | isVoidTy ty -> varName prelReturn Pointer Ptr _ ty | (isConstructedTy (nukeNames ty) && isReferenceTy ty) -> varName prelReturn Pointer pt isExp (Iface nm mod _ attrs _ inh) -> case pt of Unique | optCom && isExp -> funApp marshallMaybe [ varName m_iptr, varName nullFO ] | optHaskellToC && isExp -> funApp marshallMaybe [ varName m_ip , varName $ if attrs `hasAttributeWithName` "finaliser" then nullFO else nullPtr ] _ -> case inh of [] | optHaskellToC -> varName m_ip | otherwise -> varName m_iptr (x:_) | optCorba -> varName (prefix marshallPrefix (fst x)) | optCom || qName (fst x) == "IUnknown" -> varName m_iptr | otherwise -> varName (prefix marshallPrefix (fst x)) where m_ip = prefix marshallPrefix (mkQVarName mod nm) Pointer pt _ (Name _ _ _ _ _ (Just ti)) | pt /= Ptr && is_pointed ti -> let mshaller | forInOut mInfo && finalised ti = varName (copy_marshaller ti) | otherwise = varName (marshaller ti) null_elt | forInOut mInfo && finalised ti = varName nullPtr | finalised ti = varName nullFO | otherwise = varName nullPtr in case pt of Unique -> funApp marshallMaybe [ mshaller, null_elt ] _ -> mshaller Pointer pt _ ty | isFunTy ty && pt /= Ptr -> marshallType mInfo ty | forInOut mInfo && forProxy mInfo -> refMarshallType mInfo t | otherwise -> case pt of Ptr -> varName prelReturn Ref -> funApp m_ref [ allocPointerTo ty, refMarshallType mInfo{forRef=False} ty ] Unique -> funApp m_unique [ allocPointerTo ty, refMarshallType mInfo{forRef=False} ty ] Void -> varName prelReturn -- this shouldn't really happen. String _ isUnique _ | isUnique -> funApp marshallMaybe [varName m_string, varName nullPtr] | otherwise -> varName m_string Sequence ty mbSz mbTerm -> funApp m_sequence [ refMarshallType mInfo{forRef=False} ty , terminatorElt True mbTerm ty , szType ty , case mbSz of Nothing -> nothing Just x -> just (coreToHaskellExpr x) ] SafeArray{} -> varName m_safearray WString isUnique _ | isUnique -> funApp marshallMaybe [varName m_wstring, varName nullPtr] -- | isUnique -> funApp marshallMaybe [varName m_wstring, varName nullFO] | otherwise -> varName m_wstring Bool -> varName m_bool Enum{} -> varName m_enum32 Name _ _ _ _ _ (Just ti) | forInOut mInfo && finalised ti -> varName (copy_marshaller ti) | otherwise -> varName (marshaller ti) Name _ _ _ _ (Just ty@Enum{}) _ -> marshallType mInfo ty Name _ _ _ _ (Just ty@(Name{})) _ -> marshallType mInfo ty Name nm _ mod _ (Just (Struct i [f] _)) _ | isSimpleTy (fieldType f) || ((idAttributes i) `hasAttributeWithName` "hs_newtype") -> qvar mod (marshallPrefix ++ nm) Name nm _ mod _ (Just ty) _ | isConstructedTy (nukeNames ty) && not (isFunTy ty) -> funApp m_ref [ allocPointerTo t, refMarshallType mInfo{forRef=False} t ] | isFunTy ty -> qvar mod (marshallPrefix ++ nm) | otherwise -> marshallType mInfo ty Struct i fs _ | length fs == 1 && isSimpleTy (fieldType (head fs)) && not ((idAttributes i) `hasAttributeWithName` "hs_newtype") -> qvar (idModule i) (marshallPrefix ++ idName i) | otherwise -> funApp m_ref [ allocPointerTo t, refMarshallType mInfo{forRef=False} t ] Union{} -> funApp m_ref [ allocPointerTo t, refMarshallType mInfo{forRef=False} t ] UnionNon{} -> funApp m_ref [ allocPointerTo t, refMarshallType mInfo{forRef=False} t ] CUnion{} -> funApp m_ref [ allocPointerTo t, refMarshallType mInfo{forRef=False} t ] Integer LongLong isSigned | optLongLongIsInteger -> varName (m_integer isSigned) Iface{} -> marshallType mInfo (Pointer Ref True t) _ -> let nm = mkMarshaller marshallPrefix t in varName nm where m_bool = libImport bool m_string = libImport stringName m_list = libImport list m_blist = libImport blist m_ref = libImport ref m_unique = libImport unique m_sequence = libImport "Sequence" m_safearray | forProxy mInfo = prefix marshallPrefix sAFEARRAY | otherwise = prefix marshallPrefix (mkQVarName autoLib safearray) w_list = prefix marshallRefPrefix (mkQVarName hdirectLib list) w_bstring = prefix marshallRefPrefix (mkQVarName hdirectLib bstring) w_blist = prefix marshallRefPrefix (mkQVarName hdirectLib blist) m_integer isSigned | isSigned = libImport integer | otherwise = libImport ('U':integer) m_enum32 = libImport enum32 m_iptr = prefix marshallPrefix iUnknown m_wstring | optNoWideStrings = wideImport wstring2 | otherwise = wideImport wstring libImport nm = prefix marshallPrefix (mkQVarName hdirectLib nm) wideImport nm = prefix marshallPrefix (mkQVarName wStringLib nm) terminatorElt :: Bool -> Maybe Expr -> Type -> Haskell.Expr terminatorElt forMarshalling mbTerm ty | forMarshalling = lam [patVar "x"] $ funApp (prefix marshallRefPrefix termTyName) [ var "x" , termVal ] | otherwise = lam [patVar "ptr"] $ bind (funApp (prefix unmarshallRefPrefix termTyName) [var "ptr"]) (var "dx") (ret (infixOp (var "dx") eqName termVal)) where (termVal, termTyName) = case ty of Char{} -> (Haskell.Lit (CharLit '\0'), mkQVarName hdirectLib "Char") Integer sz signed -> case mbTerm of Just e -> (coreToHaskellExpr e, deTyCon $ mkIntTy sz signed) _ -> (coreToHaskellExpr (Lit (iLit (0::Int))), deTyCon $ mkIntTy sz signed) _ -> (varName nullPtr, mkQVarName hdirectLib "Ptr") \end{code} \begin{code} unmarshallType :: MarshallInfo -> Type -> Haskell.Expr unmarshallType mInfo ty = case ty of Array (Char _) [] -> varName u_string {- Handling open arrays when unmarshalling is tricky if you haven't got any information on the size of the thing. Nontheless, we represent open arrays as lists, since there's no trouble marshalling a list into an (open) array - it's this other way which is troublesome. So..we emit code which tries to marshall *one* element from the array here. Elsewhere we emit a warning message that the unmarshaller for a struct containing open arrays isn't 100% cool. The fix is either to use [size_is()] or manually tweak the gen'ed code. ToDo: add support for the notion of zero terminated vectors? -} Array t [] -> funApp u_single [ refUnmarshallType mInfo{forRef=True} t ] Array (Char _) [e] -> funApp u_bstring [ coreToHaskellExpr e ] Array t [e] | doFree mInfo -> funApp doThenFree [ freeType ty , funApp u_list [ szType t , var "0" , coreToHaskellExpr e , refUnmarshallType mInfo{forRef=True} t ] ] | otherwise -> funApp u_list [ szType t , var "0" , coreToHaskellExpr e , refUnmarshallType mInfo{forRef=True} t ] Array t [mi,ma] | doFree mInfo -> funApp doThenFree [ freeType ty , funApp u_list [ szType t , coreToHaskellExpr mi , coreToHaskellExpr ma , refUnmarshallType mInfo{forRef=True} t ] ] | otherwise -> funApp u_list [ szType t , coreToHaskellExpr mi , coreToHaskellExpr ma , refUnmarshallType mInfo{forRef=True} t ] String _ isUnique _ | doFree mInfo -> funApp doThenFree [ freeType ty, uString ] | otherwise -> uString where uString | isUnique = funApp readMaybe [ varName u_string ] | otherwise = varName u_string Sequence t mbSz mbTerm -> funApp u_sequence [ refUnmarshallType mInfo{forRef=False} t , terminatorElt False mbTerm t , szType t , case mbSz of Nothing -> nothing Just x -> just (coreToHaskellExpr x) ] WString isUnique _ | doFree mInfo -> funApp doThenFree [ freeType ty, uString ] | otherwise -> uString where uString | isUnique = funApp readMaybe [ varName u_wstring ] | otherwise = varName u_wstring Pointer pt isExp t@(Iface nm mod _ attrs _ inh) -> case pt of -- see marshallType comment re: raw interface ptrs. Ptr | optCom -> u_ipointer Unique | (optCom || optHaskellToC) && isExp -> if forRef mInfo then funApp u_unique [ refUnmarshallType mInfo{forRef=False} t ] else funApp u_unique [ unmarshallType mInfo t ] _ -> case inh of [] | optCom -> u_ipointer | optHaskellToC && attrs `hasAttributeWithName` "finaliser" -> funApp u_ip [ lit (BooleanLit (not (forProxy mInfo))) ] | otherwise -> varName u_ip (x:_) | optCorba -> varName (prefix unmarshallPrefix (fst x)) | optCom || qName (fst x) == "IUnknown" -> if forRef mInfo then refUnmarshallType mInfo{forRef=False} ty else u_ipointer | optHaskellToC && attrs `hasAttributeWithName` "finaliser" -> funApply (varName (prefix unmarshallPrefix (fst x))) [ lit (BooleanLit (not (forProxy mInfo))) ] | otherwise -> varName (prefix unmarshallPrefix (fst x)) where u_ip = prefix unmarshallPrefix (mkQVarName mod nm) Pointer _ _ (Name _ _ _ _ _ (Just ti)) | is_pointed ti -> if finalised ti then funApply (varName (unmarshaller ti)) [lit (BooleanLit (not (forProxy mInfo))) ] else varName (unmarshaller ti) Pointer _ _ Void -> varName prelReturn Pointer pt _ t -> case pt of Ptr | isIfacePtr t -> if forRef mInfo then refUnmarshallType mInfo{forRef=False} t else u_ipointer | isPointerTy t && doFree mInfo -> funApp doThenFree [ freeType ty , refUnmarshallType mInfo{forRef=False} t ] | isPointerTy t -> refUnmarshallType mInfo{forRef=False} t | otherwise -> varName prelReturn Ref | doFree mInfo -> funApp doThenFree [ freeType ty , refUnmarshallType mInfo{forRef=False} t ] | otherwise -> refUnmarshallType mInfo{forRef=False} t Unique | isIfacePtr t -> if forRef mInfo then funApp u_unique [ refUnmarshallType mInfo{forRef=False} (getIfaceTy ty) ] else funApp u_unique [ unmarshallType mInfo (getIfaceTy ty) ] | doFree mInfo -> funApp doThenFree [ freeType ty , funApp u_unique [ refUnmarshallType mInfo{forRef=False} t ] ] | otherwise -> funApp u_unique [ refUnmarshallType mInfo{forRef=False} t ] Void -> varName prelReturn Bool -> varName u_bool Name _ _ _ _ _ (Just ti) {- not (is_pointed ti) -} -> if finalised ti then funApply (varName (unmarshaller ti)) [lit (BooleanLit (not (forProxy mInfo))) ] else varName (unmarshaller ti) Name _ _ _ _ (Just t@Enum{}) _ -> unmarshallType mInfo t Name _ _ _ _ (Just t@(Name{})) _ -> unmarshallType mInfo t Name nm _ mod _ (Just (Struct i [f] _)) _ | isSimpleTy (fieldType f) || ((idAttributes i) `hasAttributeWithName` "hs_newtype") -> qvar mod (unmarshallPrefix ++ nm) Name _ _ _ _ (Just t) _ | isConstructedTy (nukeNames t) -> refUnmarshallType mInfo{forRef=False} (Pointer Ref True ty) Struct i fs _ | (length fs == 1 && isSimpleTy (fieldType (head fs))) || ((idAttributes i) `hasAttributeWithName` "hs_newtype") -> qvar (idModule i) (unmarshallPrefix ++ idName i) | otherwise -> refUnmarshallType mInfo{forRef=False} (Pointer Ref True ty) Union{} -> refUnmarshallType mInfo{forRef=False} (Pointer Ref True ty) UnionNon{} -> refUnmarshallType mInfo{forRef=False} (Pointer Ref True ty) CUnion{} -> refUnmarshallType mInfo{forRef=False} (Pointer Ref True ty) Enum{} -> varName u_enum32 Iface{} -> unmarshallType mInfo (Pointer Ptr True ty) SafeArray{} -> u_safearray Integer LongLong isSigned | optLongLongIsInteger -> varName (u_integer isSigned) _ -> let nm = mkMarshaller unmarshallPrefix ty in varName nm where u_bool = libImport bool u_string = libImport stringName u_list = libImport list u_bstring = libImport bstring u_unique = libImport unique u_single = libImport "Single" u_sequence = libImport "Sequence" u_safearray | forProxy mInfo = funApply (varName (prefix unmarshallPrefix sAFEARRAY)) [ lit (BooleanLit False) ] | forStruct mInfo = funApply (varName (prefix unmarshallPrefix sAFEARRAY)) [ lit (BooleanLit False) ] | otherwise = varName (prefix marshallPrefix (mkQVarName autoLib safearray)) u_integer isSigned | isSigned = libImport integer | otherwise = libImport ('U':integer) u_enum32 = libImport enum32 u_iptr = prefix unmarshallPrefix iUnknown u_ipointer | forInOut mInfo = varName (prefix unmarshallPrefix iUnknownFO) | otherwise = funApp u_iptr [ lit (BooleanLit (forProxy mInfo)) ] u_wstring | optNoWideStrings = wStringImport wstring2 | otherwise = wStringImport wstring libImport nm = prefix unmarshallPrefix (mkQVarName hdirectLib nm) wStringImport nm = prefix unmarshallPrefix (mkQVarName wStringLib nm) \end{code} \begin{code} refMarshallType :: MarshallInfo -> Type -> Haskell.Expr refMarshallType mInfo t = case t of Array (Char _) [e] -> funApp w_bstring [ varName true, coreToHaskellExpr e ] Array at [e] -> funApp w_blist [ szType at , coreToHaskellExpr e , refMarshallType mInfo{forRef=False} at ] Array at [mi, ma] -> funApp w_blist [ szType at , binOp Sub (coreToHaskellExpr ma) (coreToHaskellExpr mi) , refMarshallType mInfo{forRef=False} at ] Array at [] -> funApp w_list [ varName false , szType at , refMarshallType mInfo{forRef=False} at ] SafeArray{} -> varName w_safearray String _ isUnique _ | isUnique -> funApp writeMaybe [ varName w_string ] | otherwise -> funApp w_string [ varName true ] WString isUnique _ | isUnique -> funApp writeMaybe [ varName w_string ] | otherwise -> varName w_wstring Sequence ty mbSz mbTerm -> funApp w_sequence [ varName true , refMarshallType mInfo{forRef=False} ty , terminatorElt True mbTerm ty , szType ty , case mbSz of Nothing -> nothing Just x -> just (coreToHaskellExpr x) ] {- Hmm..I'm not sure what is the right thing here - include the Maybe or not. If the Maybe type is used, then MarshallCore.toHaskellTy will need to change accordingly. -} Pointer Unique isExp ty | forRef mInfo && (optCom || optHaskellToC) && isExp && isIfaceTy ty -> funApp writeMaybe [ w_ip ] where (Iface nm mod _ _ _ _) = getIfaceTy ty w_ip | optHaskellToC = varName (prefix marshallRefPrefix (mkQVarName mod nm)) | otherwise = funApp w_iptr [w_iptr_arg] Pointer _ _ (Iface nm mod _ _ _ inh) -> case inh of [] | not optHaskellToC -> funApp w_iptr [ w_iptr_arg ] | otherwise -> varName w_ip (x:_) | optCorba -> varName (prefix marshallRefPrefix (fst x)) | not optHaskellToC -> funApp w_iptr [ w_iptr_arg ] | otherwise -> varName (prefix marshallRefPrefix (fst x)) where w_ip = prefix marshallRefPrefix (mkQVarName mod nm) Pointer pt _ (Name _ _ _ _ _ (Just ti)) | pt /= Ptr && is_pointed ti -> case pt of Unique -> if finalised ti then funApp writeMaybe [ varName (ref_marshaller ti) ] else funApp writeMaybe [ varName (ref_marshaller ti) ] _ -> varName (ref_marshaller ti) Pointer Ptr _ (Name _ _ _ (Just as) _ _) | as `hasAttributeWithName` "foreign" -> varName w_fptr Pointer _ _ ty | isVoidTy ty -> varName w_ptr Pointer pt _ ty | isIfaceTy ty -> refMarshallType mInfo{forRef=False} (Pointer Ref True (getIfaceTy ty)) | otherwise -> case pt of Ptr -> varName w_ptr Ref | optNoDerefRefs -> funApp w_ref [ allocPointerTo ty, refMarshallType mInfo ty ] | otherwise -> refMarshallType mInfo ty Unique -> funApp w_unique [ allocPointerTo ty, refMarshallType mInfo ty ] Void -> varName w_addr Bool -> varName w_bool CUnion i _ _ -> let nm = mkMarshaller marshallRefPrefix t sw = case (getSwitchIsAttribute (idAttributes i)) of Just e | notNull fs -> let v = head fs in var ("write_tag_" ++ v) where fs = findFreeVars e _ -> var ("where_do_I_put_the_tag") in funApply (varName nm) [sw] Iface{} -> refMarshallType mInfo (Pointer Ref True t) Name _ _ _ _ _ (Just ti) | not (is_pointed ti) -> varName (ref_marshaller ti) Name _ _ _ _ (Just ty@Enum{}) _ -> refMarshallType mInfo ty Name _ _ _ _ (Just ty@(Name{})) _ -> refMarshallType mInfo ty Enum{} -> varName w_enum32 Name nm _ mod _ (Just ty) _ | not is_cons_type -> refMarshallType mInfo ty | not (forStruct mInfo) && isNonEncUnionTy (removeNames ty) -> funApply w_ty w_args | otherwise -> if isFinalisedType True ty then funApply w_ty [ w_iptr_arg ] else w_ty where is_cons_type = isConstructedTy (nukeNames ty) w_ty = varName (prefix marshallRefPrefix (mkQVarName mod nm)) w_args | isFinalisedType True ty = [ w_iptr_arg, write_tag ] | otherwise = [ write_tag ] write_tag = lam [wildPat] (ret unit) Integer LongLong isSigned | optLongLongIsInteger -> varName (w_integer isSigned) _ -> varName (mkMarshaller marshallRefPrefix t) where w_bool = libImport bool w_string = libImport stringName w_blist = libImport blist w_list = libImport list w_bstring = libImport bstring w_ptr = libImport ptrName w_fptr = libImport fptr w_ref = libImport ref w_unique = libImport unique w_addr = libImport ptrName w_sequence = libImport "Sequence" w_safearray | forStruct mInfo = prefix marshallRefPrefix sAFEARRAY | otherwise = prefix marshallRefPrefix (mkQVarName autoLib safearray) w_integer isSigned | isSigned = libImport integer | otherwise = libImport ('U':integer) w_enum32 = libImport enum32 w_iptr = prefix marshallRefPrefix iUnknown w_iptr_arg | forStruct mInfo = var "addRefMe__" | otherwise = lit (BooleanLit (forProxy mInfo)) w_wstring | optNoWideStrings = wStringImport wstring2 | otherwise = wStringImport wstring libImport nm = prefix marshallRefPrefix (mkQVarName hdirectLib nm) wStringImport nm = prefix marshallRefPrefix (mkQVarName wStringLib nm) refUnmarshallType :: MarshallInfo -> Type -> Haskell.Expr refUnmarshallType mInfo t = case t of Array (Char _) [e] -> funApp r_bstring [ coreToHaskellExpr e ] -- this is the identity unmarshallers under the assumption -- that the array ref. type is used in conjunction with -- a 'dependent' attribute (size_is() et. al). This may -- not always be the case, so this is may cause breakage. -- -- 3/99: Changed to instead do something a bit saner, but -- tell the user of the problem. Array ty [] -> trace ("warning: unmarshalling incomplete array type decl, " ++ showCore (ppType t) ++ " , don't know how big.\n Assuming of length 1. " ) $ funApp r_blist [ szType t , lit (iLit (1::Int)) , refUnmarshallType mInfo{forRef=False} ty ] Array ty [e] -> funApp r_blist [ szType ty , coreToHaskellExpr e , refUnmarshallType mInfo{forRef=False} ty ] String _ isUnique _ | isUnique -> funApp readMaybe [ varName r_string ] | otherwise -> varName r_string WString isUnique _ | isUnique -> funApp readMaybe [ varName r_wstring ] | otherwise -> varName r_wstring SafeArray _ -> r_safearray Sequence ty mbSz mbTerm -> funApp r_sequence [ refUnmarshallType mInfo{forRef=False} ty , terminatorElt False mbTerm ty , szType ty , case mbSz of Nothing -> nothing Just x -> just (coreToHaskellExpr x) ] Pointer Unique isExp ty | forRef mInfo && (optCom || optHaskellToC) && isExp && isIfaceTy ty -> funApp r_unique [ u_ip ] where (Iface nm mod _ attrs _ _) = getIfaceTy ty u_ip | optHaskellToC = funApp (prefix unmarshallRefPrefix (mkQVarName mod nm)) (if attrs `hasAttributeWithName` "finaliser" then [ lit (BooleanLit (not (forProxy mInfo))) ] else [{-empty-}] ) | otherwise = funApp u_iptr [ final_arg ] Pointer _ _ (Iface nm mod _ attrs _ inh) -> case inh of [] | optCom -> funApp r_iptr [ final_arg ] | optHaskellToC && attrs `hasAttributeWithName` "finaliser" -> funApply r_ip [ lit (BooleanLit (not (forProxy mInfo))) ] | otherwise -> r_ip (x:_) | optCorba -> varName (prefix unmarshallRefPrefix (fst x)) | optCom -> funApp r_iptr [ final_arg ] | optHaskellToC && attrs `hasAttributeWithName` "finaliser" -> funApply (varName (prefix unmarshallRefPrefix (fst x))) [ lit (BooleanLit (not (forProxy mInfo))) ] | otherwise -> varName (prefix unmarshallRefPrefix (fst x)) where r_ip = varName (prefix unmarshallRefPrefix (mkQVarName mod nm)) {- Pointer Unique isExp ty | forRef mInfo && optCom && isIfaceTy ty -> funApp r_unique [ funApp u_iptr [ final_arg ]] -} Pointer pt _ (Name _ _ _ _ _ (Just ti)) | pt /= Ptr && is_pointed ti -> case pt of Unique -> funApp readMaybe [ e ] _ -> e where e | finalised ti = funApply (varName (ref_unmarshaller ti)) [ final_arg ] | otherwise = varName (ref_unmarshaller ti) Pointer Ref _ t1@(Name _ _ _ _ (Just ty) _) | isConstructedTy (nukeNames ty) -> if isFinalisedType False ty then funApply r_ty [ final_arg ] else r_ty where r_ty = varName (mkMarshaller unmarshallRefPrefix t1) Pointer Ptr _ (Name _ _ _ (Just as) _ _) | as `hasAttributeWithName` "foreign" -> varName r_fptr Pointer _ _ Void -> varName r_ptr Pointer pt _ ty | isIfaceTy t -> refUnmarshallType mInfo (Pointer Ref True (getIfaceTy t)) | otherwise -> case pt of Ptr -> varName r_ptr -- we stop unmarshalling once we encounter a [ptr] pointer. Ref -> -- I don't understand the motivation for ignoring the [ref] here any longer! -- Conseq, -fopt-no-deref-refs can be used to turn off this 'feature' until -- the ramifications of doing it one way vs. the other can be more carefully -- assessed (same option applies to ref-marshalling of [ref]s.) if (forRef mInfo || not (isPointerTy ty)) && not optNoDerefRefs then refUnmarshallType mInfo ty else funApp r_ref [ refUnmarshallType mInfo{forRef=False} ty ] Unique -> funApp r_unique [ refUnmarshallType mInfo{forRef=False} ty ] Void -> varName r_addr Bool -> varName r_bool CUnion i _ _ -> let nm = mkMarshaller unmarshallRefPrefix t sw = case (getSwitchIsAttribute (idAttributes i)) of Just e | notNull fs -> let v = head fs in var ("read_tag_" ++ v) where fs = findFreeVars e _ -> var ("where_do_I_get_the_tag_from") in funApply (varName nm) [sw] Iface{} -> refUnmarshallType mInfo (Pointer Ref True t) Name _ _ _ _ _ (Just ti) {- not (is_pointed ti) -} -> if finalised ti then funApply (varName (ref_unmarshaller ti)) [ final_arg ] else varName (ref_unmarshaller ti) Name _ _ _ _ (Just ty@Enum{}) _ -> refUnmarshallType mInfo ty Name _ _ _ _ (Just ty@(Name{})) _ -> refUnmarshallType mInfo ty Enum{} -> varName r_enum32 Integer LongLong isSigned | optLongLongIsInteger -> varName (r_integer isSigned) Name nm _ mod mb_attrs (Just ty) _ | not (forRef mInfo) && isNonEncUnionTy (removeNames ty) -> funApply (varName (mkMarshaller unmarshallRefPrefix t)) r_args | not is_cons_type -> refUnmarshallType mInfo ty | is_cons_type -> if isFinalisedType False ty then funApply r_ty [ final_arg ] else r_ty where r_ty = varName (prefix unmarshallRefPrefix (mkQVarName mod nm)) is_cons_type = isConstructedTy (nukeNames ty) attrs = fromMaybe [] mb_attrs ++ idAttributes (getTyTag (getNonEncUnionTy ty)) r_args | isFinalisedType False ty = [ final_arg, read_tag ] | otherwise = [ read_tag ] read_tag = case getSwitchIsAttribute attrs of Nothing -> ret (lit (iLit ((-1)::Int))) Just v -> ret (coreToHaskellExpr v) _ -> varName (mkMarshaller unmarshallRefPrefix t) where r_bool = libImport bool r_string = libImport stringName r_blist = libImport blist r_bstring = libImport bstring r_ptr = libImport ptrName r_fptr = libImport fptr r_ref = libImport ref r_unique = libImport unique r_addr = libImport ptrName r_sequence = libImport "Sequence" r_safearray | forStruct mInfo = funApply (varName (prefix unmarshallRefPrefix sAFEARRAY)) [ lit (BooleanLit False) ] | otherwise = funApply (varName (prefix unmarshallRefPrefix (mkQVarName autoLib safearray))) [ lit (BooleanLit (not (forProxy mInfo))) ] r_integer isSigned | isSigned = libImport integer | otherwise = libImport ('U':integer) r_enum32 = libImport enum32 r_iptr = prefix unmarshallRefPrefix iUnknown final_arg | forStruct mInfo = var "finaliseMe__" | otherwise = lit (BooleanLit (forProxy mInfo)) u_iptr = prefix unmarshallPrefix iUnknown r_wstring | optNoWideStrings = wStringImport wstring | otherwise = wStringImport wstring libImport nm = prefix unmarshallRefPrefix (mkQVarName hdirectLib nm) wStringImport nm = prefix unmarshallRefPrefix (mkQVarName wStringLib nm) \end{code} \begin{code} coreToHaskellExpr :: Expr -> Haskell.Expr coreToHaskellExpr e = case e of Binary bop e1 e2 -> binOp bop (coreToHaskellExpr e1) (coreToHaskellExpr e2) Cond e1 e2 e3 -> hCase (coreToHaskellExpr e1) [ alt (conPat (mkQConName prelude "True") []) (coreToHaskellExpr e2) , alt (conPat (mkQConName prelude "False") []) (coreToHaskellExpr e3) ] Unary uop e1 -> unaryOp uop (coreToHaskellExpr e1) Var nm -> var (mkHaskellVarName nm) Lit l -> lit l Cast _ e1 -> coreToHaskellExpr e1 -- Sizeof t -> funApp (mkQualName Nothing sizeOfName) [szType t] Sizeof t -> varName (mkMarshaller sizeofPrefix t) \end{code} The library functions does not rely on overloading of their numeric arguments. Instead, the generated code will insert coercions that map arguments to the expected types. \begin{code} coerceTy :: Type -> Type -> Haskell.Expr -> Haskell.Expr coerceTy from_ty to_ty e | to_ty == from_ty = e | otherwise = Haskell.WithTy (funApp (mkQualName prelude (mkHaskellVarName "fromIntegral")) [ e ]) (toHaskellBaseTy False to_ty) \end{code} Allocation and a couple of other functions in the support library uses Ints as arguments; make sure that arguments passed to them are properly coerced. \begin{code} coerceToInt :: Expr -> Haskell.Expr coerceToInt e = case e of Lit IntegerLit{} -> c_expr _ -> funApp fromIntegralName [c_expr] where c_expr = coreToHaskellExpr e \end{code} \begin{code} szType :: Type -> Haskell.Expr szType (Array t [e]) = binOp Mul (szType t) (coerceToInt e) szType t | isEnum = varName enumSize | isIntegerTy t = varName (prefix sizeofPrefix (mkQualName hdirectLib "Int64")) | otherwise = case t of Name _ _ _ _ _ (Just ti) -> varName (prim_size ti) _ -> varName (mkMarshaller sizeofPrefix t) -- _ -> varName (mkMarshaller sizeOfName t) where isEnum = isJust enumRes enumRes = checkIfEnum t (Just enumSize) = enumRes checkIfEnum Enum{} = Just (mkQVarName hdirectLib "sizeofInt32") checkIfEnum (Name _ _ _ _ (Just ty) _) = checkIfEnum ty checkIfEnum _ = Nothing \end{code} \begin{code} allocPointerTo :: Type -> Haskell.Expr allocPointerTo ty = case ty of Name _ _ _ _ _ (Just ti) | isJust (alloc_type ti) -> varName (fromJust (alloc_type ti)) Pointer _ _ (Name _ _ _ _ _ (Just ti)) | isJust (alloc_type ti) -> varName (fromJust (alloc_type ti)) _ -> funApp allocBytes [funApp fromIntegralName [szType ty']] where ty' = case ty of Void -> Pointer Ptr True Void Pointer _ _ Void -> ty _ -> removePtr ty \end{code} Generating a *call* to free a marshalled representation of type t (if needed.) The modules that deal with the generation of marshalling code for user-defined types take care of generating freeing/release functions for such types. \begin{code} freeType :: Type -> Haskell.Expr freeType ty = case mbFreeType ty of Nothing -> varName trivialFree Just e -> e mbFreeType :: Type -> Maybe Haskell.Expr mbFreeType ty = case ty of {- Pointer to structs decorated with [free(foo)] are freed using 'foo'. -} Pointer _ _ (Name _ _ _ _ (Just (Struct tg _ _)) _) | (idAttributes tg) `hasAttributeWithName` "free" -> case findAttribute "free" attrs of Just (Attribute _ [ParamLit (StringLit freeR)]) -> Just (var freeR) _ -> mbFreeType' True ty | attrs `hasAttributeWithName` "free_method" -> -- what an ad-hac hock! case findAttribute "free_method" attrs of Just (Attribute _ [ParamLit (StringLit freeR)]) -> Just (lam [varPat (var "x")] $ funApply (var freeR) [var "x", var "iptr"]) _ -> mbFreeType' True ty where attrs = idAttributes tg _ -> mbFreeType' True ty mbFreeType' :: Bool -> Type -> Maybe Haskell.Expr mbFreeType' isTop ty = case ty of Pointer _ _ (Name _ _ _ _ _ (Just ti)) | is_pointed ti && finalised ti -> Nothing Pointer _ _ Void -> Nothing Pointer _ _ Iface{} -> Nothing {- The Haskell representation of a unique pointer is as a Maybe-valued `thing', where the `thing' is the unmarshalled representation of the pointer type. Clearly, the thing we're pointing to will have to be freed, but it in turn may have to free up some of its components first. -} Pointer Unique _ t | isIfaceTy t -> Nothing | otherwise -> case mbFreeType' False t of Just v -> Just (funApp (prefix freePrefix (mkQVarName hdirectLib unique)) [ v ]) Nothing | isTop -> Just (varName free) | otherwise -> Nothing -- no marshalling was done on this in the first place, so nothing to free. Pointer Ptr _ _ -> Nothing -- the type embedded within the reference may have to be freed ... Pointer Ref _ t -> case (mbFreeType' False t) of Just v -> Just (funApp (prefix freePrefix (mkQVarName hdirectLib ref)) [v]) Nothing | isTop -> Just (varName free) -- just free the toplevel pointer. | otherwise -> Nothing -- the array may consist of elements that may need to be freed -- individually, so we better check by looking at the element type.. -- [in many cases element-wise freeing isn't right, since the -- array was block allocated initially, so let's not do this for -- now -- assume block allocation.] Array aty _ | needsFreeing aty -> Just (varName free) | isTop -> Just (varName free) | otherwise -> Nothing -- the String/sequence type contains a null-terminated sequence of unpointed objects, -- so no need to do per-elt release/free. String{} -> Just (varName (prefix freePrefix (mkQVarName hdirectLib stringName))) WString{} -> Just (varName (prefix freePrefix (mkQVarName wStringLib wstring))) Sequence{} -> Just (varName (prefix freePrefix (mkQVarName hdirectLib "Sequence"))) -- nothing to free for marshalled enums (not in an external heap, anyway.) Enum{} -> Nothing -- Note: a toplevel struct is currently not possible, so we don't -- actually free the structure itself, but assume that whoever has a pointer to -- this struct value does. -- -- Notice that since we have complete information about what the struct -- contains, we could have generated the calls to free up the fields -- that needs to be explicitly release here. However, the cost of doing -- this inline hardly seems worth it. (Rely on the Haskell compiler -- to do the inlining instead.) -- Struct tg fields _ | any (needsFreeing.fieldType) fields -> Just (varName (prefix freePrefix (mkVarName (idName tg)))) | otherwise -> Nothing -- See above comment for structs. -- The tag type isn't pointed (I hope!), so we only need to worry about -- freeing the embedded value. Union i _ _ _ switches | swNeedsFreeing switches -> Just (varName (prefix freePrefix (mkVarName (idName i)))) | otherwise -> Nothing UnionNon i switches | swNeedsFreeing switches -> Just (varName (prefix freePrefix (mkVarName (idName i)))) | otherwise -> Nothing CUnion i fields _ | any (needsFreeing.fieldType) fields -> Just (varName (prefix freePrefix (mkVarName (idName i)))) | otherwise -> Nothing Name nm _ mod mb_attrs mb_ty mb_ti | don'tFree mb_attrs || (isJust mb_ti && finalised (fromJust mb_ti)) -> Nothing | isJust mb_ti && isJust (free_type (fromJust mb_ti)) -> fmap varName (free_type (fromJust mb_ti)) | otherwise -> case mb_ty of Nothing -> Nothing -- Hmm.. Just t -- want the name of the constructed type rather than its tag...if it needs to be released. | isConstructedTy (nukeNames t) -> case mbFreeType' False t of Just _ | isTop -> Just (funApply (varName (prefix freePrefix (mkQConName mod (mkHaskellTyConName nm)))) args) _ -> Nothing | otherwise -> case mbFreeType' False t of Just aty | not (don'tFree mb_attrs) -> Just aty | otherwise -> Nothing Nothing -> Nothing where args | isNonEncUnionTy t = [read_tag] | otherwise = [] attrs = fromMaybe [] mb_attrs read_tag = case getSwitchIsAttribute attrs of Nothing -> lit (iLit ((-1)::Int)) Just v -> coreToHaskellExpr v _ -> Nothing where don'tFree Nothing = False don'tFree (Just ls) = ls `hasAttributeWithName` "nofree" -- poorly named, but `needsFreeing' returns True -- if a marshalled representation of an IDL type -- has to be explicitly released (=> it was allocated -- externally.) needsFreeing :: Type -> Bool needsFreeing t = go True t where go toplevel ty = case ty of Pointer _ _ (Name _ _ _ _ _ (Just ti)) | is_pointed ti -> not (finalised ti) Pointer Unique _ pty -> needsFreeing pty Pointer Ref _ pty -> needsFreeing pty Pointer Ptr _ _ -> False Array _ _ -> True String{} -> toplevel WString{} -> toplevel Name _ _ _ _ _ (Just ti) -> not (finalised ti) Name _ _ _ _ mb_ty _ -> mapFromMb False needsFreeing mb_ty Struct _ fields _ -> any ((go False).fieldType) fields Union _ _ _ _ switches -> any sw_go switches UnionNon _ switches -> any sw_go switches CUnion _ fields _ -> any ((go False).fieldType) fields _ -> False -- basic numeric/char types + void & enum. sw_go (SwitchEmpty _) = False sw_go s = go False (switchType s) swNeedsFreeing :: [Switch] -> Bool swNeedsFreeing [] = False swNeedsFreeing (SwitchEmpty _ : xs) = swNeedsFreeing xs swNeedsFreeing (s : xs) = (needsFreeing (switchType s)) || swNeedsFreeing xs \end{code}