% % (c) 1999, sof % From an interface, generate a corresponding Java class/interface which allows you to override its methods with Haskell implementations of them. The emitted code currently uses the FunctionPointer invocation interface to call from Java into Haskell, but will eventually be extended to also emit native method declarations. \begin{code} module JavaProxy ( javaProxyGen, prepareDecls ) where import CoreIDL import CoreUtils ( isVoidTy ) import Attribute import BasicTypes import Literal import PP import PpCore ( showCore, ppType ) import Maybe ( mapMaybe ) \end{code} The generator is simple-minded - spit out a pretty printed template of the class/interface - no abstract Java syntax, no nothing. \begin{code} javaProxyGen :: Decl -> String javaProxyGen (Interface i False _ ds) | is_class = showPPDoc (emitHeader i <+> char '{' $$ vsep (map emitMethod ds) $$ emitConstructor i ds $$ text "}") () where is_class = idAttributes i `hasAttributeWithName` "jni_class" javaProxyGen _ = "" type Doc = PPDoc () \end{code} \begin{code} prepareDecls :: [Decl] -> [(String,Decl)] prepareDecls [] = [] prepareDecls (x:xs) = case x of Typedef{} -> prepareDecls xs Interface{declId=i} -> (idOrigName i ++ "Proxy", x) : prepareDecls xs Module{declDecls=ys} -> prepareDecls (ys ++ xs) Library{declDecls=ys} -> prepareDecls (ys ++ xs) _ -> prepareDecls xs \end{code} \begin{code} emitHeader :: Id -> Doc emitHeader i = text "public" <+> pp_kind <+> pp_name <+> pp_inherit <+> pp_implements where attrs = idAttributes i ifaces_implemented = mapMaybe toNm (filterAttributes attrs ["jni_interface"]) where toNm (Attribute _ [ParamLit (StringLit s)]) = Just s toNm _ = Nothing is_class = attrs `hasAttributeWithName` "jni_class" pp_name = text ((idOrigName i) ++ "Proxy") pp_kind | is_class = text "class" | otherwise = text "interface" pp_inherit = text "extends " <+> text (idOrigName i) pp_implements | null ifaces_implemented = empty | otherwise = text "implements" <+> hsep (punctuate comma (map text ifaces_implemented)) \end{code} \begin{code} emitMethod :: Decl -> Doc emitMethod (Method i _ res ps _) | is_ignorable = empty {- | is_field = text "public" <+> pp_static <+> emitType (resultType res) <+> text field_name <> semi -} | otherwise = -- a trusty old method text "public" <+> pp_static <+> emitType (resultType res) <+> text (idOrigName i) <+> ppTuple (zipWith emitParam ps [0..]) $$ char '{' $$ return_decl <+> castResult (resultType res) (fptr_call <> ppTuple (zipWith emitParamUse ps [0..])) <> semi $$ char '}' where attrs = idAttributes i fptr_call = text ("fptr_"++idOrigName i ++ ".call") return_decl | isVoidTy (resultType res) = empty | otherwise = text "return" {- field_name = case idOrigName i of 'g':'e':'t':'_':xs -> xs ls -> ls -} -- Java doesn't have a notion of read-only fields, -- so we simply ignore field setters and only -- generate the Java field decl when seeing the getter. -- -- ...Leave out fields for the moment, as we don't have a -- good way of mapping them to a Haskell impl. is_ignorable = attrs `hasAttributeWithNames` ["jni_set_field", "jni_get_field", "jni_ctor"] -- is_field = attrs `hasAttributeWithName` "jni_get_field" is_static = attrs `hasAttributeWithName` "jni_static" pp_static | is_static = text "static" | otherwise = empty emitMethod _ = empty \end{code} \begin{code} emitType :: Type -> Doc emitType ty = case ty of Integer Short _ -> text "short" Integer Long _ -> text "long" Integer LongLong _ -> text "long" Integer Natural _ -> text "int" Float Short -> text "float" Float Long -> text "double" Char _ -> text "char" Bool -> text "boolean" Octet -> text "byte" Object -> text "java.lang.Object" String{} -> text "java.lang.String" Name _ _ _ _ (Just t) _ -> emitType t Pointer _ _ t -> emitType t Array t [] -> emitType t <> text "[]" Void -> text "void" Iface _ _ o _ _ _ -> text o _ -> error ("emitType: unknown type " ++ showCore (ppType ty)) \end{code} \begin{code} emitParam :: Param -> Int -> Doc emitParam p idx = emitType (paramType p) <+> text ("arg"++show idx) \end{code} If the parameter is of an unboxed type, box it up before invoking @call@ \begin{code} emitParamUse :: Param -> Int -> Doc emitParamUse p idx = boxValue (paramType p) (text ("arg"++show idx)) \end{code} \begin{code} boxValue :: Type -> Doc -> Doc boxValue ty d = case ty of Integer _ _ -> text "new Integer" <> parens d Float Short -> text "new Float" <> parens d Float Long -> text "new Double" <> parens d Char _ -> text "new Character" <> parens d Bool -> text "new Boolean" <> parens d Octet -> text "new Byte" <> parens d Object -> d String{} -> d Name _ _ _ _ (Just t) _ -> boxValue t d Pointer _ _ t -> boxValue t d Array _ [] -> d Iface{} -> d _ -> error ("boxValue: unknown type " ++ showCore (ppType ty)) \end{code} \begin{code} castResult :: Type -> Doc -> Doc castResult t d = case t of Integer _ _ -> parens (text "Integer") <> d <> text ".value" Float Short -> parens (text "Float") <> d <> text ".floatValue" Float Long -> parens (text "Double") <> d <> text ".doubleValue" Char _ -> parens (text "Character") <> d <> text ".value" Bool -> parens (text "Character") <> d <> text ".booleanValue" Octet -> parens (text "Character") <> d <> text ".byteValue" Object -> d String{} -> parens (text "String") <> d Name _ _ _ _ (Just ty) _ -> castResult ty d Pointer _ _ ty -> castResult ty d Array ty [] -> parens (castResult ty (text "[]" <> d)) -- won't work. Iface _ _ o _ _ _ -> parens (text o) <> d Void -> d _ -> error ("castResult: unknown type " ++ showCore (ppType t)) \end{code} \begin{code} emitConstructor :: Id -> [Decl] -> Doc emitConstructor i ds = vsep (map mkMethodPtr ms) $$ text "public" <+> text (idOrigName i ++ "Proxy") <> ppTuple (zipWith (\ x _ -> text ("FunctionPtr arg" ++ show x)) idxs ms) $$ char '{' $$ vsep (zipWith assignFptr idxs ms) $$ char '}' where ms = filter isMethod ds idxs = [(0::Int)..] assignFptr idx m = functionPtrName m <+> equals <+> text ("arg"++show idx) <> semi functionPtrName d = text ("fptr_" ++ idOrigName (declId d)) -- should qualify FunctionPtr with its package name. mkMethodPtr d = text "private FunctionPtr" <+> functionPtrName d <> semi isMethod d = not (idAttributes (declId d) `hasAttributeWithNames` ["jni_set_field", "jni_get_field", "jni_ctor"]) \end{code}