000001| (** Abstract store datatype and operations *)
000002|
000003| module VL = Valuelattice
000004| module PL = Proplattice
000005| module StoreMap = Map.Make(struct type t = int
000006| let compare i i' = (*[1719787]*)i - i' end)
000007| module Str = Stringlattice
000008|
000009| type elem = Proplattice.elem StoreMap.t
000010|
000011| (* leq : elem -> elem -> bool *)
000012| let leq st0 st1 =
000013| (*[43643]*)StoreMap.fold (fun lab obj acc ->
000014| (*[702173]*)let obj' = try (*[702173]*)StoreMap.find lab st1
000015| with Not_found -> (*[630846]*)PL.bot
000016| in (*[702173]*)acc && (*[108582]*)PL.leq obj obj') st0 true
000017|
000018| (* eq : elem -> elem -> bool *)
000019| let eq = (*[1]*)StoreMap.equal Proplattice.eq
000020|
000021| (* join : elem -> elem -> elem *)
000022| let join st0 st1 = (*[62745]*)StoreMap.merge (fun lab obj0 obj1 -> match obj0,obj1 with
000023| | None,_ -> (*[19013]*)obj1
000024| | _,None -> (*[52026]*)obj0
000025| | Some obj0,Some obj1 -> (*[1246867]*)Some (PL.join obj0 obj1)) st0 st1
000026|
000027| (* meet : elem -> elem -> elem *)
000032|
000033| (* bot : elem *)
000034| let bot = (*[1]*)StoreMap.empty
000035|
000036|
000037| (** {3 Lattice operations } *)
000038|
000039| (* is_bot : store -> bool *)
000040| let is_bot st = (*[24856]*)leq st bot
000041|
000042| (* add_label : store -> label -> PL -> store *)
000043| let add_label st lab prop =
000044| (*[4259]*)StoreMap.add lab prop st
000045|
000046| (* find_label_exn : store -> label -> PL *)
000047| let find_label_exn st lab =
000048| (*[14765]*)StoreMap.find lab st
000049|
000050| (* find_label : store -> label -> PL *)
000051| let find_label st lab =
000052| (*[12859]*)try (*[12859]*)find_label_exn st lab
000053| with Not_found -> (*[109]*)PL.bot
000054|
000055| (* fold_labels_scopechain : (label list -> 'a -> 'a) -> store -> label -> 'a -> 'a *)
000056| let fold_labels_scopechain f st lab acc =
000057| (*[470]*)if is_bot st
000058| then (*[2]*)acc
000059| else
000060| (*[468]*)try
000061| (*[468]*)let pl = find_label st lab in
000062| (match pl with
000073| else
000074| (*[828]*)let tables = vlat.VL.tables in
000075| (*[828]*)VL.LabelSet.fold (fun lab acc ->
000076| (*[497]*)let plat = find_label st lab in
000077| (*[497]*)VL.join acc (PL.find prop plat) ) tables VL.bot
000078|
000079| (* lookup_all_keys : store -> VL -> VL *)
000080| let lookup_all_keys st vlat =
000081| (*[97]*)let tables = vlat.VL.tables in
000082| (*[97]*)VL.LabelSet.fold (fun lab acc ->
000083| (*[118]*)let plat = find_label st lab in
000084| (*[118]*)VL.join acc (PL.find_all_keys plat) ) tables VL.bot
000085|
000086| (* lookup_all_str_props : store -> VL -> VL *)
000087| let lookup_all_str_props st vlat =
000088| (*[126]*)let tables = vlat.VL.tables in
000089| (*[126]*)VL.LabelSet.fold (fun lab acc ->
000090| (*[142]*)let plat = find_label st lab in
000091| (*[142]*)VL.join acc (PL.find_all plat) ) tables VL.bot
000092|
000093| (* lookup_all_nonstr_props : store -> VL -> VL *)
000094| let lookup_all_nonstr_props st vlat =
000095| (*[95]*)let tables = vlat.VL.tables in
000096| (*[95]*)VL.LabelSet.fold (fun lab acc ->
000097| (*[116]*)let plat = find_label st lab in
000098| (*[116]*)VL.join acc (snd (PL.find_nonstr_defaults plat))) tables VL.bot
000099|
000100| (* lookup_all_props : store -> VL -> VL *)
000101| let lookup_all_props st vlat =
000102| (*[79]*)VL.join (lookup_all_str_props st vlat) (lookup_all_nonstr_props st vlat)
000103|
000104| (* lookup_nonstr_default_prop : store -> VL -> VL -> VL *)
000105| let lookup_nonstr_default_prop st vlat vlat' =
000106| (*[288]*)if (*[288]*)is_bot st (*|| VL.is_bot vlat*) || (*[288]*)VL.is_bot vlat'
000108| else
000109| (*[288]*)let tables = vlat.VL.tables in
000110| (*[288]*)VL.LabelSet.fold (fun lab acc ->
000111| (*[176]*)let plat = find_label st lab in
000112| (*[176]*)let def_key,def = PL.find_nonstr_defaults plat in (* Note: for non-str entries *)
000113| (*[176]*)let acc' = VL.join VL.nil acc in
000114| (*[176]*)if VL.is_bot (VL.meet vlat' def_key)
000115| then (*[70]*)acc'
000116| else (*[106]*)VL.join acc' def) tables VL.bot
000117|
000118| (* lookup_dyn_prop : store -> VL -> VL -> VL *)
000119| let lookup_dyn_prop st vlat vlat' =
000120| (*[2411]*)if (*[2411]*)is_bot st || (*[2411]*)VL.is_bot vlat (* No indexed expr values *)
000121| || (*[1108]*)VL.is_bot vlat' (* No indexing expr values *)
000122| then (*[1303]*)VL.bot
000123| else
000124| (*[1108]*)let res = if VL.may_be_non_strings vlat'
000125| then (*[288]*)lookup_nonstr_default_prop st vlat vlat' (* all relevant default entries *)
000126| else (*[820]*)VL.bot in
000127| (*[1108]*)let strres = match vlat'.VL.strings with
000128| | Str.Top -> (*[38]*)lookup_all_str_props st vlat
000129| | Str.Const s -> (*[820]*)lookup_str_prop st vlat s
000130| | Str.Bot -> (*[250]*)VL.bot in
000131| (*[1108]*)VL.join res strres
000132|
000133| (* raw_get : store -> VL -> VL -> VL *)
000134| let raw_get = (*[1]*)lookup_dyn_prop
000135|
000136| (* write_str_prop : store -> VL -> str -> VL -> store *)
000137| let write_str_prop st vlat prop vlat' =
000139| (*[155]*)let tables = vlat.VL.tables in
000140| (*[155]*)VL.LabelSet.fold
000141| (fun lab stacc ->
000142| (*[155]*)try
000143| (*[155]*)let plat = find_label_exn st lab in
000144| (*[155]*)let old_vlat = PL.find prop plat in (* can be improved if not (str <= def_key) *)
000145| (*[155]*)let new_plat = PL.add prop (VL.join vlat' (* join in default + weak update *)
000146| (VL.exclude_nil old_vlat)) plat in
000147| (* if prev. entry was nil (not present) filter it and add strongly *)
000148| (*[155]*)join (add_label st lab new_plat) stacc (* improvements: exclude nil from vlat', model removal *)
000149| with Not_found -> ((*Printf.printf "Unknown allocation site label %i\n" lab;*)
000156| else
000157| (*[39]*)let tables = vlat0.VL.tables in
000158| (*[39]*)VL.LabelSet.fold (fun lab stacc ->
000159| (*[39]*)try
000160| (*[39]*)let plat = find_label_exn st lab in
000161| (*[39]*)let new_plat = PL.add_all_str vlat plat in (* weak updates all str entries -- incl.default_str *)
000162| (*[39]*)add_label stacc lab new_plat
000163| with Not_found -> ((*Printf.printf "Unknown allocation site label %i\n" lab;*)
000170| else
000171| (*[171]*)let tables = vlat0.VL.tables in
000172| (*[171]*)VL.LabelSet.fold (fun lab stacc ->
000173| (*[171]*)let plat = find_label st lab in
000174| (*[171]*)let old_def_key, old_def = PL.find_nonstr_defaults plat in
000175| (*[171]*)let new_plat = PL.add_nonstr_default (VL.join vlat1 old_def_key) (VL.join vlat old_def) plat in (* weak update in default *)
000176| (*[171]*)add_label stacc lab new_plat) tables st
000177|
000178| (* write_dyn_prop : store -> VL -> VL -> VL -> store *)
000179| let write_dyn_prop st vlat0 vlat1 vlat =
000180| (*[353]*)if (*[353]*)is_bot st || (*[311]*)VL.is_bot vlat0 (* No indexed expr values *)
000181| || (*[311]*)VL.is_bot vlat1 (* No indexing expr values *)
000182| || (*[311]*)VL.is_bot vlat (* No value to write *)
000183| then (*[42]*)bot
000184| else
000185| (*[311]*)let res = if VL.may_be_non_strings vlat1
000186| then (*[171]*)write_default_nonstr_prop st vlat0 (VL.exclude_strings vlat1) vlat (* write to all relevant default entries *)
000187| else (*[140]*)bot in (* don't *)
000188| (*[311]*)let strres = match vlat1.VL.strings with
000189| | Str.Top -> (*[39]*)write_all_str_props st vlat0 vlat
000190| | Str.Const s -> (*[155]*)write_str_prop st vlat0 s vlat
000191| | Str.Bot -> (*[117]*)bot in
000192| (*[311]*)if is_bot res
000193| then (*[140]*)strres
000194| else (*[171]*)join res strres
000195|
000196| (* raw_set : store -> VL -> VL -> VL -> store *)
000197| let raw_set = (*[1]*)write_dyn_prop
000198|
000199| (* get_metatable : store -> VL -> VL *)
000200| let get_metatable st vlat = (* fixme: strings have a metatable with a __index metamethod? *)
000201| (*[8]*)if (*[8]*)is_bot st || (*[8]*)VL.is_bot vlat
000203| else (* semantics: http://www.lua.org/manual/5.1/manual.html#pdf-getmetatable *)
000204| (*[8]*)let tables = vlat.VL.tables in
000205| (*[8]*)VL.LabelSet.fold (fun lab acc ->
000206| (*[8]*)let plat = find_label st lab in (* for each table: *)
000207| (*[8]*)let metatabs = PL.get_metatable plat in (* lookup metatable *)
000208| (*[8]*)let metametatabs =
000209| VL.LabelSet.fold (fun metalab acc ->
000210| (*[10]*)let metatab = find_label st metalab in (* and __metatable in metatable *)
000211| (*[10]*)let metametatab = PL.find "__metatable" metatab in
000212| (*[10]*)if VL.may_be_nil metametatab (* second lookup may fail: include metatab *)
000213| then (*[10]*)VL.join (VL.table metalab) (VL.join (VL.exclude_nil metametatab) acc)
000215| (*[8]*)VL.join acc
000216| (VL.join (VL.exclude_tables metatabs) metametatabs)) tables VL.bot
000217|
000218| (* set_metatable : store -> VL -> VL -> store *)
000219| let set_metatable st vlat metavlat =
000220| (*[70]*)if is_bot st
000222| else (* semantics: http://www.lua.org/manual/5.1/manual.html#pdf-setmetatable *)
000223| (*[70]*)let tables = vlat.VL.tables in
000224| (*[70]*)VL.LabelSet.fold (fun lab stacc ->
000225| (*[70]*)try
000226| (*[70]*)let plat = find_label_exn st lab in
000227| (*[70]*)let old_metavlat = PL.get_metatable plat in
000228| (*[70]*)join stacc
000229| (join
000230| (* no metatable present -> set it *)
000231| (if VL.may_be_nil old_metavlat
000232| then (* normal operation *)
000233| (*[62]*)let plat' = PL.set_metatable (VL.exclude_nil metavlat) plat in
000234| (*[62]*)add_label st lab plat'
000235| else (*[8]*)bot)
000236|
000237| (* metatable labels present, potentially with __metatable fields *)
000238| (VL.LabelSet.fold (fun metalab acc ->
000239| (*[8]*)let metatab = find_label st metalab in
000240| (*[8]*)let metametatab = PL.find "__metatable" metatab in
000241| (*[8]*)if VL.may_be_nil metametatab
000242| then (* may fail to have __metatable field, i.e., may succeed -> update *)
000243| (*[6]*)if (*[6]*)VL.may_be_nil metavlat && (*[4]*)VL.is_nil metavlat (* "rhs" definitely nil *)
000244| then
000245| (*[2]*)let plat' = PL.set_metatable_absent plat in (* weak update: delete+keep = maybe absent *)
000246| (*[2]*)join acc (add_label st lab plat')
000247| else (* weak update *)
000248| (*[4]*)if VL.may_be_nil metavlat (* maybe delete, maybe write = maybe absent *)
000249| then
000250| (*[2]*)let plat' = PL.set_metatable (VL.join old_metavlat (VL.exclude_nil metavlat)) plat in
000251| (*[2]*)let plat' = PL.set_metatable_absent plat' in
000252| (*[2]*)join acc (add_label st lab plat')
000253| else (* just write *)
000254| (*[2]*)let plat' = PL.set_metatable (VL.join old_metavlat (VL.exclude_nil metavlat)) plat in
000255| (*[2]*)join acc (add_label st lab plat')
000256| else
000257| (*[2]*)acc (* error, so return bot, i.e., no effect on acc *) ) old_metavlat.VL.tables bot))
000258| with Not_found -> ((*Printf.printf "Unknown allocation site label %i\n" lab;*)
000260|
000261| (* The following function helps to implement the metatable(obj)[event] notation *)
000262| (* modelling an internal metatable lookup *)
000263| (* This operation differs from the semantics of the API getmetatable (it is unprotected) *)
000264| (* despite the explanation in http://www.lua.org/manual/5.1/manual.html#2.8 *)
000265| (* myget_metatable : store -> VL -> VL *)
000266| let myget_metatable st vlat = (* fixme: strings have a metatable with a __index metamethod? *)
000267| (*[1431]*)if (*[1431]*)is_bot st || (*[1431]*)VL.is_bot vlat
000269| else
000270| (*[1431]*)let tables = vlat.VL.tables in
000271| (*[1431]*)VL.LabelSet.fold (fun lab acc ->
000272| (*[594]*)let plat = find_label st lab in (* for each table: *)
000273| (*[594]*)let metatabs = PL.get_metatable plat in (* lookup metatable *)
000274| (*[594]*)VL.join acc metatabs) tables VL.bot
000275|
000276| (* lookup_event : ST -> VL -> string -> VL *)
000277| let lookup_event st vlat strevent = (* corresponds to metatable(obj)[event] notation in manual *)
000278| (*[1431]*)if (*[1431]*)is_bot st || (*[1431]*)VL.is_bot vlat
000280| else
000281| (*[1431]*)let base = myget_metatable st vlat in (* http://www.lua.org/manual/5.1/manual.html#2.8 *)
000282| (*[1431]*)let res = raw_get st (VL.exclude_nil base) (VL.string strevent) in
000283| (*[1431]*)if VL.may_be_nil base
000284| then (*[452]*)VL.join VL.nil res
000285| else (*[979]*)res
000286|
000287| (* getbinhandler : ST -> VL -> VL -> string -> VL *)
000288| let getbinhandler st vlat0 vlat1 strevent =
000289| (*[299]*)if (*[299]*)is_bot st || (*[228]*)VL.is_bot vlat0 || (*[228]*)VL.is_bot vlat1 (* FIXED *)
000290| then (*[71]*)VL.bot
000291| else (*[228]*)VL.or_join (lookup_event st vlat0 strevent)
000292| (lookup_event st vlat1 strevent)
000293|
000294|
000295| (** {3 Pretty printing} *)
000296|
000297| (* pprint : store -> unit *)
000298| let pprint st =
000299| begin
000300| Format.printf "{ @[<v 0>";
000301| ignore (StoreMap.fold (fun lab plat first ->
000302| if first
000303| then (Format.printf "%-8i -> " lab; PL.pprint plat; false)
000304| else
000305| begin
000306| Format.print_space ();
000307| Format.printf "%-8i -> " lab;
000308| PL.pprint plat;
000309| false
000310| end) st true);
000311| Format.printf "@]}";
000312| end
000313|
000314| (* to_string : elem -> string *)
000315| let to_string st =
000316| let buf = Buffer.create 128 in
000317| let out,flush = Format.get_formatter_output_functions () in (* save previous outputters *)
000318| begin
000319| Format.set_formatter_output_functions (Buffer.add_substring buf) (fun () -> ());
000320| pprint st;
000321| Format.print_flush ();
000322| Format.set_formatter_output_functions out flush; (* restore previous outputters *)
000323| Buffer.contents buf
000324| end