{-# LANGUAGE FlexibleContexts, ConstraintKinds, TypeFamilies, MultiParamTypeClasses, FlexibleInstances #-} module AOP.Internal.PointcutLanguage ( pcCall, pcType, pcAnd, pcTag, pcOr, pcNot, ) where import GHC.Prim (Constraint) import AOP.Internal.JoinpointModel import Debug.Trace {- | Built-in pointcuts pcCall and pcType, and pointcut combinators pcAnd, pcOr and pcNot. Using typeclasses, pointcuts are open for new definitions of functions, like the Function wrapper. We also define pcSeq, that matches a sequence of two join points. -} pcCall :: (Typeable1Monad m, PolyTypeable (a -> b)) => (a -> b) -> PC m a b pcCall f = let typRefF = polyTypeOf f in PC (pcCallPred f typRefF defaultFunctionTag) where pcCallPred fun t tag = return $ \ jp -> return (compareFun fun tag jp && compareType t jp) pcTag :: (Typeable1Monad m, PolyTypeable (a -> b)) => FunctionTag -> PC m a' b' pcTag t = PC (pcTagPred t) where pcTagPred t = return $ \ (Jp _ tag _ _) -> return (tag == t) pcType :: (Typeable1Monad m, PolyTypeable (a -> b)) => (a -> b) -> PC m a b pcType f = let typRefF = polyTypeOf f in PC (pcTypePred typRefF) where pcTypePred t = (return (\jp -> return (compareType t jp))) -- | And pointcut combinator, overloaded to support PC and RequirePC class Typeable1Monad m => PCAnd m a1 b1 a2 b2 pct where type PCAndCtx m a1 b1 a2 b2 pct :: Constraint pcAnd :: PCAndCtx m a1 b1 a2 b2 pct => PC m a1 b1 -> pct m a2 b2 -> PC m a1 b1 -- | When combining two PC pointcuts, the matched types t1 and t2 must be the same -- | This expressed in the constraint t1 ~ t2 instance Typeable1Monad m => PCAnd m a1 b1 a2 b2 PC where type PCAndCtx m a1 b1 a2 b2 PC = ((a1 -> b1) ~ (a2 -> b2)) pcAnd (PC mpc1) (PC mpc2) = PC (_pcAndImpl mpc1 mpc2) -- | When combining a PC with a RequirePC we constraint t1 to be LessGen than t2 instance Typeable1Monad m => PCAnd m a1 b1 a2 b2 RequirePC where type PCAndCtx m a1 b1 a2 b2 RequirePC = (LessGen (a1 -> b1) (a2 -> b2)) pcAnd (PC mpc1) (RequirePC mpc2) = PC (_pcAndImpl mpc1 mpc2) -- | Logical Or pointcut combinator. pcOr :: (Typeable1Monad m, LeastGen (a1 -> b1) (a2 -> b2) (agen -> bgen)) => PC m a1 b1 -> PC m a2 b2 -> PC m agen bgen pcOr (PC mpc1) (PC mpc2) = PC (_pcOrImpl mpc1 mpc2) -- | Logical Not pointcut combinator. pcNot :: Typeable1Monad m => PC m a1 b1 -> PC m a2 b2 pcNot (PC mpc) = PC (_pcNotImpl mpc) -- Implementations _pcAndImpl mpc1 mpc2 = do pc1 <- mpc1 pc2 <- mpc2 return $ \ jp -> do res1 <- pc1 jp if res1 then do res2 <- pc2 jp return res2 else return False _pcOrImpl mpc1 mpc2 = do pc1 <- mpc1 pc2 <- mpc2 return $ \ jp -> do res1 <- pc1 jp if res1 then return True else do res2 <- pc2 jp return res2 _pcNotImpl mpc1 = do pc1 <- mpc1 return $ \ jp -> do res1 <- pc1 jp return (not res1)