File: src/transfer.ml (return to index)



Statistics:  
kind coverage
binding 58 / 58 (100%)
sequence 0 / 0 (-%)
for 0 / 0 (-%)
if/then 44 / 45 (97%)
try 1 / 1 (100%)
while 0 / 0 (-%)
match/function 187 / 197 (94%)
kind coverage
class expression 0 / 0 (-%)
class initializer 0 / 0 (-%)
class method 0 / 0 (-%)
class value 0 / 0 (-%)
toplevel expression 0 / 0 (-%)
lazy operator 15 / 15 (100%)



Source:

fold all unfold all
000001| (** Transfer functions over the analysis lattice *)
000002|  
000003| (** Following classical compositional abstract interpretation,
000004|     the analysis is implemented as a structural decent over the AST.
000005|  
000006|     This avoids any risk of divergence, as a monotone analyse will take
000007|     at most |lattice height| traversals over the AST before converging.
000008|  *)
000009|  
000010| module L   = Last
000011| module Abs = Absencelattice
000012| module Str = Stringlattice
000013| module VL  = Valuelattice
000014| module EL  = Envlattice
000015| module PL  = Proplattice
000016| module ST  = Storelattice
000017| module SL  = Statelattice
000018| module AL  = Analysislattice
000019|  
000020| (* a record type of commonly passed arguments *)
000021| type info = { fun_map        : L.label -> L.lit;
000022|               break_labels   : L.label * L.label;
000023|               ret_label      : L.label }
000024|  
000025| (** {3 A helper function} *)
000026|  
000027| (*  join_rlat_lists : VL list -> VL list -> VL list *)
000028| let rec join_rlat_lists vs ws = match vs,ws with
000029|   | [], []      -> (*[51]*)[]
000030|   | [], ws      -> (*[1057]*)ws
000031|   | vs, []      -> (*[0]*)vs
000032|   | v::vs,w::ws -> (*[30]*)(VL.join v w)::(join_rlat_lists vs ws)
000033|  
000034| (** {2 Monad } *)
000035|  
000036| (** The following 'return' and 'bind' implements a (syntactic) monad
000037|     which is handy to avoid a lot of let bindings. *)
000038|  
000039| let return vlat = fun (alat,slat) -> ((*[14796]*)alat,slat,vlat)
000040| let red_return vlat = 
000041|   fun (alat,slat) -> (*[7490]*)if (*[7490]*)VL.is_bot vlat || (*[6789]*)SL.is_bot slat ||
000042|                         (*[6417]*)ST.is_bot slat.SL.store || (*[6417]*)EL.is_bot slat.SL.env
000043|                      then ((*[1073]*)alat,SL.bot,VL.bot) else ((*[6417]*)alat,slat,vlat)
000044| let list_return vlat = fun (alat,slat) -> ((*[8891]*)alat,slat,[vlat])
000045| let red_list_return vlats = 
000046|   fun (alat,slat) -> (*[1078]*)if (*[1078]*)vlats = [] || (*[1057]*)vlats = [VL.bot] ||
000047|                        (*[940]*)SL.is_bot slat || (*[940]*)ST.is_bot slat.SL.store || (*[938]*)EL.is_bot slat.SL.env
000048|                      then ((*[140]*)alat,SL.bot,[VL.bot]) else ((*[938]*)alat,slat,vlats)
000049|  
000050| let bind v f = fun (alat,slat) -> match v (alat,slat) with
000051|   | (alat',slat',vlat) -> (*[60110]*)f vlat (alat',slat')
000052| let ( >>= ) = (*[1]*)bind
000053|  
000054|  
000055| (** {3 Monadic operations } *)
000056|  
000057| let adjust_to_single vlats = match vlats with
000058|                                | []      -> (*[0]*)return VL.nil
000059|                                | vlat::_ -> (*[5719]*)return vlat
000060|  
000061| let merror     = fun (alat,slat) -> ((*[2273]*)alat,SL.bot,VL.bot)
000062|  
000063| let curr_scope_chain        = fun (alat,slat) -> ((*[681]*)alat,slat,slat.SL.env)
000064| let restore_scope_chain env = fun (alat,slat) -> ((*[298]*)alat,{slat with SL.env = env},())
000065|  
000066| let get_curr_state     = fun (alat,slat) -> ((*[9175]*)alat,slat,slat)
000067| let restore_state slat = fun (alat,_)    -> ((*[6907]*)alat,slat,())
000068|  
000069| let lookup_state label = fun (alat,slat) -> ((*[3880]*)alat,slat,AL.lookup alat label)
000070| let set_state label    = ((*[3827]*)lookup_state label >>= fun slat -> (*[3827]*)restore_state slat)
000071| let record_state label =
000072|   fun (alat,slat) -> ((*[3947]*)AL.add alat label (SL.join slat (AL.lookup alat label)), slat, ())
000073|  
000074| let mfailwith msg   = fun (_alat,_slat) -> (*[0]*)failwith msg
000075|  
000076| let enter_scope label   = fun (alat,slat) -> ((*[778]*)alat, SL.enter_scope slat label, ()) 
000077| let add_locals xs vlats = fun (alat,slat) -> ((*[778]*)alat, SL.add_local_list slat vlats xs, ()) 
000078|  
000079|  
000080| (** {4 lvalue read/write operations } *)
000081|  
000082| let read_name x             = (*[4183]*)get_curr_state >>= fun slat -> (*[4240]*)red_return (SL.read_name slat x)
000083| let lookup_event tabv strev = (*[975]*)get_curr_state >>= fun slat -> (*[975]*)return (ST.lookup_event slat.SL.store tabv strev)
000084| let raw_get tabv idxv       = (*[975]*)get_curr_state >>= fun slat -> (*[975]*)return (ST.raw_get slat.SL.store tabv idxv)
000085|  
000086| let write_name n vlat = (*[822]*)get_curr_state >>= fun slat ->
000087|                         (*[822]*)let slat' = SL.write_name slat n vlat in
000088|                          (*[822]*)restore_state slat'
000089|  
000090| let write_dyn_prop vlat0 vlat1 vlat = (*[349]*)get_curr_state >>= fun slat ->
000091|                                       (*[349]*)let slat' = SL.write_dyn_prop slat vlat0 vlat1 vlat in
000092|                                        (*[349]*)restore_state slat'
000093|  
000094| let alloc_closure l prop_chain =
000095|   fun (alat,slat) -> ((*[394]*)alat,{slat with SL.store = ST.add_label slat.SL.store l prop_chain},())
000096|  
000097| let alloc_resstore ret_label vlats = (* Hack: write 'ret_label.ret_label.res1' in heap *)
000098|   fun (alat,slat) ->
000099|     (*[461]*)if SL.is_bot slat then ((*[226]*)alat,SL.bot,ST.bot)
000100|     else
000101|       (*[235]*)let vlats = match vlats with
000102|                    | [] -> [(*[2]*)VL.nil] (* if return is empty, return nil value *)
000103|                    | _  -> (*[233]*)vlats in
000104|       (*[235]*)let _,res_table = List.fold_left
000105|         (fun (i,tbl) vlat -> (*[263]*)let entry = "res" ^ (string_of_int i) in
000106|                              ((*[263]*)i+1,PL.add entry vlat tbl)) (1,PL.mt) vlats in
000107|       ((*[235]*)alat,slat,ST.add_label slat.SL.store ret_label res_table)
000108|  
000109| let getbinhandler op1 op2 event =
000110|   fun (alat,slat) -> ((*[299]*)alat,slat,SL.getbinhandler slat op1 op2 event)
000111|  
000112| (** {4 control flow operations } *)
000113|  
000114| let and_join slat0 vlat0 slat1 vlat1 =
000115|   (fun (alat,slat) -> (* potentially false if overapprox. has nil *)
000116|      (*[40]*)let slat,vlat   = if VL.may_be_nil vlat0 then ((*[2]*)slat0,vlat0) else ((*[38]*)SL.bot,VL.bot) in
000117|      (*[40]*)let slat',vlat' = if VL.may_be_non_nil vlat0 then ((*[38]*)slat1,vlat1) else ((*[2]*)SL.bot,VL.bot) in
000118|      ((*[40]*)alat, SL.join slat slat', VL.join vlat vlat')) (* potentially true if overapprox. has non nil *)
000119|      (* potentially reduce *)
000120|  
000121| let or_join slat0 vlat0 slat1 vlat1 =
000122|   (fun (alat,slat) -> (* potentially true if overapprox. has non nil *)
000123|      (*[14]*)let slat,vlat   = if VL.may_be_non_nil vlat0 then ((*[12]*)slat0,VL.exclude_nil vlat0) else ((*[2]*)SL.bot,VL.bot) in
000124|      (*[14]*)let slat',vlat' = if VL.may_be_nil vlat0 then ((*[2]*)slat1,vlat1) else ((*[12]*)SL.bot,VL.bot) in
000125|      ((*[14]*)alat, SL.join slat slat', VL.join vlat vlat')) (* potentially false if overapprox. has nil *)
000126|      (* potentially reduce *)
000127|  
000128| let mvl_join branch1 branch2 = (fun (alat,slat) ->
000129|   (*[2249]*)let (alat1,slat1,vlat1) = branch1 (alat,slat) in
000130|   (*[2249]*)let (alat2,slat2,vlat2) = branch2 (alat,slat) in
000131|   ((*[2249]*)AL.join alat1 alat2, SL.join slat1 slat2, VL.join vlat1 vlat2))
000132|  
000133| let branch_and_join env thenbr elsebr =
000134|   (fun lat -> (*[145]*)let (alat1,slat1,()),(alat2,slat2,()) = thenbr lat, elsebr lat in
000135|               (*[145]*)let joined_store                      = ST.join slat1.SL.store slat2.SL.store in
000136|               ((*[145]*)AL.join alat1 alat2, (if ST.leq joined_store ST.bot then (*[47]*)SL.bot
000137|                                      else (*[98]*){SL.env = env; SL.store = joined_store}), ()))
000138|  
000139| let rec loop test body =
000140|   (fun (alat_prev,slat_prev) ->
000141|     (*[207]*)let (alat',slat',_vlat') = test (alat_prev,slat_prev) in
000142|     (*[207]*)let (alat'',slat'',())   = body (alat',slat') in
000143|     (*[207]*)if (*[207]*)AL.leq alat'' alat' && (*[129]*)ST.leq slat''.SL.store slat'.SL.store (* Fixpoint upto scoping of local variables *)
000144|     then ((*[104]*)alat',slat',())
000145|     else (*[103]*)loop (* Otherwise:iterate *)
000146|       test body ((*AL.join alat'*) alat'', {slat'' with SL.store = ST.join slat'.SL.store slat''.SL.store}))
000147|  
000148|  
000149| (** {3 Transfer functions } *)
000150|  
000151| (*  transfer_call : label -> label -> VL list -> info -> (AL * SL) -> (AL * SL * VL list) *)
000152| let transfer_call clab tgtlabel vlats info (alat,slat) =
000153|   (*[470]*)let f = info.fun_map tgtlabel in
000154|   match f with
000155|     | L.Fun (label',xs,bl,ret_label) ->
000156|       (*[470]*)if (tgtlabel != label')
000157|       then (*[0]*)failwith ("Mismatch in fun_map between labels "
000158|                        ^ (string_of_int tgtlabel) ^ " and " ^ (string_of_int label'))
000159|       else (match bl with
000160|         | None    -> (*[0]*)failwith "encountered bodyless function"
000161|         | Some bl ->
000162|           (*[470]*)let body_label   = bl.L.label in
000163|           (*[470]*)let entry_state  = AL.lookup alat body_label in
000164| (*
000165|  Caller invariant:    - pass arguments and clabel
000166|                       - receive results through ret_label entry
000167|  
000168|  Receiver invariant:  - pair formals and actuals and store in clabel,
000169|                       - add an extended scope chain,
000170|                       - store result(s) in AL[ret_label][ret_label][res1]
000171|  
000172|  Needs two allocation sites
000173|   - caller-entry - for scope-chain allocation
000174|   - caller-exit  - for ?
000175|  
000176|  propagate call:    - vlats into localenv of function label
000177|                     - slat.store into store of function label
000178|  propagate return:  - vlat results from return label to caller
000179|                     - slat.store from return label into store of caller
000180|  *)
000181| (* caller: clab    callee: tgtlabel body_label ret_label *)
000182|  
000183|           (* extend stored env with new level *)
000184|           (*[470]*)let ext_env      = ST.fold_labels_scopechain
000185|                                (fun is accpairset -> (*[468]*)EL.PairSet.add (clab,is) accpairset)
000186|                                    slat.SL.store tgtlabel EL.PairSet.empty in
000187|           (*[470]*)if EL.is_bot ext_env (* scopechain potentially deleted because of domain reduction *)
000188|           then ((*[2]*)alat,slat,[VL.bot])
000189|           else
000190|           (* add bindings from formals to actuals *)
000191|           (* join store and environment into receiver's state *)
000192|           (*[468]*)let entry_state' = SL.join entry_state
000193|                                { SL.store   =
000194|                                    ST.add_label slat.SL.store clab (PL.add_all_params xs vlats PL.mt);
000195|                                  SL.env     = ext_env } in
000196|           (*[468]*)let alat'        = AL.add alat body_label entry_state' in
000197|  
000198|           (* lookup exit state and results *)
000199|           (*[468]*)let exit_state   = AL.lookup alat' ret_label in
000200|           (* if body reachable but exit is bot,
000201|                either func completes abnormally
000202|                or: flow has not propagated through function yet *)
000203|           (*[468]*)if (*[468]*)not (SL.is_bot entry_state) && (*[383]*)SL.is_bot exit_state 
000204|           then ((*[34]*)alat',SL.bot,[VL.bot]) (* unreachable: no return state injected *)
000205|           else
000206|           (*[434]*)let exit_prop    = ST.find_label exit_state.SL.store ret_label in
000207|  
000208|           (*[434]*)let rec build_res_lat i =
000209|             ((*[817]*)try
000210|                (*[817]*)let entry    = "res" ^ (string_of_int i) in
000211|                (*[817]*)let vlat,abs = PL.find_exn entry exit_prop in
000212|                (*[383]*)let vlats    = build_res_lat (i+1) in
000213|                (*[383]*)if abs = Abs.is_present (* result arity may vary: if so, include nil *)
000214|                then (*[275]*)vlat::vlats
000215|                else (*[108]*)(VL.join vlat VL.nil)::vlats
000216|              with Not_found ->
000217|                (*[434]*)if i=1
000218|                then [(*[85]*)VL.bot] (* no result yet: head is bot *)
000219|                else (*[349]*)[]) in  (* Hack: read 'result' local *)
000220|  
000221|           (*[434]*)let res_lats      = build_res_lat 1 in
000222|           (* Note: L.Return is responsible for propagating to ret_label
000223|                    Here we only read off the propagated input  *)
000224|  
000225|           (* return store/heap effects to caller's state (joined by transfer_calls) *)
000226|           (* and restore environment*)
000227|           (*[434]*)let slat'        = (*SL.join slat*)
000228|                              { SL.store   = exit_state.SL.store;
000229|                                SL.env     = slat.SL.env (* EL.bot;*) } in
000230|           ((*[434]*)alat',slat',res_lats)
000231|       )
000232|     | _ ->
000233|       (*[0]*)failwith "Got non-function literal from function map"
000234|  
000235| (*  transfer_calls : label -> VL -> VL list -> info -> (AL * SL) -> (AL * SL * VL list) *)
000236| let transfer_calls clab vlat vlats info =
000237|   (*[1281]*)if not (VL.may_be_proc vlat)  (* type error: receiver is definitely not a function *)
000238|   then (*[203]*)restore_state SL.bot >>= fun () -> (*[203]*)list_return VL.bot
000239|   else
000240|     (*[1078]*)(fun (alat,slat) ->
000241|       (*[1078]*)let funs = vlat.VL.funs in
000242|         (*[1078]*)VL.ProcSet.fold 
000243|           (fun proc (alatacc,slatacc,rlatacc) -> match proc with
000244|             | VL.Funtag f   -> 
000245|               (*[470]*)let (alatacc',slat',rlats) = transfer_call clab f vlats info (alatacc,slat) in
000246|               ((*[470]*)alatacc',SL.join slatacc slat',join_rlat_lists rlatacc rlats)
000247|             | VL.Builtin bi -> 
000248|               (*[638]*)let slat',rlats = SL.apply_builtin bi slat vlats in
000249|               ((*[638]*)alatacc,SL.join slatacc slat',join_rlat_lists rlatacc rlats))
000250|           funs (alat, { SL.store = ST.bot;
000251|                         SL.env   = EL.bot } ,[]))
000252|      >>= red_list_return (* slight hack to reduce to bot on callee error *)
000253|  
000254| (*  transfer_str : str -> info -> (AL * SL) -> (AL * SL * VL) *)
000255| let rec transfer_str s info =
000256|   match s with
000257|     | Ast.Normal ns -> (*[856]*)red_return (VL.string ns)
000258|     | Ast.Char cs   -> (*[161]*)red_return (VL.string cs)
000259|     | Ast.Long ls   -> (*[10]*)red_return (VL.string ls)
000260|  
000261| (*  transfer_lit : lit -> info -> (AL * SL) -> (AL * SL * VL) *)
000262| let rec transfer_lit l info =
000263|   match l with
000264|   | L.Nil              -> (*[303]*)red_return VL.nil
000265|   | L.Bool _           -> (*[127]*)red_return VL.bool
000266|   | L.String s         -> (*[1027]*)transfer_str s info
000267|   | L.Number _         -> (*[870]*)red_return VL.number
000268|   | L.Table (l,uns,ns) -> 
000269|     (* add unamed entries *)
000270|     (*[337]*)transfer_exp_list uns info >>= fun unvlats ->
000271|      (*[337]*)let joined_unsvlats = List.fold_left (fun acc vlat -> (*[60]*)VL.join acc vlat) VL.bot unvlats in
000272|      (*[337]*)let plat            =
000273|        (match unvlats with
000274|          | [] -> (*[319]*)PL.mt
000275|          | _  -> (*[18]*)PL.add_nonstr_default VL.number joined_unsvlats PL.mt) in(*unnamed/numeric all go in default*)
000276|      (* add named entries *)
000277|      (*[337]*)let (props, exps)   = List.split ns in
000278|       (*[337]*)transfer_exp_list exps info >>= fun vlats ->
000279|        (*[337]*)let plat'  = List.fold_left2 (fun plat prop vlat -> (*[313]*)PL.add prop vlat plat) plat props vlats in
000280|         (*[337]*)get_curr_state >>= fun slat' ->
000281|          (*[337]*)restore_state { slat' with SL.store = ST.add_label slat'.SL.store l plat' } >>= fun () ->
000282|           (*[337]*)return (VL.table l)
000283|   | L.Fun (l,_,body,ret_label) -> 
000284|     (match body with
000285|       | None      -> (*[0]*)mfailwith "encountered bodyless function"
000286|       | Some body ->
000287|         (*[394]*)get_curr_state >>= fun slat ->
000288|          (*[394]*)curr_scope_chain >>= fun scopechain ->
000289|           (*[394]*)let prop_chain = SL.build_prop_chain scopechain in
000290|           (*[394]*)set_state body.L.label >>= fun () ->
000291|             (*[394]*)let fun_info   = { info with ret_label = ret_label } in
000292|            (*[394]*)transfer_stmts body.L.stmts fun_info >>= fun () ->
000293|             (*[394]*)restore_state slat >>= fun () ->
000294|                 (* allocate abstract closure for l: record scope chain *)
000295|              (*[394]*)alloc_closure l prop_chain >>= fun () ->
000296|               (*[394]*)return (VL.proc l))
000297|     (* invariant: any proc l, has had its scopechain allocated in store *)
000298|  
000299| (*  transfer_gettable_event : label -> info -> VL -> VL -> (AL * SL) -> AL * SL * VL *)
000300| and transfer_gettable_event clab info tabvlat idxvlat = 
000301|   (*  inner_gettable_event : VL -> VL -> (AL * SL) -> AL * SL * VL *)
000302|   (*[647]*)let rec inner_gettable_event tabvlat acc =
000303|     (*[1116]*)if VL.is_bot tabvlat (* strict *)
000304|     then (*[141]*)return VL.bot
000305|     else
000306|       (*[975]*)get_curr_state >>= fun slat ->
000307|        (*[975]*)raw_get tabvlat idxvlat >>= fun v ->
000308|         (*[975]*)lookup_event tabvlat "__index" >>= fun h ->
000309|          (*[975]*)mvl_join
000310|            (if VL.may_be_table tabvlat
000311|             then (*[518]*)return 
000312|                (VL.join
000313|                   (VL.exclude_nil v)
000314|                   (if (*[518]*)VL.may_be_nil v && (*[205]*)VL.may_be_nil h then (*[172]*)VL.nil else (*[346]*)VL.bot))
000315|             else (*[457]*)merror)
000316|            (mvl_join
000317|               (if VL.may_be_proc h
000318|                then ((*[45]*)transfer_calls clab h [tabvlat;idxvlat] info >>= (*-- call the handler *)
000319|                        adjust_to_single)
000320|                else (*[930]*)merror)
000321|               (let h' = VL.exclude_proc h in
000322|                (*[975]*)if VL.leq h' acc
000323|                then (*[506]*)merror (* already explored base values *)
000324|                else (*[469]*)inner_gettable_event h' (VL.join h' acc)))        (*-- or repeat operation on it *)
000325|   in
000326|   (*[647]*)inner_gettable_event tabvlat VL.bot (* empty accumulator of explored 'base' values *)
000327|  
000328| (*  transfer_lvalue_read : lvalue -> info -> (AL * SL) -> (AL * SL * VL) *)
000329| and transfer_lvalue_read lv info =
000330|   match lv with
000331|   | L.Name x ->
000332|     (*[4183]*)read_name x
000333|   | L.DynIndex (clab,e0,e1) ->
000334|     (*[591]*)transfer_exp e0 info >>= adjust_to_single >>= fun vlat0 ->
000335|      (*[591]*)transfer_exp e1 info >>= adjust_to_single >>= fun vlat1 ->
000336|       (*[591]*)transfer_gettable_event clab info vlat0 vlat1
000337|  
000338| (*  transfer_lvalue_write_list : lvalue list -> exp list -> info -> (AL * SL) -> (AL * SL * unit) *)
000339| and transfer_lvalue_write_list lvals exps info =
000340|   (* Three passes: 1. evaluate lvalues down through recursion
000341|                    2. at end, evaluate rhs, left-to-right
000342|                    3. perform assignment back through the recursion *)
000343|  
000344|   (*  adjust_length : int -> VL list -> VL list *)
000345|   (*[1119]*)let rec adjust_length lvallen vlats = match (lvallen,vlats) with
000346|     | (0,[])    -> (*[1098]*)[]
000347|     | (0, _)    -> (*[21]*)[]     (* too many values: drop the rest *)
000348|     | (n,[])    -> (*[11]*)VL.nil::(adjust_length (n-1) []) (* too few values: add nils *)
000349|     | (n,vlat::vlats) -> (*[1160]*)vlat::(adjust_length (n-1) vlats)
000350|   in
000351|   (*  inner_write_list : lvalue list -> exp list -> info -> (AL * SL) -> (AL * SL * VL list) *)
000352|   (*[1119]*)let rec inner_write_list lvs exps info = match lvs with
000353|     | [] ->
000354|       (*[1119]*)transfer_exp_list exps info >>= fun vlats ->  (* step 2: evaluate rhs *)
000355|        (*[1119]*)let lvallen = List.length lvals in
000356|        (* too few values: add nils, too many values: drop some *)
000357|        (*[1119]*)return (List.rev (adjust_length lvallen vlats))
000358|     | lv::lvs ->
000359|       (match lv with
000360|         | Last.Name n ->
000361|           (*[822]*)inner_write_list lvs exps info >>= fun vlats ->
000362|             (match vlats with
000363|              | [] ->              (*[0]*)mfailwith ("missing rhs in name assignment: " ^ n)
000364|              | vlat::vlats -> (*[822]*)write_name n vlat >>= fun () -> (*[822]*)return vlats)
000365|         | Last.DynIndex (clab,e0,e1) ->
000366|           (*[349]*)transfer_exp e0 info >>= adjust_to_single >>= fun vlat0 ->
000367|            (*[349]*)red_return (VL.only_tables vlat0) >>= fun vlat0 -> (* slight hack to reduce to bot on index error *)
000368|               (*[349]*)transfer_exp e1 info >>= adjust_to_single >>= fun vlat1 ->
000369|              (*[349]*)inner_write_list lvs exps info >>= fun vlats ->
000370|               (match vlats with
000371|                 | [] ->                (*[0]*)mfailwith "missing rhs in dyn. indexed assignment"
000372|                 | vlat::vlats -> (*[349]*)write_dyn_prop vlat0 vlat1 vlat >>= fun () -> (*[349]*)return vlats))
000373|   in
000374|   (*[1119]*)inner_write_list lvals exps info >>= fun vlats -> (* step 1: evaluate lvalues *)
000375|    match vlats with
000376|      | []   -> (*[1119]*)return ()
000377|      | _::_ -> (*[0]*)mfailwith "additional rhs in assignment"
000378|  
000379| (*  transfer_arith_event : label -> info -> Ast.binop -> string -> VL -> VL -> (AL * SL) -> AL * SL * VL *)
000380| and transfer_arith_event clab info op event op1 op2 =
000381|   (*[299]*)let o1,o2 = VL.coerce_tonum op1, VL.coerce_tonum op2 in
000382|   (*[299]*)mvl_join
000383|     (if VL.may_be_number (VL.meet o1 o2)  (* -- both operands are numeric? *)
000384|      then (*[197]*)red_return (VL.binop op o1 o2)  (* -- '+' here is the primitive 'add' *)
000385|      else (*[102]*)merror)
000386|     (getbinhandler op1 op2 event >>= fun h ->
000387|      (*[299]*)if VL.may_be_proc h
000388|      then (* -- call the handler with both operands *)
000389|        (*[21]*)transfer_calls clab h [op1;op2] info >>= adjust_to_single
000390|      else (* -- no handler available: default behavior *)
000391|        (*[278]*)merror)
000392|  
000393| (*  transfer_binop : op -> label -> info -> VL -> VL -> (AL * SL) -> (AL * SL * VL) *)
000394| and transfer_binop op clab info vlat0 vlat1 = match op with
000395|   | Ast.Plus  -> (*[125]*)transfer_arith_event clab info op "__add" vlat0 vlat1
000396|   | Ast.Minus -> (*[105]*)transfer_arith_event clab info op "__sub" vlat0 vlat1
000397|   | Ast.Times -> (*[17]*)transfer_arith_event clab info op "__mul" vlat0 vlat1
000398|   | Ast.Div   -> (*[9]*)transfer_arith_event clab info op "__div" vlat0 vlat1
000399|   | Ast.Mod   -> (*[15]*)transfer_arith_event clab info op "__mod" vlat0 vlat1
000400|   | Ast.Pow   -> (*[28]*)transfer_arith_event clab info op "__pow" vlat0 vlat1
000401|   | _        -> (*[312]*)red_return (VL.binop op vlat0 vlat1) (* potentially reduce statelattice to bot *)
000402|  
000403| (*  transfer_exp : exp -> info -> (AL * SL) -> (AL * SL * VL list) *)
000404| and transfer_exp e info = match e with
000405|   | L.Lit l ->
000406|     (*[3058]*)transfer_lit l info >>=
000407|      list_return
000408|   | L.Lvalue lv ->
000409|     (*[4774]*)transfer_lvalue_read lv info >>=
000410|      list_return
000411|   | L.Unop (op,e) ->
000412|     (*[19]*)transfer_exp e info >>= adjust_to_single >>= fun vlat ->
000413|      (*[19]*)red_return (VL.unop op vlat) >>=         (* potentially reduce statelattice to bot *)
000414|       list_return
000415|   | L.Binop (clab,e0,op,e1) ->
000416|     (*[569]*)transfer_exp e0 info >>= adjust_to_single >>= fun vlat0 ->  (* left-to-right eval *)
000417|      (*[611]*)transfer_exp e1 info >>= adjust_to_single >>= fun vlat1 ->
000418|       (*[611]*)transfer_binop op clab info vlat0 vlat1 >>=
000419|        list_return                            (* potentially reduce statelattice to bot *)
000420|   | L.And (e0,e1) ->
000421|     (*[34]*)transfer_exp e0 info >>= adjust_to_single >>= fun vlat0 ->  (* left-to-right eval *)
000422|      (*[40]*)get_curr_state >>= fun slat0 ->
000423|       (*[40]*)transfer_exp e1 info >>= adjust_to_single >>= fun vlat1 ->
000424|        (*[40]*)get_curr_state >>= fun slat1 ->
000425|         (*[40]*)and_join slat0 vlat0 slat1 vlat1 >>=
000426|          list_return
000427|   | L.Or (e0,e1) ->
000428|     (*[8]*)transfer_exp e0 info >>= adjust_to_single >>= fun vlat0 ->  (* left-to-right eval *)
000429|      (*[14]*)get_curr_state >>= fun slat0 ->
000430|       (*[14]*)transfer_exp e1 info >>= adjust_to_single >>= fun vlat1 ->
000431|        (*[14]*)get_curr_state >>= fun slat1 ->
000432|         (*[14]*)or_join slat0 vlat0 slat1 vlat1 >>=
000433|          list_return
000434|   | L.Call (clab,e,es) -> 
000435|     (*[538]*)transfer_exp e info >>= adjust_to_single >>= fun vlat -> (* left-to-right eval *)
000436|      (*[538]*)transfer_exp_list es info >>= fun vlats ->
000437|       (*[538]*)transfer_calls clab vlat vlats info
000438|   | L.Methcall (clab,ilab,e,mname,es) -> 
000439|     (*[46]*)transfer_exp e info >>= adjust_to_single >>= fun vlat -> (* left-to-right eval *)
000440|      (*[46]*)transfer_gettable_event ilab info vlat (VL.string mname) >>= fun recv ->
000441|       (*[46]*)transfer_exp_list es info >>= fun vlats ->
000442|        (*[46]*)transfer_calls clab recv (vlat::vlats) info (* pass self (vlat) as first arg *)
000443|   | L.Paren e -> 
000444|     (*[69]*)transfer_exp e info >>= adjust_to_single >>= list_return
000445|  
000446| (*  transfer_exp_list : exp list -> info -> (AL * SL) -> (AL * SL * VL list) *)
000447| and transfer_exp_list es info =
000448|   match es with
000449|   | []    -> (*[628]*)return []
000450|   | [e]   -> (*[3619]*)transfer_exp e info
000451|   | e::es ->
000452|     (*[788]*)transfer_exp e info >>= adjust_to_single >>= fun vlat ->
000453|     (*[788]*)transfer_exp_list es info >>= fun vlats ->
000454|     (*[788]*)return (vlat::vlats)
000455|  
000456| (*  transfer_stmts : stmts -> info -> (AL * SL) -> (AL * SL * unit) *)
000457| and transfer_stmts ss info =
000458|   match ss with
000459|   | [] ->
000460|     (*[767]*)return () (* empty sequence does not necessarily mean 'Return'
000461|                        -- only when falling through a procedure
000462|                        patch: add an explicit 'Return nil' to all procedures in parser (when absent) *)
000463|   | s::ss ->
000464|     (*[3226]*)let lab = s.L.stmt_pos.L.line_label in
000465|     (*[3226]*)record_state lab >>= fun () -> (* join into prev.elm. *)
000466|     (*[3329]*)set_state lab >>= fun () ->    (* and read off joined result *)
000467|     (match s.L.stmt with
000468|     | L.Break ->
000469|       (*[53]*)lookup_state (fst info.break_labels) >>= fun slat_entry -> (* lookup old env and restore it *)
000470|        (*[53]*)restore_scope_chain (slat_entry.SL.env) >>= fun () ->
000471|         (*[53]*)record_state (snd info.break_labels) >>= fun () -> (*join state into outer break label *)
000472|          (*[53]*)restore_state SL.bot >>= fun () ->  (* i.e., unreachable *)
000473|         (*[53]*)return ()
000474|     | L.If (e,bl1,bl2) ->
000475|       (*[145]*)curr_scope_chain >>= fun env ->
000476|        (*[145]*)transfer_exp e info >>= adjust_to_single >>= fun _vlat ->
000477|         (*[145]*)let thenbranch = transfer_block bl1 info in
000478|         (*[145]*)let elsebranch = transfer_block bl2 info in
000479|         (*[145]*)branch_and_join env thenbranch elsebranch >>= fun () ->
000480|           (*[145]*)transfer_stmts ss info
000481|     | L.WhileDo (e,bl,endlab) ->
000482|       (*[104]*)let loop_info = { info with break_labels = (lab,endlab) } in
000483|       (*[104]*)curr_scope_chain >>= fun env ->
000484|        (*[104]*)let test = transfer_exp e info >>= adjust_to_single in
000485|        (*[104]*)let body = transfer_block bl loop_info >>= fun () -> (*[207]*)restore_scope_chain env in
000486|        (*[104]*)loop test body >>= fun () ->
000487|          (* joined "breaked state" with post condition *)
000488|          (*[104]*)record_state endlab >>= fun () ->
000489|           (*[104]*)set_state endlab >>= fun () ->
000490|            (*[104]*)transfer_stmts ss info
000491|     | L.Doend bl ->
000492|       (*[38]*)curr_scope_chain >>= fun env ->
000493|        (*[38]*)transfer_block bl info >>= fun () ->
000494|         (*[38]*)restore_scope_chain env >>= fun () ->
000495|          (*[38]*)transfer_stmts ss info
000496|     | L.Assign (lvs,es) ->
000497|       (*[1119]*)transfer_lvalue_write_list lvs es info >>= fun () ->
000498|        (*[1119]*)transfer_stmts ss info
000499|     | L.Local (xs,es) ->
000500|       (*[778]*)transfer_exp_list es info >>= fun vlats -> (* left-to-right eval *)
000501|        (*[778]*)enter_scope lab >>= fun () ->
000502|         (*[778]*)add_locals xs vlats >>= fun () ->
000503|          (*[778]*)transfer_stmts ss info
000504|     | L.Callstmt (e,es) ->
000505|       (*[621]*)transfer_exp e info >>= adjust_to_single >>= fun vlat -> (* left-to-right eval *)
000506|        (*[621]*)transfer_exp_list es info >>= fun vlats ->
000507|         (*[621]*)transfer_calls lab vlat vlats info >>= fun _vlats -> (* stmt call, so ignore results *)
000508|          (*[621]*)transfer_stmts ss info
000509|     | L.Methcallstmt (ilab,e,mname,es) ->
000510|       (*[10]*)transfer_exp e info >>= adjust_to_single >>= fun vlat -> (* left-to-right eval *)
000511|        (*[10]*)transfer_gettable_event ilab info vlat (VL.string mname) >>= fun recv ->
000512|         (*[10]*)transfer_exp_list es info >>= fun vlats ->
000513|          (*[10]*)transfer_calls lab recv (vlat::vlats) info >>= fun _vlats -> (* pass self (vlat) as first arg *)
000514|           (*[10]*)transfer_stmts ss info
000515|     | L.Return es ->
000516|       (*[461]*)let ret_label  = info.ret_label in
000517|       (*[461]*)transfer_exp_list es info >>= fun vlats ->
000518|        (*[461]*)alloc_resstore ret_label vlats >>= fun ret_store ->
000519|         (*[461]*)restore_state { SL.bot with SL.store = ret_store } >>= fun () ->
000520|           (*[461]*)record_state ret_label >>= fun () ->
000521|           (*[461]*)restore_state SL.bot (* i.e., unreachable *)
000522|     )
000523| (* Since return is a control-delimiter,
000524|    neither a semantics nor an analysis of a statement list
000525|    should be a simple fold *)
000526|  
000527| (*  transfer_block : block -> info -> (AL * SL) -> (AL * SL * unit)  *)
000528| and transfer_block bl info = match bl with
000529|   | None    -> (*[92]*)return ()
000530|   | Some bl -> (*[340]*)transfer_stmts bl.L.stmts info
000531|  
000532| (*  transfer_prog : Last.prog -> AL  *)
000533| let transfer_prog p =
000534|   (*[172]*)let init = (AL.init, SL.init) in
000535|   (*[172]*)let info = { fun_map        = p.L.fun_map;
000536|                break_labels   = (p.L.ret_label,p.L.ret_label);
000537|                ret_label      = p.L.ret_label } in
000538|   (*[172]*)let stmts = match p.L.last with
000539|     | None    -> (*[0]*)[]
000540|     | Some bl -> (*[172]*)bl.L.stmts in
000541|   (*[172]*)let rec loop prev =
000542|     (*[444]*)let (alat,slat,()) = transfer_stmts stmts info prev in
000543|     (*[444]*)if AL.leq alat (fst prev)
000544|     then ((*[172]*)alat,slat)
000545|     else (*[272]*)loop (alat, SL.init)
000546|   in
000547|   (*[172]*)let (alat,slat) = loop init in
000548|   (*[172]*)let alat = AL.add alat p.L.ret_label slat in (* add extra end-of-program label to lattice *)
000549|   (*[172]*)alat

Legend:
   some code - line containing no point
   some code - line containing only visited points
   some code - line containing only unvisited points
   some code - line containing both visited and unvisited points