%
% (c) The Foo Project, University of Glasgow, 1998
%
% @(#) $Docid: Feb. 9th 2003 08:01 Sigbjorn Finne $
% @(#) $Contactid: sof@galois.com $
%
Generating code for going between IDL structs and
their Haskell equivalent.
\begin{code}
module MarshallStruct ( marshallStruct ) where
import BasicTypes ( Name, QualName, qName )
import Literal ( iLit )
import Attribute
import AbstractH ( HDecl )
import qualified AbstractH as Haskell ( Expr, ConDecl )
import AbsHUtils
import LibUtils
import CgMonad
import CoreIDL
import CoreUtils
import MarshallUtils
import MarshallMonad
import MarshallType ( refMarshallType
, refUnmarshallType
, unmarshallType
, marshallType
, freeType
, needsFreeing
)
import MarshallDep ( marshallDependents
, unmarshallDependents
)
import MarshallCore
import List ( findIndex, partition )
import Maybe ( mapMaybe )
import Utils ( diff, notNull )
import Opts ( optCom )
\end{code}
The marshalling of structs is mostly complete, but here's
a list of current shortcomings/ToDos:
- structure layout is very simplistic and wrong.
- there's chance of name capture, as the marshalling
routines introduce the names "ptr" and "pf"[0-9]+
- [ignore] attributes may just work..(lightly tested.)
\begin{code}
marshallStruct :: Name
-> Id
-> Haskell.ConDecl
-> [Field]
-> Maybe Int
-> CgM HDecl
marshallStruct tdef_name struct_tag datacon fields mb_pack = do
ds <- mapM exportDecl decl_list
return (andDecls ds)
where
decl_list =
(if needToFree then
((f_name, f_tysig `andDecl` f_def):)
else
id) $
(if simplStruct then
(\ x -> (m_name, m_tysig `andDecl` m_def):
(u_name, u_tysig `andDecl` u_def):x)
else
id)
[ (w_name, w_tysig `andDecl` w_def)
, (r_name, r_tysig `andDecl` r_def)
, (s_name, s_tysig `andDecl` s_def)
]
simplStruct = length (fields) == 1 &&
isSimpleTy field_ty
addRef_fields = isFinalisedType True (Struct struct_tag fields Nothing)
final_fields = isFinalisedType False (Struct struct_tag fields Nothing)
[field] = fields
field_ty = fieldType field
field_h_ty = toHaskellBaseTy False field_ty
v_field = var (mkHaskellVarName (idName (fieldId field)))
name = mkConName tdef_name
t_ty = tyConst tdef_name
b_ty = tyConst tdef_name
ptr = var "ptr"
pf0 = var "pf0"
field_names = map (idName.fieldId) fields
structCon = conDeclToCon datacon
structPat = conDeclToPat datacon
dep_list = findFieldDependents fields
fields_w = mapMaybe (adjustField True dep_list) fields
fields_r = mapMaybe (adjustField False dep_list) fields
w_name = qName (prefix marshallRefPrefix name)
w_tysig = typeSig w_name w_type
w_type
| optCom && addRef_fields = funTy tyBool (funTy (tyPtr b_ty) (funTy t_ty io_unit))
| otherwise = funTy (tyPtr b_ty) (funTy t_ty io_unit)
w_def = funDef w_name w_pats w_rhs
w_pats
| optCom && addRef_fields = [patVar "addRefMe__", varPat ptr, structPat]
| otherwise = [varPat ptr, structPat]
w_rhs = runMm Nothing field_names w_rest w_unpack
w_rest = foldr ($) (ret unit) ((hLet pf0 ptr) : w_fields)
w_fields = zipWith3 (refMarshallField dep_list
(offsetOfName fields offsets)
(findFieldTy fields))
rel_offsets
[(1::Int)..]
(tagLast fields_w)
w_unpack = marshallDependents True False
dep_list (findFieldOrigTy fields)
r_name = qName (prefix unmarshallRefPrefix name)
r_tysig = typeSig r_name r_type
r_type
| final_fields = funTy tyBool (funTy (tyPtr b_ty) (io t_ty))
| otherwise = funTy (tyPtr b_ty) (io t_ty)
r_def = funDef r_name r_pats r_rhs
r_pats
| final_fields = [patVar "finaliseMe__", varPat ptr]
| otherwise = [varPat ptr]
r_rhs = hLet pf0 ptr r_fields
r_fields =
foldr
($)
(runMm Nothing field_names (ret structCon) r_pack)
(zipWith3 (refUnmarshallField (offsetOfName fields offsets)
(findFieldTy fields))
rel_offsets
[(1::Int)..]
(tagLast fields_r))
r_pack = unmarshallDependents True False dep_list (findFieldOrigTy fields)
m_name = qName (prefix marshallPrefix name)
m_tysig = typeSig m_name (funTy t_ty (io field_h_ty))
m_def = funDef m_name [structPat] m_rhs
m_rhs = ret v_field
u_name = qName (prefix unmarshallPrefix name)
u_tysig = typeSig u_name (funTy field_h_ty (io t_ty))
u_def = funDef u_name [varPat v_field] u_rhs
u_rhs = ret structCon
s_name = qName (prefix sizeofPrefix name)
s_tysig = typeSig s_name tyWord32
s_def = funDef s_name [] s_rhs
s_rhs
| null fields = var "0"
| otherwise = var (show sz)
rel_offsets = diff offsets
((sz, _), offsets) = computeStructSizeOffsets mb_pack fields
needToFree = needsFreeing (Struct struct_tag fields Nothing)
f_name = qName (prefix freePrefix name)
f_tysig = typeSig f_name (funTy (tyPtr b_ty) (io_unit))
f_def = funDef f_name [varPat ptr] f_rhs
f_rhs = foldr1 (bind_) (map unmarshalTag field_switches ++
(mapMaybe freeField fields_sans_switches))
(field_switches, fields_sans_switches) =
partition (\ (f,_) -> isSwitchDependee dep_list (fieldId f))
(zip fields offsets)
struct_ptr__ = var "struct_ptr__"
field_ptr__ = var "field_ptr__"
unmarshalTag (f,offset) =
let v = idName (fieldId f) in
bind (funApply (refUnmarshallType structMarshallInfo (toBaseTy (fieldType f)))
[addPtr (var "ptr") (lit (iLit offset))])
(var v)
(ret unit)
freeField (f , offset)
| not (needsFreeing (fieldType f)) = Nothing
| otherwise =
let
ty = fieldOrigType f
e = freeType ty
in
Just (hLet struct_ptr__ (addPtr ptr (lit (iLit offset)))
(if (isPointerTy ty) then
(bind (funApp derefPtr [ struct_ptr__ ]) field_ptr__
(funApply e [field_ptr__]))
else
(funApply e [struct_ptr__])))
offsetOfName :: [Field] -> [Int] -> Name -> Int
offsetOfName fields offsets nm =
case (findIndex (\ f -> idName (fieldId f) == nm) fields) of
Nothing -> (1)
Just v -> offsets!!v
\end{code}
When marshalling the Haskell representation of a "struct"
into its external representation, @refMarshallField@ takes
care of generating code to marshall a given field plus
set up the offset for the code that will unmarshall the
next "struct" field (if any.)
\begin{code}
refMarshallField :: DependInfo
-> (Name -> Int)
-> (Name -> Type)
-> Int
-> Int
-> (Field, Bool)
-> (Haskell.Expr -> Haskell.Expr)
refMarshallField dep_list to_offset lookup_ty offset field_no (field, is_last) = \ hole ->
hLet pf (addPtr pf_prev (lit (iLit offset))) $
bind_ (funApply mshaller args) hole
where
f_id = fieldId field
ty = fieldType field
args
| isNonEncUnionTy ty = [dependee_arg, pf, fi]
| otherwise = [pf, fi]
dependee_arg =
case (getSwitchIsAttribute (idAttributes f_id)) of
Just e | notNull fs ->
let v = head fs in
funApply (refMarshallType stubMarshallInfo (toBaseTy (lookup_ty v)))
[addPtr (var "pf0") (lit (iLit (to_offset v)))]
where
fs = findFreeVars e
_ -> (lam [wildPat] (ret unit))
mshaller
| isDepender dep_list f_id &&
not (isSwitchDepender dep_list f_id) &&
not (isArrayTy ty)
= refMarshallType structMarshallInfo addrTy
| is_last && isArrayTy ty = marshallType structMarshallInfo{forInOut=True} ty
| isVariantTy ty = varName (prefix copyPrefix vARIANT)
| otherwise = refMarshallType structMarshallInfo ty
fi
| hasIgnoreAttribute f_id = varName nullPtr
| otherwise = var (mkHaskellVarName (idName f_id))
pf = mkFieldPtrName field_no
pf_prev = mkFieldPtrName (field_no 1)
mkFieldPtrName :: Int -> Haskell.Expr
mkFieldPtrName field_no = varName (prefix "pf" (mkVarName (show field_no)))
\end{code}
When unmarshalling a "struct" from its external representation
to its Haskell representation, @refUnMarshallField@ takes
care of generating code to unpack a given field.
\begin{code}
refUnmarshallField :: (Name -> Int)
-> (Name -> Type)
-> Int
-> Int
-> (Field, Bool)
-> (Haskell.Expr -> Haskell.Expr)
refUnmarshallField to_offset lookup_ty offset field_no (field, is_last) hole =
hLet pf (addPtr pf_prev (lit (iLit offset))) (binders hole)
where
f_id = fieldId field
ty = fieldType field
o_ty = removeNames (fieldOrigType field)
fi = var (mkHaskellVarName (idName f_id))
pf = mkFieldPtrName field_no
pf_prev = mkFieldPtrName (field_no 1)
args
| isNonEncUnionTy ty = [dependee_arg, pf]
| otherwise = [pf]
dependee_arg =
case (getSwitchIsAttribute (idAttributes f_id)) of
Just e | notNull fs ->
let v = head fs in
funApply (refUnmarshallType structMarshallInfo (toBaseTy (lookup_ty v)))
[addPtr (var "pf0") (lit (iLit (to_offset v)))]
where
fs = findFreeVars e
_ -> ret (lit (iLit ((1)::Int)))
binders
| hasIgnoreAttribute f_id = hLet fi (varName nullPtr)
| otherwise = bind (funApply un_marshaller args) fi
un_marshaller
| is_last && isArrayTy o_ty = unmarshallType structMarshallInfo{doFree=True} ty
| isVariantTy ty = unmarshallType structMarshallInfo{doFree=True} ty
| otherwise = refUnmarshallType structMarshallInfo ty
tagLast :: [a] -> [(a,Bool)]
tagLast [] = []
tagLast [x] = [(x,True)]
tagLast (x:xs) = (x,False) : tagLast xs
\end{code}