% % (c) 1999, sof % % @(#) $Docid: Feb. 9th 2003 15:02 Sigbjorn Finne $ % @(#) $Contactid: sof@galois.com $ % Generating Haskell-side proxies for HJOs (Haskell implemented Java objects.) \begin{code}
module MarshallJServ ( cgJServMethod, cgJClass ) where

import CoreIDL
import Attribute
import qualified AbstractH as Haskell ( Type )
import CoreUtils
import PpCore ( showCore, ppType )

-- code/defs to generate Haskell code:

import AbstractH   ( HDecl )
import AbsHUtils

import MarshallUtils
import MarshallJNI

import CgMonad

-- utility libraries:
import BasicTypes
import Utils   ( snoc )
import LibUtils

\end{code} \begin{code}
cgJServMethod :: Id
	      -> Result
	      -> [Param]
	      -> CgM HDecl
cgJServMethod i res params = do
  cls_name <- getIfaceName
  return ( helpStringComment i `andDecl` marshallMethod i cls_name params res )
 
\end{code} The JNI library lets you export Haskell IO actions to Java by tupling up the arguments. For the automatically generated proxies we 'un-curry' the arguments before applying them to the Haskell method. \begin{code}
marshallMethod :: Id
	       -> String
	       -> [Param]
	       -> Result
	       -> HDecl
marshallMethod i cls_name params res 
  | is_ignorable = emptyDecl
  | otherwise    = ty_sig `andDecl` def
 where
  nm           = mkWrapperName (idName i)
  attrs	       = idAttributes i

  is_ignorable = hasAttributeWithNames 
  			attrs
  			[ "jni_get_field"
			, "jni_set_field"
			, "jni_ctor"
			]

  ty_sig      = genTypeSig nm ctxt
  			      (funTy method_ty        $
  			       funTy (tuple arg_tys') $
			       funTy thisTy (io res_ty'))
  def         = funDef nm pats rhs
  pats        = [patVar "meth", tuplePat arg_pats, patVar "this"]
  rhs         = funApp (mkVarName "meth") (arg_exprs `snoc` var "this")

  thisTy      = tyCon cls_name [tyVar "a"]

  (res_ty':arg_tys', ctxt) = generaliseTys (res_ty:arg_tys)

  method_ty   = foldr funTy (funTy thisTy (io res_ty')) arg_tys'

  res_ty      = toJNIType (resultOrigType res)

  arg_exprs   = map var arg_names
  arg_pats    = map patVar arg_names
  arg_names   = zipWith (\ _ x -> "arg" ++ show x) arg_tys [(0::Int)..]
  arg_tys     = map (toJNIType.paramType) params

\end{code} Converting an IDL type to a corresponding Haskell type is not too much work: \begin{code}
toJNIType :: Type -> Haskell.Type
toJNIType t = 
  case t of
    Integer sz signed  -> mkIntTy sz signed
    Float Short        -> tyFloat
    Float Long         -> tyDouble
    Float _	       -> error "toJNIType: unsupported Float size"
    Char _             -> tyWord16
    Bool               -> tyBool
    Octet              -> tyByte
    Object             -> mkTyCon jObject [tyVar "a"]
    String{}           -> tyString
    Name _ _ _ _ (Just ty) _ -> toJNIType ty
    Pointer _ _ ty     -> toJNIType ty
    Array ty []        -> mkTyCon jArray [toJNIType ty]
    Void                 -> tyUnit
    Iface nm imod _ attrs _ _
      | not (attrs `hasAttributeWithName` "jni_iface_ty") -> tyQCon imod nm [tyVar "a"]
      | otherwise  -> 
		    let 
                       i  = tyVar "a"
		    in
		    mkTyCon jObject
		       [ctxtTyApp (ctxtClass (mkQualName imod nm)
		       			     [mkTyCon jObject [i]])
			          i]

    _ -> error ("toJNIType: unknown type " ++ showCore (ppType t))

-- Convert a method's type into its Haskell equivalent.
toHaskellMethodTy :: Haskell.Type -> Decl -> Haskell.Type
toHaskellMethodTy obj_ty meth = funTys ps_tys (funTy obj_ty (io res_ty))
  where
    res_ty = toJNIType (resultType (methResult meth))
    ps_tys = map (toJNIType.paramType) (methParams meth)
\end{code} \begin{code}
cgJClass :: Id
	 -> [Decl]
	 -> CgM HDecl
cgJClass i ds 
 | is_interface = cgJNIInterface i False
 | otherwise = do
  addExport (ieValue ctor_nm)
  addExport (ieValue cls_cls_name)
  return (cls_nm_decl `andDecl` ty_sig `andDecl` decl)
  where
    is_interface = not ((idAttributes i) `hasAttributeWithName` "jni_class")

    ty_sig = mkTypeSig ctor_nm ctor_param_tys ctor_res_ty
    decl   = funDef ctor_nm ctor_params rhs

    cls_nm = idName i ++ "Proxy"
    i'     = i{idOrigName=cls_nm}

    (cls_nm_decl, cls_cls_name) = cgClassNameDecl i'

    ctor_nm = "new_" ++ idName i

     -- only interested in the 'real' methods here.
    ms   = filter (\ d -> isMethod d && not (isIgnorable d)) ds

    ctor_param_tys = map (toHaskellMethodTy obj_ty) ms
    ctor_params    = map (varPat.methArg) meth_idxs `snoc` varPat env

    ctor_res_ty = funTy (mkTyConst jniEnv) (io fptr_ty)

    obj_ty  = mkTyCon (mkQualName (idModule i) (idName i)) [tyVar "a"]
    fptr_ty = mkTyCon (mkQualName Nothing  (idName i)) [tyUnit]

    rhs  = foldr (\ f acc -> f acc) res xs

    res  = funApp newObj [ clsName, tySpec, tup x_ms, env ]
    xs   = zipWith exportMethod ms meth_idxs

    clsName = var cls_cls_name
    tySpec  = stringLit tySpec_lit
    env     = var "env"
    
    tySpec_lit = '(':ty_args ++ ")V"

    ty_args = concat $ map (\ _ -> functionPtrTySpec) ms

    functionPtrTySpec = "LFunctionPtr;"

    x_ms = map xMethArg meth_idxs

    meth_idxs  = zipWith const [(1::Int)..] ms

    exportMethod d mi =
      bind (funApp newFPointer [funApp (mkWrapName d) [methArg mi], env])
           (xMethArg mi)
	

    xMethArg mi = var ("xm_" ++ show mi)
    methArg  mi = var ("meth_" ++ show mi)

    mkWrapName d = mkVarName (mkWrapperName (idName (declId d)))

    isIgnorable d = 
     hasAttributeWithNames (idAttributes (declId d))
     			   ["jni_set_field", "jni_get_field", "jni_ctor"]

\end{code}