000001| (** Abstract datatype and operations for property maps (environments) *)
000002|
000003| module Abs = Absencelattice
000004| module VL = Valuelattice
000005|
000006| module TableKey = struct
000007| type t =
000008| | String of string
000009| | Metatable
000010| let compare k k' = match (k,k') with
000011| | Metatable, Metatable -> (*[8846877]*)0
000012| | String _, Metatable -> (*[9143320]*)-1 (* ordering: forall s. s < metatable = metatable *)
000013| | Metatable, String _ -> (*[21045544]*)1
000014| | String s, String s' -> (*[354728590]*)String.compare s s'
000015| end
000016| module TableMap = Map.Make(TableKey)
000017|
000018| module ScopeChainSet =
000019| Set.Make(struct type t = int list
000020| let rec compare is1 is2 = match is1,is2 with
000021| | [],[] -> (*[46535215]*)0
000022| | [],_ -> (*[22992672]*)1
000023| | _,[] -> (*[23008930]*)-1
000024| | i1::is1',i2::is2' ->
000025| (*[400758778]*)let r = compare is1' is2' in
000026| (match r with
000027| | 0 -> (*[194412770]*)i1 - i2
000028| | _ -> (*[206346008]*)r) end)
000029|
000030| (* TODO: add default (integer) index for more precise "array approximation" *)
000031| type elem =
000032| | Bot
000033| | Table of table
000034| and table = { table : (VL.elem * Abs.elem) TableMap.t;
000035| default_str : VL.elem; (* fallback for string entries *)
000036| default_key : VL.elem; (* collective approx. of non-string keys *)
000037| default : VL.elem; (* collective approx. of non-string entries *)
000038| scopechain : ScopeChainSet.t; }
000039|
000040| (* table : table -> elem *)
000041| let table { table; default_str; default_key; default; scopechain } =
000042| (*[8956748]*)Table { table; default_str; default_key; default; scopechain }
000043|
000044| (* bot : elem *)
000045| let bot = (*[1]*)Bot
000046|
000047| (* mt : elem *)
000048| let mt = (*[1]*)Table { table = TableMap.empty;
000049| default_str = VL.bot;
000050| default_key = VL.bot;
000051| default = VL.bot;
000052| scopechain = ScopeChainSet.empty; }
000053|
000054| (* leq : elem -> elem -> bool *)
000055| let leq m0 m1 = match m0,m1 with
000056| | Bot,_ -> (*[1021774]*)true
000057| | _,Bot -> (*[894970]*)false
000058| | Table m0,Table m1 ->
000059| (*[5472200]*)VL.leq m0.default_str m1.default_str
000060| && (*[5472200]*)VL.leq m0.default_key m1.default_key
000061| && (*[5472200]*)VL.leq m0.default m1.default
000062| && (*[5472200]*)((*[5472200]*)TableMap.fold
000063| (fun p (v,a) acc ->
000064| (*[40437922]*)acc
000065| && ((*[40437922]*)try (*[40437922]*)let v',a' = TableMap.find p m0.table in
000066| ((*[39342488]*)VL.leq v' v && (*[39342488]*)Abs.leq a' a)
000067| with Not_found -> (* not necessarily in first map *)
000068| (*[1095434]*)Abs.eq a Abs.maybe_absent)) (* if definite entry in second: not ordered *)
000069| m1.table true)
000070| && (*[5472200]*)((*[5472200]*)TableMap.fold
000071| (fun p (v,a) acc ->
000072| (*[39553530]*)acc
000073| &&
000074| ((*[39553530]*)try (*[39553530]*)let _ = TableMap.find p m1.table in
000075| (*[39342488]*)true (* elements in both have already been checked above *)
000076| with Not_found -> (* not necessarily in second map *)
000077| (match p with
000078| | TableKey.String s -> ((*[211042]*)VL.leq v m1.default_str)
000125| | None, Some (v1,a1) ->
000126| (match s with
000127| | TableKey.String s ->
000128| (*[295361]*)let entrycand = VL.meet m0.default_str v1 in
000129| (*[295361]*)if VL.is_bot entrycand
000130| then
000131| ((*[153236]*)if Abs.eq a1 Abs.is_present
000132| then (*[8142]*)raise Returnbot (* certainly in second, but not in meet --> return bot *)
000133| else (*[145094]*)Some (entrycand, a1) (* maybe in second and not in result *)
000134| ) (* -> absent from meet, represented as uncertain bot entry *)
000135| else
000136| (*[142125]*)Some (entrycand, a1)
000137| | TableKey.Metatable ->
000138| ((*[24714]*)if Abs.eq a1 Abs.is_present
000139| then (*[5096]*)raise Returnbot
000140| else (*[19618]*)None))
000141| | Some (v0,a0), None ->
000142| (match s with
000143| | TableKey.String s ->
000144| (*[92363]*)let entrycand = VL.meet v0 m1.default_str in
000145| (*[92363]*)if VL.is_bot entrycand
000146| then
000147| ((*[16063]*)if Abs.eq a0 Abs.is_present
000148| then (*[7965]*)raise Returnbot (* certainly in first, but not in meet --> return bot *)
000149| else (*[8098]*)Some (entrycand, a0) (* maybe in first and not in result *)
000150| ) (* -> absent from meet, represented as uncertain bot entry *)
000151| else (*[76300]*)Some (entrycand, a0)
000152| | TableKey.Metatable ->
000153| ((*[10225]*)if Abs.eq a0 Abs.is_present
000154| then (*[5136]*)raise Returnbot
000155| else (*[5089]*)None))
000156| | Some (v0,a0), Some (v1,a1) ->
000157| (*[1668152]*)let abscand = Abs.meet a0 a1 in
000158| (*[1668152]*)let entrycand = VL.meet v0 v1 in
000159| (*[1668152]*)if VL.is_bot entrycand
000160| then
000161| ((*[8109]*)if Abs.eq abscand Abs.is_present
000162| then (*[4279]*)raise Returnbot (* definite, empty entry -> result is bottom *)
000163| else ((*[3830]*)Some (entrycand, abscand)))
000164| else
000165| (*[1660043]*)Some (entrycand, abscand)) m0.table m1.table in
000166| (*[256527]*)let sch = ScopeChainSet.inter m0.scopechain m1.scopechain in
000167| (*[256527]*)table { table = tab; default_str = def_str; default_key = def_key; default = def; scopechain = sch }
000168| with Returnbot -> (*[30618]*)Bot
000169|
000170| (* is_bot : map -> bool *)
000171| let is_bot pl = (*[37678]*)leq pl bot
000172|
000173| (* mem : str -> map -> bool *)
000174| let mem s map = match map with
000176| | Table map -> (*[11259]*)TableMap.mem (TableKey.String s) map.table
000177|
000178| (* find_exn : str -> map -> VL * Abs *)
000179| let find_exn str map = match map with
000180| | Bot -> (*[1677]*)raise Not_found
000181| | Table map -> (*[14669]*)TableMap.find (TableKey.String str) map.table (* used for variable lookup *)
000182|
000183| (* find : str -> map -> VL *)
000184| let find str map = match map with
000185| | Bot -> (*[87443]*)VL.bot
000186| | Table map ->
000187| (*[55297]*)try
000188| (*[55297]*)let (vlat,abs) = TableMap.find (TableKey.String str) map.table in
000189| (*[15448]*)if abs = Abs.is_present
000190| then (*[8128]*)vlat
000191| else (*[7320]*)VL.join vlat VL.nil (* not definite read --> include nil *)
000192| with Not_found ->
000193| (*[39849]*)VL.join map.default_str VL.nil (* maybe not covered by default *)
000194|
000195| (* find_nonstr_defaults : map -> VL * VL *)
000196| let find_nonstr_defaults map = match map with
000197| | Bot -> ((*[244235]*)VL.bot,VL.bot)
000198| | Table map -> ((*[74293]*)map.default_key, map.default)
000199|
000200| (* find_all_keys : map -> VL *)
000201| let find_all_keys map = match map with
000202| | Bot -> (*[52119]*)VL.bot
000203| | Table map ->
000204| (*[16263]*)TableMap.fold (fun name (_vlat,_abs) acc -> match name with
000205| | TableKey.Metatable -> (*[8432]*)acc (* excluding metatable *)
000206| | TableKey.String kname -> (*[107974]*)VL.join (VL.string kname) acc)
000207| map.table (VL.join map.default_key
000208| (if VL.is_bot map.default_str then (*[778]*)VL.bot else (*[15485]*)VL.anystring))
000209|
000210| (* find_all : map -> VL *)
000211| let find_all map = match map with
000212| | Bot -> (*[76733]*)VL.bot
000213| | Table map ->
000214| (*[22650]*)TableMap.fold (fun name (vlat,_abs) acc -> match name with
000215| | TableKey.Metatable -> (*[11345]*)acc (* excluding metatable *)
000216| | TableKey.String name -> (*[150793]*)VL.join vlat acc) map.table map.default_str
000217|
000218| (* get_metatable : map -> VL *)
000219| let get_metatable map = match map with
000220| | Bot -> (*[188877]*)VL.bot
000221| | Table map ->
000222| (*[69733]*)try
000223| (*[69733]*)let (vlat,abs) = TableMap.find TableKey.Metatable map.table in
000224| (*[37242]*)if abs = Abs.is_present
000225| then (*[19493]*)vlat
000226| else (*[17749]*)VL.join vlat VL.nil (* not definite read --> include nil *)
000227| with Not_found -> (*[32491]*)VL.nil (* failure: return nil *)
000228|
000229| (* set_metatable : VL -> map -> map *)
000230| let set_metatable v map = match map with
000231| | Bot -> (*[1930]*)bot
000232| | Table map ->
000233| (*[21468]*)if v = VL.bot
000234| then (*[956]*)bot
000235| else (*[20512]*)table { map with table = TableMap.add TableKey.Metatable (v,Abs.is_present) map.table }
000236|
000237| (* set_metatable_absent : map -> map *)
000238| let set_metatable_absent map' = match map' with
000239| | Bot -> (*[1469]*)bot
000240| | Table map ->
000241| (*[5434]*)try
000242| (*[5434]*)let (vlat,abs) = TableMap.find TableKey.Metatable map.table in
000243| (*[3637]*)if abs = Abs.is_present (* already marked absent? *)
000244| then (*[2853]*)table { map with table = TableMap.add TableKey.Metatable (vlat,Abs.maybe_absent) map.table }
000245| else (*[784]*)map'
000246| with Not_found -> (*[1797]*)map' (* failure: return nil *)
000247|
000248| (* add : str -> val -> map -> map *)
000249| let add s v map = match map with
000250| | Bot -> (*[3768]*)bot
000251| | Table map ->
000252| (*[61481]*)if VL.leq v VL.bot
000253| then (*[950]*)bot
000254| else
000255| (*[60531]*)table { map with table =
000256| let v' = VL.exclude_nil v in
000257| (*[60531]*)if (*[60531]*)VL.may_be_nil v || (*[20649]*)VL.is_bot v'
000258| then (*[39882]*)TableMap.add (TableKey.String s) (v',Abs.maybe_absent) map.table
000259| else (*[20649]*)TableMap.add (TableKey.String s) (v',Abs.is_present) map.table }
000260|
000261| (* add_local : str -> val -> map -> map *)
000262| let add_local s v map = match map with
000263| | Bot -> (*[233126]*)bot
000264| | Table map ->
000265| (*[83308]*)if VL.leq v VL.bot
000266| then (*[955]*)bot
000267| else (* adding a local variable with value nil, makes it visible in scope, e.g. for recursion *)
000268| (*[82353]*)table { map with table = TableMap.add (TableKey.String s) (v,Abs.is_present) map.table }
000269|
000270| (* add_nonstr_default : val -> val -> map -> map *)
000271| let add_nonstr_default k v map = match map with
000272| | Bot -> (*[114664]*)bot
000273| | Table map ->
000274| (*[46703]*)if (*[46703]*)VL.leq k VL.bot || (*[45760]*)VL.leq v VL.bot
000275| then (*[1859]*)bot
000276| else
000277| (*[44844]*)let k = VL.exclude_nil k in
000278| (*[44844]*)if VL.leq k VL.bot
000279| then (*[3]*)table { map with default_key = VL.bot; default = VL.bot } (* reduce domain *)
000280| else (*[44841]*)table { map with default_key = k; default = (VL.exclude_nil v) }
000281|
000282| (* add_scopechain : map -> label list -> map *)
000283| let add_scopechain map sc = match map with
000284| | Bot -> (*[1464]*)bot
000285| | Table map -> (*[20885]*)table { map with scopechain = ScopeChainSet.add sc map.scopechain }
000286|
000287| (* add_all_str : val -> map -> map *)
000288| let add_all_str v map = match map with
000289| | Bot -> (*[3507]*)bot
000290| | Table map ->
000291| (*[23844]*)if VL.leq v VL.bot
000292| then (*[951]*)bot
000293| else
000294| (*[22893]*)let delete = VL.may_be_nil v in
000295| (*[22893]*)table { table = TableMap.mapi
000296| (fun key (old_vlat,old_abs) -> match key with
000297| | TableKey.Metatable -> ((*[11670]*)old_vlat,old_abs) (* excluding metatable *)
000298| | TableKey.String _ ->
000299| (*[152541]*)if delete
000300| then ((*[70501]*)VL.join (VL.exclude_nil v) old_vlat,Abs.maybe_absent)
000301| else ((*[82040]*)VL.join (VL.exclude_nil v) old_vlat,old_abs)) map.table;
000302| default_str = VL.join v map.default_str;
000303| default_key = map.default_key;
000304| default = map.default;
000305| scopechain = map.scopechain; }
000306|
000307| (* add_all_params : str list -> val list -> map -> map *)
000308| let rec add_all_params xs vs map' = match map' with
000309| | Bot -> (*[1445]*)bot
000310| | Table map -> match (xs,vs) with
000311| | [] , [] -> (*[2763]*)map'
000312| | [] , v::vs -> (*[798]*)map' (* drop superflous arguments *)
000313| | x::xs, [] -> (*[21098]*)add_all_params xs vs (add x VL.nil (Table map)) (* unsupplied args are nil *)
000314| | x::xs, v::vs -> (*[13126]*)add_all_params xs vs (add x v (Table map))
000315|
000316|
000317| (** {2 Pretty printing} *)
000318|
000319| (* pprint : PL -> unit *)
000320| let pprint plat =
000321| (* string_of_tablekey : tablekey -> string *)
000322| let string_of_tablekey k = match k with
000323| | TableKey.Metatable -> "metatable"
000324| | TableKey.String s -> "\"" ^ s ^ "\""
000325| in
000326| (* pprint_list : int list -> unit *)
000327| let pprint_list is =
000328| begin
000329| Format.printf "[ @[<h 1>";
000330| ignore (List.fold_left (fun first i ->
000331| if first
000332| then (Format.print_int i; false)
000333| else
000334| begin
000335| Format.printf ",";
000336| Format.print_space ();
000337| Format.print_int i;
000338| false
000339| end) true is);
000340| Format.printf "@] ]";
000341| end in
000342| match plat with
000343| | Bot -> Format.printf "bot"
000344| | Table plat ->
000345| begin
000346| Format.printf "{ @[<v 0>";
000347| (* print table *)
000348| let first =
000349| TableMap.fold (fun prop (vlat,abs) first ->
000350| if first
000351| then
000352| begin
000353| Format.printf "%-12s -> " (string_of_tablekey prop);
000354| Abs.pprint abs;
000355| Format.printf " ";
000356| VL.pprint vlat;
000357| false
000358| end
000359| else
000360| begin
000361| Format.print_space ();
000362| Format.printf "%-12s -> " (string_of_tablekey prop);
000363| Abs.pprint abs; Format.printf " "; VL.pprint vlat;
000364| false
000365| end) plat.table true in
000366| (* prettyprint default str *)
000367| let first =
000368| (if VL.is_bot plat.default_str
000369| then first
000370| else
000371| begin
000372| (if first then () else Format.print_space ());
000373| Format.printf "%-12s -> " "default str";
000374| VL.pprint plat.default_str;
000375| false
000376| end) in
000377| (* prettyprint default key and default *)
000378| (if VL.is_bot plat.default_key
000379| then ()
000380| else
000381| begin
000382| (if first then () else Format.print_space ());
000383| Format.printf "%-12s -> " "default key";
000384| VL.pprint plat.default_key;
000385| Format.print_space ();
000386| Format.printf "%-12s -> " "default";
000387| VL.pprint plat.default
000388| end);
000389| (* prettyprint scopechain *)
000390| (if plat.scopechain = ScopeChainSet.empty
000391| then ()
000392| else
000393| begin
000394| (if first then () else Format.print_space ());
000395| Format.printf "%-12s -> " "scopechain";
000396| Format.printf "{ @[<h 1>";
000397| ignore (ScopeChainSet.fold (fun sc first ->
000398| if first
000399| then (pprint_list sc; false)
000400| else
000401| begin
000402| Format.printf ",";
000403| Format.print_space ();
000404| pprint_list sc;
000405| false
000406| end) plat.scopechain true);
000407| Format.printf "@] }";
000408| end);
000409| Format.printf "@] }";
000410| end
000411|
000412| (* to_string : elem -> string *)
000413| let to_string pl =
000414| let buf = Buffer.create 128 in
000415| let out,flush = Format.get_formatter_output_functions () in (* save previous outputters *)
000416| begin
000417| Format.set_formatter_output_functions (Buffer.add_substring buf) (fun () -> ());
000418| pprint pl;
000419| Format.print_flush ();
000420| Format.set_formatter_output_functions out flush; (* restore previous outputters *)
000421| Buffer.contents buf
000422| end