module CallGraph type CallGraph = (string * string list) list let callsOf (caller : string) (graph : CallGraph) = match List.tryFind (fun (x,_) -> x = caller) graph with | None -> [] | Some (_, calls) -> calls let calls (caller : string) (callee : string) (graph : CallGraph) = List.exists (fun x -> x=callee) (callsOf caller graph) open AbSyn (* Remove duplicate elements in a list. Quite slow - O(n^2) - but our lists here will be small. *) let rec nub = function | [] -> [] | x::xs -> if List.exists (fun y -> y = x) xs then nub xs else x :: nub xs let rec expCalls = function | Constant _ -> [] | StringLit _ -> [] | ArrayLit (es, _, _) -> List.concat (List.map expCalls es) | Var _ -> [] | Plus (e1, e2, _) -> expCalls e1 @ expCalls e2 | Minus (e1, e2, _) -> expCalls e1 @ expCalls e2 | Equal (e1, e2, _) -> expCalls e1 @ expCalls e2 | Less (e1, e2, _) -> expCalls e1 @ expCalls e2 | If (e1, e2, e3, _) -> expCalls e1 @ expCalls e2 @ expCalls e3 | Apply (fname, es, _) -> fname :: List.concat (List.map expCalls es) | Let ( Dec(_, e, _), body, _) -> expCalls e @ expCalls body | Index (_, e, _, _) -> expCalls e | Iota (e, _) -> expCalls e | Map (farg, e, _, _, _) -> fargCalls farg @ expCalls e | Filter (farg, e, _, _) -> fargCalls farg @ expCalls e | Reduce (farg, e1, e2, _, _) -> fargCalls farg @ expCalls e1 @ expCalls e2 | Replicate (n, e, _, _) -> expCalls n @ expCalls e | Scan (farg, e1, e2, _, _) -> fargCalls farg @ expCalls e1 @ expCalls e2 | Times (e1, e2, _) -> expCalls e1 @ expCalls e2 | Divide (e1, e2, _) -> expCalls e1 @ expCalls e2 | And (e1, e2, _) -> expCalls e1 @ expCalls e2 | Or (e1, e2, _) -> expCalls e1 @ expCalls e2 | Not (e, _) -> expCalls e | Negate (e, _) -> expCalls e | Read _ -> [] | Write (e, _, _) -> expCalls e and fargCalls = function | Lambda (_, _, body, _) -> expCalls body | FunName s -> [s] (* Get the direct function calls of a single function *) let functionCalls = function | FunDec (fname, _, _, body, _) -> (fname, nub (expCalls body)) (* Expand the direct function call graph to its transitive closure. *) let rec transitiveClosure (graph : CallGraph) = let grow ((caller : string), (callees : string list)) = let calleecalls = List.concat (List.map (fun callee -> callsOf callee graph) callees) let newCalls = (List.filter (fun call -> not (List.exists (fun x -> x = call) callees) ) calleecalls) if List.isEmpty newCalls then ((caller, callees), false) else ((caller, callees @ nub newCalls), true) let (graph', changes) = List.unzip (List.map grow graph) let changed = List.exists (fun x -> x) changes if changed then transitiveClosure graph' else graph' let callGraph (prog : TypedProg) = transitiveClosure (List.map functionCalls prog)