% % (c) The GRASP/AQUA Project, Glasgow University, 1993-2000 % \section{Generate Java} Name mangling for Java. ~~~~~~~~~~~~~~~~~~~~~~ Haskell has a number of namespaces. The Java translator uses the standard Haskell mangles (see OccName.lhs), and some extra mangles. All names are hidden inside packages. module name: - becomes a first level java package. - can not clash with java, because haskell modules are upper case, java default packages are lower case. function names: - these turn into classes - java keywords (eg. private) have the suffix "zdk" ($k) added. data *types* - These have a base class, so need to appear in the same name space as other object. for example data Foo = Foo - We add a postfix to types: "zdc" ($c) - Types are upper case, so never clash with keywords data constructors - There are tWO classes for each Constructor (1) - Class with the payload extends the relevent datatype baseclass. - This class has the prefix zdw ($w) (2) - Constructor *wrapper* just use their own name. - Constructors are upper case, so never clash with keywords - So Foo would become 2 classes. * Foo -- the constructor wrapper * zdwFoo -- the worker, with the payload $i for instances. $k for keyword nameclash avoidance. \begin{code} module JavaGen( javaGen ) where import Java import Literal ( Literal(..) ) import Id ( Id, isDataConWorkId_maybe, isId, idName, isDeadBinder, idPrimRep , isPrimOpId_maybe ) import Name ( NamedThing(..), getOccString, isExternalName, isInternalName , nameModule ) import PrimRep ( PrimRep(..) ) import DataCon ( DataCon, dataConRepArity, dataConRepArgTys, dataConWorkId ) import qualified Type import qualified CoreSyn import CoreSyn ( CoreBind, CoreExpr, CoreAlt, CoreBndr, Bind(..), AltCon(..), collectBinders, isValArg ) import TysWiredIn ( boolTy, trueDataCon, falseDataCon ) import qualified CoreUtils import Module ( Module, moduleString ) import TyCon ( TyCon, isDataTyCon, tyConDataCons ) import Outputable import Maybe import PrimOp import Util ( lengthIs, notNull ) #include "HsVersions.h" \end{code} \begin{code} javaGen :: Module -> [Module] -> [TyCon] -> [CoreBind] -> CompilationUnit javaGen mod import_mods tycons binds = liftCompilationUnit package where decls = [Import "haskell.runtime.*"] ++ [Import (moduleString mod) | mod <- import_mods] ++ concat (map javaTyCon (filter isDataTyCon tycons)) ++ concat (map javaTopBind binds) package = Package (moduleString mod) decls \end{code} %************************************************************************ %* * \subsection{Type declarations} %* * %************************************************************************ \begin{code} javaTyCon :: TyCon -> [Decl] -- public class List {} -- -- public class $wCons extends List { -- Object f1; Object f2 -- } -- public class $wNil extends List {} javaTyCon tycon = tycon_jclass : concat (map constr_class constrs) where constrs = tyConDataCons tycon tycon_jclass_jname = javaTyConTypeName tycon ++ "zdc" tycon_jclass = Class [Public] (shortName tycon_jclass_jname) [] [] [] constr_class data_con = [ Class [Public] constr_jname [tycon_jclass_jname] [] (field_decls ++ [cons_meth,debug_meth]) ] where constr_jname = shortName (javaConstrWkrName data_con) field_names = constrToFields data_con field_decls = [ Field [Public] n Nothing | n <- field_names ] cons_meth = mkCons constr_jname field_names debug_meth = Method [Public] (Name "toString" stringType) [] [] ( [ Declaration (Field [] txt Nothing) ] ++ [ ExprStatement (Assign (Var txt) (mkStr ("( " ++ getOccString data_con ++ " ") ) ) ] ++ [ ExprStatement (Assign (Var txt) (Op (Var txt) "+" (Op (Var n) "+" litSp) ) ) | n <- field_names ] ++ [ Return (Op (Var txt) "+" (mkStr ")") ) ] ) litSp = mkStr " " txt = Name "__txt" stringType -- This checks to see the type is reasonable to call new with. -- primitives might use a static method later. mkNew :: Type -> [Expr] -> Expr mkNew t@(PrimType primType) _ = error "new primitive -- fix it???" mkNew t@(Type _) es = New t es Nothing mkNew _ _ = error "new with strange arguments" constrToFields :: DataCon -> [Name] constrToFields cons = [ fieldName i t | (i,t) <- zip [1..] (map primRepToType (map Type.typePrimRep (dataConRepArgTys cons) ) ) ] mkCons :: TypeName -> [Name] -> Decl mkCons name args = Constructor [Public] name [ Parameter [] n | n <- args ] [ ExprStatement (Assign (Access this n) (Var n) ) | n <- args ] mkStr :: String -> Expr mkStr str = Literal (StringLit str) \end{code} %************************************************************************ %* * \subsection{Bindings} %* * %************************************************************************ \begin{code} javaTopBind :: CoreBind -> [Decl] javaTopBind (NonRec bndr rhs) = [java_top_bind bndr rhs] javaTopBind (Rec prs) = [java_top_bind bndr rhs | (bndr,rhs) <- prs] java_top_bind :: Id -> CoreExpr -> Decl -- public class f implements Code { -- public Object ENTER() { ...translation of rhs... } -- } java_top_bind bndr rhs = Class [Public] (shortName (javaIdTypeName bndr)) [] [codeName] [enter_meth] where enter_meth = Method [Public] enterName [vmArg] [excName] (javaExpr vmRETURN rhs) \end{code} %************************************************************************ %* * \subsection{Expressions} %* * %************************************************************************ \begin{code} javaVar :: Id -> Expr javaVar v | isExternalName (idName v) = mkNew (javaIdType v) [] | otherwise = Var (javaName v) javaLit :: Literal.Literal -> Expr javaLit (MachInt i) = Literal (IntLit (fromInteger i)) javaLit (MachChar c) = Literal (CharLit c) javaLit (MachStr fs) = Literal (StringLit str) where str = concatMap renderString (unpackFS fs) ++ "\\000" -- This should really handle all the chars 0..31. renderString '\NUL' = "\\000" renderString other = [other] javaLit other = pprPanic "javaLit" (ppr other) -- Pass in the 'shape' of the result. javaExpr :: (Expr -> Statement) -> CoreExpr -> [Statement] -- Generate code to apply the value of -- the expression to the arguments aleady on the stack javaExpr r (CoreSyn.Var v) = [r (javaVar v)] javaExpr r (CoreSyn.Lit l) = [r (javaLit l)] javaExpr r (CoreSyn.App f a) = javaApp r f [a] javaExpr r e@(CoreSyn.Lam _ _) = javaLam r (collectBinders e) javaExpr r (CoreSyn.Case e x alts) = javaCase r e x alts javaExpr r (CoreSyn.Let bind body) = javaBind bind ++ javaExpr r body javaExpr r (CoreSyn.Note _ e) = javaExpr r e javaCase :: (Expr -> Statement) -> CoreExpr -> Id -> [CoreAlt] -> [Statement] -- case e of x { Nil -> r1 -- Cons p q -> r2 } -- ==> -- final Object x = VM.WHNF(...code for e...) -- else if x instance_of Nil { -- ...translation of r1... -- } else if x instance_of Cons { -- final Object p = ((Cons) x).f1 -- final Object q = ((Cons) x).f2 -- ...translation of r2... -- } else throw java.lang.Exception -- This first special case happens a lot, typically -- during dictionary deconstruction. -- We need to access at least *one* field, to check to see -- if we have correct constructor. -- If we've got the wrong one, this is _|_, and the -- casting will catch this with an exception. javaCase r e x [(DataAlt d,bs,rhs)] | notNull bs = java_expr PushExpr e ++ [ var [Final] (javaName x) (whnf primRep (vmPOP (primRepToType primRep))) ] ++ bind_args d bs ++ javaExpr r rhs where primRep = idPrimRep x whnf PtrRep = vmWHNF -- needs evaluation whnf _ = id -- anything else does notg bind_args d bs = [var [Final] (javaName b) (Access (Cast (javaConstrWkrType d) (javaVar x) ) f ) | (b,f) <- filter isId bs `zip` (constrToFields d) , not (isDeadBinder b) ] javaCase r e x alts | isIfThenElse && isPrimCmp = javaIfThenElse r (fromJust maybePrim) tExpr fExpr | otherwise = java_expr PushExpr e ++ [ var [Final] (javaName x) (whnf primRep (vmPOP (primRepToType primRep))) , IfThenElse (map mk_alt con_alts) (Just default_code) ] where isIfThenElse = CoreUtils.exprType e `Type.eqType` boolTy -- also need to check that x is not free in -- any of the branches. maybePrim = findCmpPrim e [] isPrimCmp = isJust maybePrim (_,_,tExpr) = CoreUtils.findAlt (DataAlt trueDataCon) alts (_,_,fExpr) = CoreUtils.findAlt (DataAlt falseDataCon) alts primRep = idPrimRep x whnf PtrRep = vmWHNF -- needs evaluation whnf _ = id (con_alts, maybe_default) = CoreUtils.findDefault alts default_code = case maybe_default of Nothing -> ExprStatement (Raise excName [Literal (StringLit "case failure")]) Just rhs -> Block (javaExpr r rhs) mk_alt (DataAlt d, bs, rhs) = (instanceOf x d, Block (bind_args d bs ++ javaExpr r rhs)) mk_alt (LitAlt lit, bs, rhs) = (eqLit lit , Block (javaExpr r rhs)) eqLit (MachInt n) = Op (Literal (IntLit n)) "==" (Var (javaName x)) eqLit (MachChar n) = Op (Literal (CharLit n)) "==" (Var (javaName x)) eqLit other = pprPanic "eqLit" (ppr other) bind_args d bs = [var [Final] (javaName b) (Access (Cast (javaConstrWkrType d) (javaVar x) ) f ) | (b,f) <- filter isId bs `zip` (constrToFields d) , not (isDeadBinder b) ] javaIfThenElse r cmp tExpr fExpr {- - Now what we need to do is generate code for the if/then/else. - [all arguments are already check for simpleness (Var or Lit).] - - if ( arg1 arg2 arg3 ...) { - trueCode - } else { - falseCode - } -} = [IfThenElse [(cmp,j_tExpr)] (Just j_fExpr)] where j_tExpr, j_fExpr :: Statement j_tExpr = Block (javaExpr r tExpr) j_fExpr = Block (javaExpr r fExpr) javaBind (NonRec x rhs) {- x = ...rhs_x... ==> final Object x = new Thunk( new Code() { ...code for rhs_x... } ) -} = java_expr (SetVar name) rhs where name = case coreTypeToType rhs of ty@(PrimType _) -> javaName x `withType` ty _ -> javaName x `withType` codeType javaBind (Rec prs) {- rec { x = ...rhs_x...; y = ...rhs_y... } ==> class x implements Code { Code x, y; public Object ENTER() { ...code for rhs_x...} } ...ditto for y... final x x_inst = new x(); ...ditto for y... final Thunk x = new Thunk( x_inst ); ...ditto for y... x_inst.x = x; x_inst.y = y; ...ditto for y... -} = (map mk_class prs) ++ (map mk_inst prs) ++ (map mk_thunk prs) ++ concat (map mk_knot prs) where mk_class (b,r) = Declaration (Class [] class_name [] [codeName] stmts) where class_name = javaIdTypeName b stmts = [Field [] (javaName b `withType` codeType) Nothing | (b,_) <- prs] ++ [Method [Public] enterName [vmArg] [excName] (javaExpr vmRETURN r)] mk_inst (b,r) = var [Final] name (mkNew ty []) where name@(Name _ ty) = javaInstName b mk_thunk (b,r) = var [Final] (javaName b `withType` codeType) (mkNew thunkType [Var (javaInstName b)]) mk_knot (b,_) = [ ExprStatement (Assign lhs rhs) | (b',_) <- prs, let lhs = Access (Var (javaInstName b)) (javaName b'), let rhs = Var (javaName b') ] javaLam :: (Expr -> Statement) -> ([CoreBndr], CoreExpr) -> [Statement] javaLam r (bndrs, body) | null val_bndrs = javaExpr r body | otherwise = vmCOLLECT (length val_bndrs) this ++ [var [Final] n (vmPOP t) | n@(Name _ t) <- val_bndrs] ++ javaExpr r body where val_bndrs = map javaName (filter isId bndrs) javaApp :: (Expr -> Statement) -> CoreExpr -> [CoreExpr] -> [Statement] javaApp r (CoreSyn.App f a) as | isValArg a = javaApp r f (a:as) | otherwise = javaApp r f as javaApp r (CoreSyn.Var f) as = case isDataConWorkId_maybe f of { Just dc | as `lengthIs` dataConRepArity dc -- NOTE: Saturated constructors never returning a primitive at this point -- -- We push the arguments backwards, because we are using -- the (ugly) semantics of the order of evaluation of arguments, -- to avoid making up local names. Oh to have a namesupply... -- -> javaArgs (reverse as) ++ [r (New (javaIdType f) (javaPops as) Nothing ) ] | otherwise -> -- build a local let stmts = vmCOLLECT (dataConRepArity dc) this ++ [ vmRETURN (New (javaIdType f) [ vmPOP ty | (Name _ ty) <- constrToFields dc ] Nothing ) ] in javaArgs (reverse as) ++ [r (newCode stmts)] ; other -> java_apply r (CoreSyn.Var f) as } javaApp r f as = java_apply r f as -- This means, given a expression an a list of arguments, -- generate code for "pushing the arguments on the stack, -- and the executing the expression." java_apply :: (Expr -> Statement) -> CoreExpr -> [CoreExpr] -> [Statement] java_apply r f as = javaArgs as ++ javaExpr r f -- This generates statements that have the net effect -- of pushing values (perhaps thunks) onto the stack. javaArgs :: [CoreExpr] -> [Statement] javaArgs args = concat [ java_expr PushExpr a | a <- args, isValArg a] javaPops :: [CoreExpr] -> [Expr] javaPops args = [ vmPOP (primRepToType (Type.typePrimRep (CoreUtils.exprType a))) | a <- args , isValArg a ] -- The result is a list of statments that have the effect of -- pushing onto the stack (via one of the VM.PUSH* commands) -- the argument, (or returning, or setting a variable) -- perhaps thunked. {- This is mixing two things. (1) Optimizations for things like primitives, whnf calls, etc. (2) If something needs a thunk constructor round it. - Seperate them at some point! -} data ExprRetStyle = SetVar Name | PushExpr | ReturnExpr java_expr :: ExprRetStyle -> CoreExpr -> [Statement] java_expr _ (CoreSyn.Type t) = pprPanic "java_expr" (ppr t) java_expr ret e | isPrimCall = [push (fromJust maybePrim)] -- This is a shortcut, -- basic names and literals do not need a code block -- to compute the value. | isPrim primty && CoreUtils.exprIsTrivial e = javaExpr push e | isPrim primty = let expr = javaExpr vmRETURN e code = access (vmWHNF (newCode expr)) (primRepToType primty) in [push code] | otherwise = let expr = javaExpr vmRETURN e code = newCode expr code' = if CoreUtils.exprIsValue e || CoreUtils.exprIsTrivial e || isPrim primty then code else newThunk code in [push code'] where maybePrim = findFnPrim e [] isPrimCall = isJust maybePrim push e = case ret of SetVar name -> var [Final] name e PushExpr -> vmPUSH e ReturnExpr -> vmRETURN e corety = CoreUtils.exprType e primty = Type.typePrimRep corety isPrim PtrRep = False -- only this needs updated isPrim _ = True coreTypeToType = primRepToType . Type.typePrimRep . CoreUtils.exprType renameForKeywords :: (NamedThing name) => name -> String renameForKeywords name | str `elem` keywords = "zdk" ++ str | otherwise = str where str = getOccString name keywords :: [String] keywords = [ "return" , "if" , "then" , "else" , "class" , "instance" , "import" , "throw" , "try" ] \end{code} %************************************************************************ %* * \subsection{Helper functions} %* * %************************************************************************ \begin{code} true, this,javaNull :: Expr this = Var thisName true = Var (Name "true" (PrimType PrimBoolean)) javaNull = Var (Name "null" objectType) vmCOLLECT :: Int -> Expr -> [Statement] vmCOLLECT 0 e = [] vmCOLLECT n e = [ExprStatement (Call varVM collectName [ Literal (IntLit (toInteger n)) , e ] ) ] vmPOP :: Type -> Expr vmPOP ty = Call varVM (Name ("POP" ++ suffix ty) ty) [] vmPUSH :: Expr -> Statement vmPUSH e = ExprStatement (Call varVM (Name ("PUSH" ++ suffix (exprType e)) void) [e]) vmRETURN :: Expr -> Statement vmRETURN e = Return ( case ty of PrimType _ -> Call varVM (Name ("RETURN" ++ suffix ty) valueType ) [e] _ -> e) where ty = exprType e var :: [Modifier] -> Name -> Expr -> Statement var ms field_name@(Name _ ty) value | exprType value == ty = Declaration (Field ms field_name (Just value)) | otherwise = var ms field_name (Cast ty value) vmWHNF :: Expr -> Expr vmWHNF e = Call varVM whnfName [e] suffix :: Type -> String suffix (PrimType t) = primName t suffix _ = "" primName :: PrimType -> String primName PrimInt = "int" primName PrimChar = "char" primName PrimByte = "byte" primName PrimBoolean = "boolean" primName _ = error "unsupported primitive" varVM :: Expr varVM = Var vmName instanceOf :: Id -> DataCon -> Expr instanceOf x data_con = InstanceOf (Var (javaName x)) (javaConstrWkrType data_con) newCode :: [Statement] -> Expr newCode [Return e] = e newCode stmts = New codeType [] (Just [Method [Public] enterName [vmArg] [excName] stmts]) newThunk :: Expr -> Expr newThunk e = New thunkType [e] Nothing vmArg :: Parameter vmArg = Parameter [Final] vmName -- This is called with boolean compares, checking -- to see if we can do an obvious shortcut. -- If there is, we return a (GOO) expression for doing this, -- So if, we have case (#< x y) of { True -> e1; False -> e2 }, -- we will call findCmpFn with (#< x y), this return Just (Op x "<" y) findCmpPrim :: CoreExpr -> [Expr] -> Maybe Expr findCmpPrim (CoreSyn.App f a) as = case a of CoreSyn.Var v -> findCmpPrim f (javaVar v:as) CoreSyn.Lit l -> findCmpPrim f (javaLit l:as) _ -> Nothing findCmpPrim (CoreSyn.Var p) as = case isPrimOpId_maybe p of Just prim -> find_cmp_prim prim as Nothing -> Nothing findCmpPrim _ as = Nothing find_cmp_prim cmpPrim args@[a,b] = case cmpPrim of IntGtOp -> fn ">" IntGeOp -> fn ">=" IntEqOp -> fn "==" IntNeOp -> fn "/=" IntLtOp -> fn "<" IntLeOp -> fn "<=" _ -> Nothing where fn op = Just (Op a op b) find_cmp_prim _ _ = Nothing findFnPrim :: CoreExpr -> [Expr] -> Maybe Expr findFnPrim (CoreSyn.App f a) as = case a of CoreSyn.Var v -> findFnPrim f (javaVar v:as) CoreSyn.Lit l -> findFnPrim f (javaLit l:as) _ -> Nothing findFnPrim (CoreSyn.Var p) as = case isPrimOpId_maybe p of Just prim -> find_fn_prim prim as Nothing -> Nothing findFnPrim _ as = Nothing find_fn_prim cmpPrim args@[a,b] = case cmpPrim of IntAddOp -> fn "+" IntSubOp -> fn "-" IntMulOp -> fn "*" _ -> Nothing where fn op = Just (Op a op b) find_fn_prim _ _ = Nothing \end{code} %************************************************************************ %* * \subsection{Haskell to Java Types} %* * %************************************************************************ \begin{code} exprType (Var (Name _ t)) = t exprType (Literal lit) = litType lit exprType (Cast t _) = t exprType (New t _ _) = t exprType (Call _ (Name _ t) _) = t exprType (Access _ (Name _ t)) = t exprType (Raise t _) = error "do not know the type of raise!" exprType (Op _ op _) | op `elem` ["==","/=","<","<=","=>",">"] = PrimType PrimBoolean exprType (Op x op _) | op `elem` ["+","-","*"] = exprType x exprType expr = error ("can't figure out an expression type: " ++ show expr) litType (IntLit i) = PrimType PrimInt litType (CharLit i) = PrimType PrimChar litType (StringLit i) = stringType -- later, might use char array? \end{code} %************************************************************************ %* * \subsection{Name mangling} %* * %************************************************************************ \begin{code} codeName, excName, thunkName :: TypeName codeName = "haskell.runtime.Code" thunkName = "haskell.runtime.Thunk" excName = "java.lang.Exception" enterName, vmName,thisName,collectName, whnfName :: Name enterName = Name "ENTER" objectType vmName = Name "VM" vmType thisName = Name "this" (Type "") collectName = Name "COLLECT" void whnfName = Name "WHNF" objectType fieldName :: Int -> Type -> Name -- Names for fields of a constructor fieldName n ty = Name ("f" ++ show n) ty withType :: Name -> Type -> Name withType (Name n _) t = Name n t -- This maps (local only) names Ids to Names, -- using the same string as the Id. javaName :: Id -> Name javaName n | isExternalName (idName n) = error "useing javaName on global" | otherwise = Name (getOccString n) (primRepToType (idPrimRep n)) -- TypeName's are almost always global. This would typically return something -- like Test.foo or Test.Foozdc or PrelBase.foldr. -- Local might use locally bound types, (which do not have '.' in them). javaIdTypeName :: Id -> TypeName javaIdTypeName n | isInternalName n' = renameForKeywords n' | otherwise = moduleString (nameModule n') ++ "." ++ renameForKeywords n' where n' = getName n -- There is no such thing as a local type constructor. javaTyConTypeName :: TyCon -> TypeName javaTyConTypeName n = (moduleString (nameModule n') ++ "." ++ renameForKeywords n') where n' = getName n -- this is used for getting the name of a class when defining it. shortName :: TypeName -> TypeName shortName = reverse . takeWhile (/= '.') . reverse -- The function that makes the constructor name -- The constructor "Foo ..." in module Test, -- would return the name "Test.Foo". javaConstrWkrName :: DataCon -> TypeName javaConstrWkrName = javaIdTypeName . dataConWorkId -- Makes x_inst for Rec decls -- They are *never* is primitive -- and always have local (type) names. javaInstName :: Id -> Name javaInstName n = Name (renameForKeywords n ++ "zdi_inst") (Type (renameForKeywords n)) \end{code} %************************************************************************ %* * \subsection{Types and type mangling} %* * %************************************************************************ \begin{code} -- Haskell RTS types codeType, thunkType, valueType :: Type codeType = Type codeName thunkType = Type thunkName valueType = Type "haskell.runtime.Value" vmType = Type "haskell.runtime.VMEngine" -- Basic Java types objectType, stringType :: Type objectType = Type "java.lang.Object" stringType = Type "java.lang.String" void :: Type void = PrimType PrimVoid inttype :: Type inttype = PrimType PrimInt chartype :: Type chartype = PrimType PrimChar bytetype :: Type bytetype = PrimType PrimByte -- This lets you get inside a possible "Value" type, -- to access the internal unboxed object. access :: Expr -> Type -> Expr access expr (PrimType prim) = accessPrim (Cast valueType expr) prim access expr other = expr accessPrim expr PrimInt = Call expr (Name "intValue" inttype) [] accessPrim expr PrimChar = Call expr (Name "charValue" chartype) [] accessPrim expr PrimByte = Call expr (Name "byteValue" bytetype) [] accessPrim expr other = pprPanic "accessPrim" (text (show other)) -- This is where we map from typename to types, -- allowing to match possible primitive types. mkType :: TypeName -> Type mkType "PrelGHC.Intzh" = inttype mkType "PrelGHC.Charzh" = chartype mkType other = Type other -- Turns a (global) Id into a Type (fully qualified name). javaIdType :: Id -> Type javaIdType = mkType . javaIdTypeName javaLocalIdType :: Id -> Type javaLocalIdType = primRepToType . idPrimRep primRepToType ::PrimRep -> Type primRepToType PtrRep = objectType primRepToType IntRep = inttype primRepToType CharRep = chartype primRepToType Int8Rep = bytetype primRepToType AddrRep = objectType primRepToType other = pprPanic "primRepToType" (ppr other) -- The function that makes the constructor name javaConstrWkrType :: DataCon -> Type javaConstrWkrType con = Type (javaConstrWkrName con) \end{code} %************************************************************************ %* * \subsection{Class Lifting} %* * %************************************************************************ This is a very simple class lifter. It works by carrying inwards a list of bound variables (things that might need to be passed to a lifted inner class). * Any variable references is check with this list, and if it is bound, then it is not top level, external reference. * This means that for the purposes of lifting, it might be free inside a lifted inner class. * We remember these "free inside the inner class" values, and use this list (which is passed, via the monad, outwards) when lifting. \begin{code} type Bound = [Name] type Frees = [Name] combine :: [Name] -> [Name] -> [Name] combine [] names = names combine names [] = names combine (name:names) (name':names') | name < name' = name : combine names (name':names') | name > name' = name' : combine (name:names) names' | name == name = name : combine names names' | otherwise = error "names are not a total order" both :: [Name] -> [Name] -> [Name] both [] names = [] both names [] = [] both (name:names) (name':names') | name < name' = both names (name':names') | name > name' = both (name:names) names' | name == name = name : both names names' | otherwise = error "names are not a total order" combineEnv :: Env -> [Name] -> Env combineEnv (Env bound env) new = Env (bound `combine` new) env addTypeMapping :: TypeName -> TypeName -> [Name] -> Env -> Env addTypeMapping origName newName frees (Env bound env) = Env bound ((origName,(newName,frees)) : env) -- This a list of bound vars (with types) -- and a mapping from old class name -- to inner class name (with a list of frees that need passed -- to the inner class.) data Env = Env Bound [(TypeName,(TypeName,[Name]))] newtype LifterM a = LifterM { unLifterM :: TypeName -> -- this class name Int -> -- uniq supply ( a -- * , Frees -- frees , [Decl] -- lifted classes , Int -- The uniqs ) } instance Monad LifterM where return a = LifterM (\ n s -> (a,[],[],s)) (LifterM m) >>= fn = LifterM (\ n s -> case m n s of (a,frees,lifted,s) -> case unLifterM (fn a) n s of (a,frees2,lifted2,s) -> ( a , combine frees frees2 , lifted ++ lifted2 , s) ) liftAccess :: Env -> Name -> LifterM () liftAccess env@(Env bound _) name | name `elem` bound = LifterM (\ n s -> ((),[name],[],s)) | otherwise = return () scopedName :: TypeName -> LifterM a -> LifterM a scopedName name (LifterM m) = LifterM (\ _ s -> case m name 1 of (a,frees,lifted,_) -> (a,frees,lifted,s) ) genAnonInnerClassName :: LifterM TypeName genAnonInnerClassName = LifterM (\ n s -> ( n ++ "$" ++ show s , [] , [] , s + 1 ) ) genInnerClassName :: TypeName -> LifterM TypeName genInnerClassName name = LifterM (\ n s -> ( n ++ "$" ++ name , [] , [] , s ) ) getFrees :: LifterM a -> LifterM (a,Frees) getFrees (LifterM m) = LifterM (\ n s -> case m n s of (a,frees,lifted,n) -> ((a,frees),frees,lifted,n) ) rememberClass :: Decl -> LifterM () rememberClass decl = LifterM (\ n s -> ((),[],[decl],s)) liftCompilationUnit :: CompilationUnit -> CompilationUnit liftCompilationUnit (Package name ds) = Package name (concatMap liftCompilationUnit' ds) liftCompilationUnit' :: Decl -> [Decl] liftCompilationUnit' decl = case unLifterM (liftDecls True (Env [] []) [decl]) [] 1 of (ds,_,ds',_) -> ds ++ ds' -- The bound vars for the current class have -- already be captured before calling liftDecl, -- because they are in scope everywhere inside the class. liftDecl :: Bool -> Env -> Decl -> LifterM Decl liftDecl = \ top env decl -> case decl of { Import n -> return (Import n) ; Field mfs n e -> do { e <- liftMaybeExpr env e ; return (Field mfs (liftName env n) e) } ; Constructor mfs n as ss -> do { let newBound = getBoundAtParameters as ; (ss,_) <- liftStatements (combineEnv env newBound) ss ; return (Constructor mfs n (liftParameters env as) ss) } ; Method mfs n as ts ss -> do { let newBound = getBoundAtParameters as ; (ss,_) <- liftStatements (combineEnv env newBound) ss ; return (Method mfs (liftName env n) (liftParameters env as) ts ss) } ; Comment s -> return (Comment s) ; Interface mfs n is ms -> error "interfaces not supported" ; Class mfs n x is ms -> do { let newBound = getBoundAtDecls ms ; ms <- scopedName n (liftDecls False (combineEnv env newBound) ms) ; return (Class mfs n x is ms) } } liftDecls :: Bool -> Env -> [Decl] -> LifterM [Decl] liftDecls top env = mapM (liftDecl top env) getBoundAtDecls :: [Decl] -> Bound getBoundAtDecls = foldr combine [] . map getBoundAtDecl getBoundAtDecl :: Decl -> Bound getBoundAtDecl (Field _ n _) = [n] getBoundAtDecl _ = [] getBoundAtParameters :: [Parameter] -> Bound getBoundAtParameters = foldr combine [] . map getBoundAtParameter -- TODO getBoundAtParameter :: Parameter -> Bound getBoundAtParameter (Parameter _ n) = [n] liftStatement :: Env -> Statement -> LifterM (Statement,Env) liftStatement = \ env stmt -> case stmt of { Skip -> return (stmt,env) ; Return e -> do { e <- liftExpr env e ; return (Return e,env) } ; Block ss -> do { (ss,env) <- liftStatements env ss ; return (Block ss,env) } ; ExprStatement e -> do { e <- liftExpr env e ; return (ExprStatement e,env) } ; Declaration decl@(Field mfs n e) -> do { e <- liftMaybeExpr env e ; return ( Declaration (Field mfs (liftName env n) e) , env `combineEnv` getBoundAtDecl decl ) } ; Declaration decl@(Class mfs n x is ms) -> do { innerName <- genInnerClassName n ; frees <- liftClass env innerName ms x is ; return ( Declaration (Comment ["lifted " ++ n]) , addTypeMapping n innerName frees env ) } ; Declaration d -> error "general Decl not supported" ; IfThenElse ecs s -> ifthenelse env ecs s ; Switch e as d -> error "switch not supported" } ifthenelse :: Env -> [(Expr,Statement)] -> (Maybe Statement) -> LifterM (Statement,Env) ifthenelse env pairs may_stmt = do { let (exprs,stmts) = unzip pairs ; exprs <- liftExprs env exprs ; (stmts,_) <- liftStatements env stmts ; may_stmt <- case may_stmt of Just stmt -> do { (stmt,_) <- liftStatement env stmt ; return (Just stmt) } Nothing -> return Nothing ; return (IfThenElse (zip exprs stmts) may_stmt,env) } liftStatements :: Env -> [Statement] -> LifterM ([Statement],Env) liftStatements env [] = return ([],env) liftStatements env (s:ss) = do { (s,env) <- liftStatement env s ; (ss,env) <- liftStatements env ss ; return (s:ss,env) } liftExpr :: Env -> Expr -> LifterM Expr liftExpr = \ env expr -> case expr of { Var n -> do { liftAccess env n ; return (Var (liftName env n)) } ; Literal l -> return expr ; Cast t e -> do { e <- liftExpr env e ; return (Cast (liftType env t) e) } ; Access e n -> do { e <- liftExpr env e -- do not consider n as an access, because -- this is a indirection via a reference ; return (Access e n) } ; Assign l r -> do { l <- liftExpr env l ; r <- liftExpr env r ; return (Assign l r) } ; InstanceOf e t -> do { e <- liftExpr env e ; return (InstanceOf e (liftType env t)) } ; Raise n es -> do { es <- liftExprs env es ; return (Raise n es) } ; Call e n es -> do { e <- liftExpr env e ; es <- mapM (liftExpr env) es ; return (Call e n es) } ; Op e1 o e2 -> do { e1 <- liftExpr env e1 ; e2 <- liftExpr env e2 ; return (Op e1 o e2) } ; New n es ds -> new env n es ds } liftParameter env (Parameter ms n) = Parameter ms (liftName env n) liftParameters env = map (liftParameter env) liftName env (Name n t) = Name n (liftType env t) liftExprs :: Env -> [Expr] -> LifterM [Expr] liftExprs = mapM . liftExpr liftMaybeExpr :: Env -> (Maybe Expr) -> LifterM (Maybe Expr) liftMaybeExpr env Nothing = return Nothing liftMaybeExpr env (Just stmt) = do { stmt <- liftExpr env stmt ; return (Just stmt) } new :: Env -> Type -> [Expr] -> Maybe [Decl] -> LifterM Expr new env@(Env _ pairs) typ args Nothing = do { args <- liftExprs env args ; return (liftNew env typ args) } new env typ [] (Just inner) = -- anon. inner class do { innerName <- genAnonInnerClassName ; frees <- liftClass env innerName inner [] [unType typ] ; return (New (Type (innerName)) (map Var frees) Nothing) } where unType (Type name) = name unType _ = error "incorrect type style" new env typ _ (Just inner) = error "cant handle inner class with args" liftClass :: Env -> TypeName -> [Decl] -> [TypeName] -> [TypeName] -> LifterM [ Name ] liftClass env@(Env bound _) innerName inner xs is = do { let newBound = getBoundAtDecls inner ; (inner,frees) <- getFrees (liftDecls False (env `combineEnv` newBound) inner) ; let trueFrees = filter (\ (Name xs _) -> xs /= "VM") (both frees bound) ; let freeDefs = [ Field [Final] n Nothing | n <- trueFrees ] ; let cons = mkCons innerName trueFrees ; let innerClass = Class [] innerName xs is (freeDefs ++ [cons] ++ inner) ; rememberClass innerClass ; return trueFrees } liftType :: Env -> Type -> Type liftType (Env _ env) typ@(Type name) = case lookup name env of Nothing -> typ Just (nm,_) -> Type nm liftType _ typ = typ liftNew :: Env -> Type -> [Expr] -> Expr liftNew (Env _ env) typ@(Type name) exprs = case lookup name env of Nothing -> New typ exprs Nothing Just (nm,args) | null exprs -> New (Type nm) (map Var args) Nothing _ -> error "pre-lifted constructor with arguments" \end{code}