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
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')
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| | _ ->
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
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
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