% % (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) ] {- To be able to marshal stuff like typedef struct { uint64 w ; } ULARGE_INTEGER; by value, we need to check if this is possible. -} 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 {- Find out which struct members depend on the value of others. -} dep_list = findFieldDependents fields fields_w = mapMaybe (adjustField True dep_list) fields fields_r = mapMaybe (adjustField False dep_list) fields -- *** Reference marshalling *** 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{-inside struct-} False{- for a (client) stub-} dep_list (findFieldOrigTy fields) -- here's a hack for you: when writing the contents of a field such -- [size_is(x)]int y[]; -- -- we cannot do the normal trick of first marshalling the Haskell list -- representing 'y' into a chunk of mem. followed by filling in the struct -- with a pointer to it, because the assumed layout is for y[] to be inlined -- into the struct. -- -- The solution is to remove such fields from the list that's presented to -- the 'dependent-arg' marshalling code. -- -- *** Reference unmarshalling *** 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) -- shouldn't really generate the next two.. 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 -- we know it is already in a marshaled form! 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 -- we know it is already in an unmarshaled form! s_name = qName (prefix sizeofPrefix name) s_tysig = typeSig s_name tyWord32 s_def = funDef s_name [] s_rhs -- Not right, struct alignment/pad not taking into consid. s_rhs | null fields = var "0" | otherwise = var (show sz) rel_offsets = diff offsets ((sz, _), offsets) = computeStructSizeOffsets mb_pack fields -- Freeing a struct. 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) = {- unpack the tag of a union, will be needed later when the union is being freed up. -} let v = idName (fieldId f) in bind (funApply (refUnmarshallType structMarshallInfo (toBaseTy (fieldType f))) [addPtr (var "ptr") (lit (iLit offset))]) (var v) (ret unit) -- sigh, shouldn't be forced to do this. 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 {- The [switch_is(..)] attribute for a field is pinned on the field Id and not the type, so we have to figure the expression which computes the dependee argument here rather than in MarshallType.refMarshallType. -} 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 {- If we've got a VARIANT, copy the struct in. ToDo: add special support for this in TypeInfos. -} | 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 -- -- A special case is VARIANT types, which are represented -- by a pointer to the external VARIANT struct. Hence, we don't -- reference-unmarshal these, but value-unmarshal. Similarly -- with arrays embedded inside structs; marshall them by value. | 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}