From fa571100dfc7d6ba8eec5a2dc213d7304975a4e1 Mon Sep 17 00:00:00 2001 From: Sungkeun Cho Date: Wed, 6 Nov 2019 05:45:31 -0800 Subject: [PATCH] [inferbo] Extend alias domain to have multiple aliases on a variable Summary: This diff extends the alias domain, so each variable can have multiple aliases. It changed `KeyLhs` can be mapped to multiple alias targets in the `AliasMap` domain: ``` before : KeyLhs.t -> KeyRhs.t * AliasTarget.t after : KeyLhs.t -> KeyRhs.t -> AliasTarget.t ``` Reviewed By: ezgicicek Differential Revision: D18062178 fbshipit-source-id: b325a6055 --- infer/src/absint/AbstractDomain.mli | 2 - .../bufferoverrun/bufferOverrunAnalysis.ml | 9 +- .../src/bufferoverrun/bufferOverrunDomain.ml | 380 +++++++++--------- .../src/bufferoverrun/bufferOverrunModels.ml | 3 +- .../bufferoverrun/bufferOverrunSemantics.ml | 125 +++--- .../java/bufferoverrun/ArrayListTest.java | 24 ++ .../java/bufferoverrun/issues.exp | 1 + .../codetoanalyze/java/performance/issues.exp | 10 +- 8 files changed, 280 insertions(+), 274 deletions(-) diff --git a/infer/src/absint/AbstractDomain.mli b/infer/src/absint/AbstractDomain.mli index 1d42e8611..da4003043 100644 --- a/infer/src/absint/AbstractDomain.mli +++ b/infer/src/absint/AbstractDomain.mli @@ -79,8 +79,6 @@ module TopLifted (Domain : S) : WithTop with type t = Domain.t top_lifted module TopLiftedUtils : sig val pp_top : Format.formatter -> unit - - val pp : pp:(Format.formatter -> 'a -> unit) -> Format.formatter -> 'a top_lifted -> unit end (** Cartesian product of two domains. *) diff --git a/infer/src/bufferoverrun/bufferOverrunAnalysis.ml b/infer/src/bufferoverrun/bufferOverrunAnalysis.ml index 4dc6d8cb0..41cec6718 100644 --- a/infer/src/bufferoverrun/bufferOverrunAnalysis.ml +++ b/infer/src/bufferoverrun/bufferOverrunAnalysis.ml @@ -113,9 +113,12 @@ module TransferFunctions = struct None with Caml.Not_found -> None ) in - Option.value_map (Dom.Mem.find_ret_alias callee_exit_mem) ~default:mem ~f:(fun tgt -> - Option.value_map (Dom.RhsAliasTarget.subst tgt ~subst_loc) ~default:mem - ~f:(fun (rhs, ret_alias) -> Dom.Mem.load_alias ret_id rhs ret_alias mem) ) + match Dom.Mem.find_ret_alias callee_exit_mem with + | Bottom -> + mem + | NonBottom tgts -> + let ret_alias = Dom.AliasTargets.subst tgts ~subst_loc in + Dom.AliasTargets.fold (Dom.Mem.load_alias ret_id) ret_alias mem in let ret_var = Loc.of_var (Var.of_id ret_id) in let ret_val = diff --git a/infer/src/bufferoverrun/bufferOverrunDomain.ml b/infer/src/bufferoverrun/bufferOverrunDomain.ml index f9c5cdbba..5e1e1e6f3 100644 --- a/infer/src/bufferoverrun/bufferOverrunDomain.ml +++ b/infer/src/bufferoverrun/bufferOverrunDomain.ml @@ -874,6 +874,10 @@ module AliasTarget = struct | Top [@@deriving compare] + let top = Top + + let is_top = function Top -> true | _ -> false + let equal = [%compare.equal: t] let pp_with_key ~pp_lhs ~pp_rhs = @@ -902,6 +906,11 @@ module AliasTarget = struct F.fprintf fmt "%t=?%t" pp_lhs pp_rhs + let pp = + let pp_underscore fmt = F.pp_print_string fmt "_" in + pp_with_key ~pp_lhs:pp_underscore ~pp_rhs:pp_underscore + + let get_locs = function | Simple {java_tmp= Some tmp} | Size {java_tmp= Some tmp} @@ -1041,95 +1050,58 @@ end module KeyRhs = Loc -module RhsAliasTarget = struct - type non_top = KeyRhs.t * AliasTarget.t - - type t = non_top top_lifted - - let top = Top - - let is_top = function AbstractDomain.Types.Top -> true | AbstractDomain.Types.NonTop _ -> false - - let leq ~lhs ~rhs = - match (lhs, rhs) with - | _, Top -> - true - | Top, _ -> - false - | NonTop (k1, v1), NonTop (k2, v2) -> - KeyRhs.equal k1 k2 && AliasTarget.leq ~lhs:v1 ~rhs:v2 - - - let equal x y = leq ~lhs:x ~rhs:y && leq ~lhs:y ~rhs:x - - let join x y = - match (x, y) with - | _, Top | Top, _ -> - Top - | NonTop (k1, v1), NonTop (k2, v2) -> - if KeyRhs.equal k1 k2 then NonTop (k1, AliasTarget.join v1 v2) else Top - - - let widen ~prev ~next ~num_iters = - match (prev, next) with - | _, Top | Top, _ -> - Top - | NonTop (k1, v1), NonTop (k2, v2) -> - if KeyRhs.equal k1 k2 then NonTop (k1, AliasTarget.widen ~prev:v1 ~next:v2 ~num_iters) - else Top - +module AliasTargets = struct + include AbstractDomain.SafeInvertedMap (KeyRhs) (AliasTarget) let pp_with_lhs ~pp_lhs fmt x = - AbstractDomain.TopLiftedUtils.pp fmt x ~pp:(fun fmt (rhs, v) -> - AliasTarget.pp_with_key ~pp_lhs ~pp_rhs:(fun fmt -> KeyRhs.pp fmt rhs) fmt v ) + let pp_sep fmt () = F.fprintf fmt ", @," in + let pp1 fmt (rhs, v) = + AliasTarget.pp_with_key ~pp_lhs ~pp_rhs:(fun fmt -> KeyRhs.pp fmt rhs) fmt v + in + F.pp_print_list ~pp_sep pp1 fmt (bindings x) let pp = pp_with_lhs ~pp_lhs:(fun fmt -> F.pp_print_string fmt "_") - let lift_map ~f = function Top -> Top | NonTop x -> NonTop (f x) - - let lift_map2 ~f = function Top -> Top | NonTop x -> f x + let forget l x = + let not_use_l k v = not (KeyRhs.equal l k || AliasTarget.use_loc l v) in + filter not_use_l x - let forget l = - lift_map2 ~f:(fun ((rhs, v) as x) -> - if not (KeyRhs.equal l rhs || AliasTarget.use_loc l v) then NonTop x else Top ) + let forget_size_alias arr_locs x = + let not_in_arr_locs k v = not (PowLoc.mem k arr_locs && AliasTarget.is_size v) in + filter not_in_arr_locs x - let forget_size_alias arr_locs = - lift_map2 ~f:(fun ((rhs, v) as x) -> - if not (PowLoc.mem rhs arr_locs && AliasTarget.is_size v) then NonTop x else Top ) + let incr_size_alias loc x = update loc (Option.map ~f:AliasTarget.incr_size_alias) x - let incr_size_alias loc = - lift_map ~f:(fun ((rhs, v) as x) -> - if Loc.equal loc rhs then (rhs, AliasTarget.incr_size_alias v) else x ) + let incr_or_not_size_alias loc x = + update loc (Option.map ~f:AliasTarget.incr_or_not_size_alias) x - let incr_or_not_size_alias loc = - lift_map ~f:(fun ((rhs, v) as x) -> - if Loc.equal loc rhs then (rhs, AliasTarget.incr_or_not_size_alias v) else x ) - - - let subst ~subst_loc (rhs, tgt) = - Option.map (subst_loc rhs) ~f:(fun rhs -> (rhs, AliasTarget.loc_map tgt ~f:subst_loc)) - - - let exists2 f (rhs1, v1) (rhs2, v2) = f rhs1 v1 rhs2 v2 - - let is_simple_zero_alias = function - | rhs, AliasTarget.Simple {i} when IntLit.iszero i -> - Some rhs - | _ -> - None + let subst ~subst_loc x = + let accum_substed rhs tgt acc = + Option.value_map (subst_loc rhs) ~default:acc ~f:(fun rhs -> + add rhs (AliasTarget.loc_map tgt ~f:subst_loc) acc ) + in + fold accum_substed x empty - let set_java_tmp loc = lift_map ~f:(fun (k, v) -> (k, AliasTarget.set_java_tmp loc v)) + let exists2 f x y = exists (fun k v -> exists (f k v) y) x - let get_non_top = function Some (NonTop x) -> Some x | _ -> None + let find_first_simple_zero_alias x = + let exception Found of KeyRhs.t in + let is_simple_zero rhs = function + | AliasTarget.Simple {i} when IntLit.iszero i -> + raise (Found rhs) + | _ -> + () + in + match iter is_simple_zero x with () -> None | exception Found rhs -> Some rhs end module AliasMap = struct - module M = AbstractDomain.SafeInvertedMap (KeyLhs) (RhsAliasTarget) + module M = AbstractDomain.SafeInvertedMap (KeyLhs) (AliasTargets) type t = M.t @@ -1142,9 +1114,7 @@ module AliasMap = struct let pp : F.formatter -> t -> unit = fun fmt x -> let pp_sep fmt () = F.fprintf fmt ", @," in - let pp1 fmt (lhs, v) = - RhsAliasTarget.pp_with_lhs ~pp_lhs:(fun fmt -> KeyLhs.pp fmt lhs) fmt v - in + let pp1 fmt (lhs, v) = AliasTargets.pp_with_lhs ~pp_lhs:(fun fmt -> KeyLhs.pp fmt lhs) fmt v in F.pp_print_list ~pp_sep pp1 fmt (M.bindings x) @@ -1152,114 +1122,142 @@ module AliasMap = struct let is_empty = M.is_empty - let add_alias ~lhs tgt m = M.add lhs (NonTop tgt) m + let add_alias ~lhs ~rhs v m = + let add_to_tgts = function + | None -> + Some (AliasTargets.singleton rhs v) + | Some tgts -> + Some (AliasTargets.add rhs v tgts) + in + M.update lhs add_to_tgts m + + + let add_aliases ~lhs tgts m = + AliasTargets.fold (fun rhs v acc -> add_alias ~lhs ~rhs v acc) tgts m + let remove = M.remove - let find_id : Ident.t -> t -> RhsAliasTarget.non_top option = - fun id x -> M.find_opt (KeyLhs.of_id id) x |> RhsAliasTarget.get_non_top + let find k m = M.find_opt k m |> Option.value ~default:AliasTargets.empty + let find_id : Ident.t -> t -> AliasTargets.t = fun id x -> find (KeyLhs.of_id id) x - let find_loc : Loc.t -> t -> RhsAliasTarget.non_top option = - fun loc x -> - M.find_opt (KeyLhs.LocKey loc) x - |> Option.map ~f:(RhsAliasTarget.set_java_tmp loc) - |> RhsAliasTarget.get_non_top + let find_loc : Loc.t -> t -> AliasTargets.t = + fun loc x -> find (KeyLhs.LocKey loc) x |> AliasTargets.map (AliasTarget.set_java_tmp loc) let load : Ident.t -> Loc.t -> AliasTarget.t -> t -> t = fun id loc tgt x -> if Loc.is_unknown loc || AliasTarget.is_unknown tgt then x else - let tgt = + let tgts = match tgt with | AliasTarget.Simple {i} when IntLit.iszero i && Language.curr_language_is Java -> - Option.value (find_loc loc x) ~default:(loc, tgt) + find_loc loc x |> AliasTargets.add loc tgt | _ -> - (loc, tgt) + AliasTargets.singleton loc tgt in - add_alias ~lhs:(KeyLhs.of_id id) tgt x + add_aliases ~lhs:(KeyLhs.of_id id) tgts x let forget : Loc.t -> t -> t = fun l x -> - let forget1 k v = - if KeyLhs.use_loc l k then RhsAliasTarget.top else RhsAliasTarget.forget l v - in + let forget1 k v = if KeyLhs.use_loc l k then AliasTargets.top else AliasTargets.forget l v in M.mapi forget1 x let store : Loc.t -> Ident.t -> t -> t = fun l id x -> if Language.curr_language_is Java then - if Loc.is_frontend_tmp l then - Option.value_map (find_id id x) ~default:x ~f:(fun tgt -> - add_alias ~lhs:(KeyLhs.of_loc l) tgt x ) + let tgts = find_id id x in + if Loc.is_frontend_tmp l then add_aliases ~lhs:(KeyLhs.of_loc l) tgts x else - match find_id id x with - | Some (rhs, AliasTarget.Simple {i}) when IntLit.iszero i && Loc.is_frontend_tmp rhs -> - add_alias ~lhs:(KeyLhs.of_id id) (l, AliasTarget.Simple {i; java_tmp= Some rhs}) x - |> add_alias ~lhs:(KeyLhs.of_loc rhs) (l, AliasTarget.Simple {i; java_tmp= None}) - | _ -> - x + let accum_java_tmp_alias rhs tgt acc = + match tgt with + | AliasTarget.Simple {i} when IntLit.iszero i && Loc.is_frontend_tmp rhs -> + add_alias ~lhs:(KeyLhs.of_id id) ~rhs:l + (AliasTarget.Simple {i; java_tmp= Some rhs}) + acc + |> add_alias ~lhs:(KeyLhs.of_loc rhs) ~rhs:l (AliasTarget.Simple {i; java_tmp= None}) + | _ -> + acc + in + AliasTargets.fold accum_java_tmp_alias tgts x else x let add_zero_size_alias ~size ~arr x = - add_alias ~lhs:(KeyLhs.of_loc size) - (arr, AliasTarget.Size {alias_typ= Eq; i= IntLit.zero; java_tmp= None}) + add_alias ~lhs:(KeyLhs.of_loc size) ~rhs:arr + (AliasTarget.Size {alias_typ= Eq; i= IntLit.zero; java_tmp= None}) x - let incr_size_alias loc x = M.map (RhsAliasTarget.incr_size_alias loc) x + let incr_size_alias loc x = M.map (AliasTargets.incr_size_alias loc) x - let incr_or_not_size_alias loc x = M.map (RhsAliasTarget.incr_or_not_size_alias loc) x + let incr_or_not_size_alias loc x = M.map (AliasTargets.incr_or_not_size_alias loc) x - let forget_size_alias arr_locs x = M.map (RhsAliasTarget.forget_size_alias arr_locs) x + let forget_size_alias arr_locs x = M.map (AliasTargets.forget_size_alias arr_locs) x let store_n ~prev loc id n x = - match find_id id prev with - | Some (rhs, AliasTarget.Size {alias_typ; i}) -> - add_alias ~lhs:(KeyLhs.of_loc loc) - (rhs, AliasTarget.Size {alias_typ; i= IntLit.add i n; java_tmp= None}) - x - | _ -> - x + let accum_size_alias rhs tgt acc = + match tgt with + | AliasTarget.Size {alias_typ; i} -> + add_alias ~lhs:(KeyLhs.of_loc loc) ~rhs + (AliasTarget.Size {alias_typ; i= IntLit.add i n; java_tmp= None}) + acc + | _ -> + acc + in + AliasTargets.fold accum_size_alias (find_id id prev) x let add_iterator_offset_alias id arr x = - add_alias ~lhs:(KeyLhs.of_id id) - (arr, AliasTarget.IteratorOffset {alias_typ= Eq; i= IntLit.zero; java_tmp= None}) + add_alias ~lhs:(KeyLhs.of_id id) ~rhs:arr + (AliasTarget.IteratorOffset {alias_typ= Eq; i= IntLit.zero; java_tmp= None}) x let incr_iterator_offset_alias id x = + let accum_incr_iterator_offset_alias rhs tgt acc = + match tgt with + | AliasTarget.IteratorOffset ({i; java_tmp} as tgt) -> + let i = IntLit.(add i one) in + let acc = + add_alias ~lhs:(KeyLhs.of_id id) ~rhs (AliasTarget.IteratorOffset {tgt with i}) acc + in + Option.value_map java_tmp ~default:x ~f:(fun java_tmp -> + add_alias ~lhs:(KeyLhs.of_loc java_tmp) ~rhs + (AliasTarget.IteratorOffset {tgt with i; java_tmp= None}) + acc ) + | _ -> + acc + in match M.find_opt (KeyLhs.of_id id) x with - | Some (NonTop (rhs, AliasTarget.IteratorOffset ({i; java_tmp} as tgt))) -> - let i = IntLit.(add i one) in - let x = - add_alias ~lhs:(KeyLhs.of_id id) (rhs, AliasTarget.IteratorOffset {tgt with i}) x - in - Option.value_map java_tmp ~default:x ~f:(fun java_tmp -> - add_alias ~lhs:(KeyLhs.of_loc java_tmp) - (rhs, AliasTarget.IteratorOffset {tgt with i; java_tmp= None}) - x ) + | Some tgts -> + AliasTargets.fold accum_incr_iterator_offset_alias tgts x | _ -> x let add_iterator_has_next_alias ~ret_id ~iterator x = + let accum_has_next_alias _rhs tgt acc = + match tgt with + | AliasTarget.IteratorOffset {java_tmp= Some java_tmp} -> + add_alias ~lhs:(KeyLhs.of_id ret_id) ~rhs:java_tmp + (AliasTarget.IteratorHasNext {java_tmp= None}) + acc + | _ -> + acc + in match M.find_opt (KeyLhs.of_id iterator) x with - | Some (NonTop (_rhs, AliasTarget.IteratorOffset {java_tmp= Some java_tmp})) -> - add_alias ~lhs:(KeyLhs.of_id ret_id) - (java_tmp, AliasTarget.IteratorHasNext {java_tmp= None}) - x + | Some tgts -> + AliasTargets.fold accum_has_next_alias tgts x | _ -> x end module AliasRet = struct - include AbstractDomain.Flat (RhsAliasTarget) + include AliasTargets let pp : F.formatter -> t -> unit = fun fmt x -> F.pp_print_string fmt "ret=" ; pp fmt x end @@ -1289,23 +1287,17 @@ module Alias = struct AliasRet.pp x.ret - let bot : t = {map= AliasMap.empty; ret= AliasRet.bottom} + let init : t = {map= AliasMap.empty; ret= AliasRet.empty} let lift_map : (AliasMap.t -> AliasMap.t) -> t -> t = fun f a -> {a with map= f a.map} let bind_map : (AliasMap.t -> 'a) -> t -> 'a = fun f a -> f a.map - let find_id : Ident.t -> t -> RhsAliasTarget.non_top option = - fun x -> bind_map (AliasMap.find_id x) - + let find_id : Ident.t -> t -> AliasTargets.t = fun x -> bind_map (AliasMap.find_id x) - let find_loc : Loc.t -> t -> RhsAliasTarget.non_top option = - fun x -> bind_map (AliasMap.find_loc x) - - - let find_ret : t -> RhsAliasTarget.non_top option = - fun x -> AliasRet.get x.ret |> RhsAliasTarget.get_non_top + let find_loc : Loc.t -> t -> AliasTargets.t = fun x -> bind_map (AliasMap.find_loc x) + let find_ret : t -> AliasTargets.t = fun x -> x.ret let load : Ident.t -> Loc.t -> AliasTarget.t -> t -> t = fun id loc tgt -> lift_map (AliasMap.load id loc tgt) @@ -1317,10 +1309,7 @@ module Alias = struct match e with | Exp.Var l -> let a = lift_map (AliasMap.store loc l) a in - if Loc.is_return loc then - let update_ret retl = {a with ret= AliasRet.v (NonTop retl)} in - Option.value_map (find_id l a) ~default:a ~f:update_ret - else a + if Loc.is_return loc then {a with ret= find_id l a} else a | Exp.BinOp (Binop.PlusA _, Exp.Var id, Exp.Const (Const.Cint i)) | Exp.BinOp (Binop.PlusA _, Exp.Const (Const.Cint i), Exp.Var id) -> lift_map (AliasMap.load id loc (AliasTarget.Simple {i= IntLit.neg i; java_tmp= None})) a @@ -1356,31 +1345,18 @@ module Alias = struct let add_empty_size_alias : Loc.t -> PowLoc.t -> t -> t = fun loc arr_locs prev -> - let a = lift_map (AliasMap.forget loc) prev in - match PowLoc.is_singleton_or_more arr_locs with - | IContainer.Singleton arr_loc -> - lift_map (AliasMap.add_zero_size_alias ~size:loc ~arr:arr_loc) a - | More -> - (* NOTE: Keeping only one alias here is suboptimal, but current alias domain can keep one - alias for each ident, which will be extended later. *) - let arr_loc = PowLoc.min_elt arr_locs in - lift_map (AliasMap.add_zero_size_alias ~size:loc ~arr:arr_loc) a - | Empty -> - a + let accum_empty_size_alias arr_loc acc = + lift_map (AliasMap.add_zero_size_alias ~size:loc ~arr:arr_loc) acc + in + PowLoc.fold accum_empty_size_alias arr_locs (lift_map (AliasMap.forget loc) prev) let add_iterator_offset_alias : Ident.t -> PowLoc.t -> t -> t = fun id arr_locs a -> - match PowLoc.is_singleton_or_more arr_locs with - | IContainer.Singleton arr_loc -> - lift_map (AliasMap.add_iterator_offset_alias id arr_loc) a - | More -> - (* NOTE: Keeping only one alias here is suboptimal, but current alias domain can keep one - alias for each ident, which will be extended later. *) - let arr_loc = PowLoc.min_elt arr_locs in - lift_map (AliasMap.add_iterator_offset_alias id arr_loc) a - | Empty -> - a + let accum_iterator_offset_alias arr_loc acc = + lift_map (AliasMap.add_iterator_offset_alias id arr_loc) acc + in + PowLoc.fold accum_iterator_offset_alias arr_locs a let incr_iterator_offset_alias : Ident.t -> t -> t = @@ -1798,7 +1774,7 @@ module MemReach = struct fun oenv -> { stack_locs= StackLocs.bot ; mem_pure= MemPure.bot - ; alias= Alias.bot + ; alias= Alias.init ; latest_prune= LatestPrune.top ; relation= Relation.empty ; oenv= GOption.GSome oenv } @@ -1877,33 +1853,29 @@ module MemReach = struct PowLoc.fold find_join locs Val.bot - let find_alias_id : Ident.t -> _ t0 -> RhsAliasTarget.non_top option = - fun k m -> Alias.find_id k m.alias - + let find_alias_id : Ident.t -> _ t0 -> AliasTargets.t = fun k m -> Alias.find_id k m.alias - let find_alias_loc : Loc.t -> _ t0 -> RhsAliasTarget.non_top option = - fun k m -> Alias.find_loc k m.alias + let find_alias_loc : Loc.t -> _ t0 -> AliasTargets.t = fun k m -> Alias.find_loc k m.alias - - let find_simple_alias : Ident.t -> _ t0 -> (Loc.t * IntLit.t) option = - fun k m -> - match Alias.find_id k m.alias with - | Some (l, AliasTarget.Simple {i}) -> - Some (l, i) - | _ -> - None + let find_simple_alias : Ident.t -> _ t0 -> (Loc.t * IntLit.t) list = + let accum_simple_alias l tgt acc = + match tgt with AliasTarget.Simple {i} -> (l, i) :: acc | _ -> acc + in + fun k m -> AliasTargets.fold accum_simple_alias (Alias.find_id k m.alias) [] - let find_size_alias : Ident.t -> _ t0 -> (AliasTarget.alias_typ * Loc.t * Loc.t option) option = - fun k m -> - match Alias.find_id k m.alias with - | Some (l, AliasTarget.Size {alias_typ; java_tmp}) -> - Some (alias_typ, l, java_tmp) - | _ -> - None + let find_size_alias : Ident.t -> _ t0 -> (AliasTarget.alias_typ * Loc.t * Loc.t option) list = + let accum_size_alias l tgt acc = + match tgt with + | AliasTarget.Size {alias_typ; java_tmp} -> + (alias_typ, l, java_tmp) :: acc + | _ -> + acc + in + fun k m -> AliasTargets.fold accum_size_alias (Alias.find_id k m.alias) [] - let find_ret_alias : _ t0 -> RhsAliasTarget.non_top option = fun m -> Alias.find_ret m.alias + let find_ret_alias : _ t0 -> AliasTargets.t = fun m -> Alias.find_ret m.alias let load_alias : Ident.t -> Loc.t -> AliasTarget.t -> t -> t = fun id loc tgt m -> {m with alias= Alias.load id loc tgt m.alias} @@ -2060,8 +2032,7 @@ module MemReach = struct | _ -> acc in - let default = (m, PrunePairs.empty) in - Option.value_map ~default (find_simple_alias r m) ~f:(apply_simple_alias1 default) + List.fold (find_simple_alias r m) ~init:(m, PrunePairs.empty) ~f:apply_simple_alias1 | LatestPrune.VRet (x, prunes, _), Exp.Var r | LatestPrune.VRet (x, _, prunes), Exp.UnOp (Unop.LNot, Exp.Var r, _) -> if Ident.equal x r then (apply_prunes prunes m, prunes) else (m, PrunePairs.empty) @@ -2076,12 +2047,16 @@ module MemReach = struct if IntLit.isone i then {m with latest_prune= LatestPrune.TrueBranch (x, p)} else if IntLit.iszero i then {m with latest_prune= LatestPrune.FalseBranch (x, p)} else {m with latest_prune= LatestPrune.forget updated_locs m.latest_prune} - | Lvar return, _, _ when Pvar.is_return return -> ( - match Alias.find_ret m.alias with - | Some (Loc.Var (ProgramVar pvar), AliasTarget.Simple {i}) when IntLit.iszero i -> - {m with latest_prune= LatestPrune.replace ~from:pvar ~to_:return m.latest_prune} - | _ -> - m ) + | Lvar return, _, _ when Pvar.is_return return -> + let tgts = Alias.find_ret m.alias in + let replace_latest_prune l tgt acc = + match (l, tgt) with + | Loc.Var (ProgramVar pvar), AliasTarget.Simple {i} when IntLit.iszero i -> + {acc with latest_prune= LatestPrune.replace ~from:pvar ~to_:return m.latest_prune} + | _ -> + acc + in + AliasTargets.fold replace_latest_prune tgts m | _, _, _ -> {m with latest_prune= LatestPrune.forget updated_locs m.latest_prune} @@ -2294,24 +2269,29 @@ module Mem = struct fun k -> f_lift_default ~default:None (MemReach.find_opt k) - let find_alias_id : Ident.t -> _ t0 -> RhsAliasTarget.non_top option = - fun k -> f_lift_default ~default:None (MemReach.find_alias_id k) + let find_alias_id : Ident.t -> _ t0 -> AliasTargets.t = + fun k -> f_lift_default ~default:AliasTargets.empty (MemReach.find_alias_id k) - let find_alias_loc : Loc.t -> _ t0 -> RhsAliasTarget.non_top option = - fun k -> f_lift_default ~default:None (MemReach.find_alias_loc k) + let find_alias_loc : Loc.t -> _ t0 -> AliasTargets.t = + fun k -> f_lift_default ~default:AliasTargets.empty (MemReach.find_alias_loc k) - let find_simple_alias : Ident.t -> _ t0 -> (Loc.t * IntLit.t) option = - fun k -> f_lift_default ~default:None (MemReach.find_simple_alias k) + let find_simple_alias : Ident.t -> _ t0 -> (Loc.t * IntLit.t) list = + fun k -> f_lift_default ~default:[] (MemReach.find_simple_alias k) - let find_size_alias : Ident.t -> _ t0 -> (AliasTarget.alias_typ * Loc.t * Loc.t option) option = - fun k -> f_lift_default ~default:None (MemReach.find_size_alias k) + let find_size_alias : Ident.t -> _ t0 -> (AliasTarget.alias_typ * Loc.t * Loc.t option) list = + fun k -> f_lift_default ~default:[] (MemReach.find_size_alias k) - let find_ret_alias : _ t0 -> RhsAliasTarget.non_top option = - fun m -> match m with Bottom | ExcRaised -> None | NonBottom m' -> MemReach.find_ret_alias m' + let find_ret_alias : _ t0 -> AliasTargets.t bottom_lifted = + fun m -> + match m with + | Bottom | ExcRaised -> + Bottom + | NonBottom m' -> + NonBottom (MemReach.find_ret_alias m') let load_alias : Ident.t -> Loc.t -> AliasTarget.t -> t -> t = diff --git a/infer/src/bufferoverrun/bufferOverrunModels.ml b/infer/src/bufferoverrun/bufferOverrunModels.ml index ddf3a2797..c5bd504c2 100644 --- a/infer/src/bufferoverrun/bufferOverrunModels.ml +++ b/infer/src/bufferoverrun/bufferOverrunModels.ml @@ -113,8 +113,7 @@ let malloc ~can_be_zero size_exp = let traces = Trace.(Set.add_elem location ArrayDeclaration) (Dom.Val.get_traces length) in let path = Dom.Mem.find_simple_alias id mem - |> Option.value_map ~default:None ~f:(fun (rhs, i) -> - if IntLit.iszero i then Loc.get_path rhs else None ) + |> List.find_map ~f:(fun (rhs, i) -> if IntLit.iszero i then Loc.get_path rhs else None) in let offset, size = (Itv.zero, Dom.Val.get_itv length) in let represents_multiple_values = not (Itv.is_one size) in diff --git a/infer/src/bufferoverrun/bufferOverrunSemantics.ml b/infer/src/bufferoverrun/bufferOverrunSemantics.ml index 515e2fb91..cd01880e8 100644 --- a/infer/src/bufferoverrun/bufferOverrunSemantics.ml +++ b/infer/src/bufferoverrun/bufferOverrunSemantics.ml @@ -27,15 +27,11 @@ let eval_const : Typ.IntegerWidths.t -> Const.t -> Val.t = let rec must_alias : Exp.t -> Exp.t -> Mem.t -> bool = fun e1 e2 m -> match (e1, e2) with - | Exp.Var x1, Exp.Var x2 -> ( + | Exp.Var x1, Exp.Var x2 -> let same_alias rhs1 tgt1 rhs2 tgt2 = KeyRhs.equal rhs1 rhs2 && AliasTarget.equal tgt1 tgt2 && not (Mem.is_rep_multi_loc rhs1 m) in - match (Mem.find_alias_id x1 m, Mem.find_alias_id x2 m) with - | Some x1', Some x2' -> - RhsAliasTarget.exists2 same_alias x1' x2' - | _, _ -> - false ) + AliasTargets.exists2 same_alias (Mem.find_alias_id x1 m) (Mem.find_alias_id x2 m) | Exp.UnOp (uop1, e1', _), Exp.UnOp (uop2, e2', _) -> Unop.equal uop1 uop2 && must_alias e1' e2' m | Exp.BinOp (bop1, e11, e12), Exp.BinOp (bop2, e21, e22) -> @@ -314,13 +310,9 @@ let rec eval_locs : Exp.t -> Mem.t -> PowLoc.t = let rec eval_arr : Typ.IntegerWidths.t -> Exp.t -> Mem.t -> Val.t = fun integer_type_widths exp mem -> match exp with - | Exp.Var id -> ( - match Mem.find_alias_id id mem with - | Some tgt -> - let alias_loc = RhsAliasTarget.is_simple_zero_alias tgt in - Option.value_map alias_loc ~default:Val.bot ~f:(fun loc -> Mem.find loc mem) - | None -> - Val.bot ) + | Exp.Var id -> + let alias_loc = AliasTargets.find_first_simple_zero_alias (Mem.find_alias_id id mem) in + Option.value_map alias_loc ~default:Val.bot ~f:(fun loc -> Mem.find loc mem) | Exp.Lvar pvar -> Mem.find (Loc.of_pvar pvar) mem | Exp.BinOp (bop, e1, e2) -> @@ -540,57 +532,66 @@ module Prune = struct let prune_has_next ~true_branch iterator ({mem} as astate) = - match Mem.find_alias_loc iterator mem with - | Some (arr_loc, AliasTarget.IteratorOffset {alias_typ; i}) when IntLit.(eq i zero) -> - let length = collection_length_of_iterator iterator mem |> Val.get_itv in - let v = Mem.find arr_loc mem in - let v = - let prune_f = - if true_branch then Val.prune_length_lt - else match alias_typ with Eq -> Val.prune_length_eq | Le -> Val.prune_length_le + let accum_pruned arr_loc tgt acc = + match tgt with + | AliasTarget.IteratorOffset {alias_typ; i} when IntLit.(eq i zero) -> + let length = collection_length_of_iterator iterator mem |> Val.get_itv in + let v = Mem.find arr_loc mem in + let v = + let prune_f = + if true_branch then Val.prune_length_lt + else match alias_typ with Eq -> Val.prune_length_eq | Le -> Val.prune_length_le + in + prune_f v length in - prune_f v length - in - update_mem_in_prune arr_loc v astate - | _ -> - astate + update_mem_in_prune arr_loc v acc + | _ -> + acc + in + AliasTargets.fold accum_pruned (Mem.find_alias_loc iterator mem) astate let prune_unop : Exp.t -> t -> t = fun e ({mem} as astate) -> match e with - | Exp.Var x -> ( - match Mem.find_alias_id x mem with - | Some (rhs, AliasTarget.Simple {i}) when IntLit.iszero i -> - let v = Mem.find rhs mem in - let v' = Val.prune_ne_zero v in - update_mem_in_prune rhs v' astate - | Some (rhs, AliasTarget.Empty) -> - let v = Mem.find rhs mem in - let v' = Val.prune_length_eq_zero v in - update_mem_in_prune rhs v' astate - | Some (rhs, AliasTarget.Fgets) -> - let strlen_loc = Loc.of_c_strlen rhs in - let v' = Val.prune_ge_one (Mem.find strlen_loc mem) in - update_mem_in_prune strlen_loc v' astate - | Some (rhs, AliasTarget.IteratorHasNext _) -> - prune_has_next ~true_branch:true rhs astate - | _ -> - astate ) - | Exp.UnOp (Unop.LNot, Exp.Var x, _) -> ( - match Mem.find_alias_id x mem with - | Some (rhs, AliasTarget.Simple {i}) when IntLit.iszero i -> - let v = Mem.find rhs mem in - let v' = Val.prune_eq_zero v in - update_mem_in_prune rhs v' astate - | Some (rhs, AliasTarget.Empty) -> - let v = Mem.find rhs mem in - let v' = Val.prune_length_ge_one v in - update_mem_in_prune rhs v' astate - | Some (rhs, AliasTarget.IteratorHasNext _) -> - prune_has_next ~true_branch:false rhs astate - | _ -> - astate ) + | Exp.Var x -> + let accum_prune_var rhs tgt acc = + match tgt with + | AliasTarget.Simple {i} when IntLit.iszero i -> + let v = Mem.find rhs mem in + let v' = Val.prune_ne_zero v in + update_mem_in_prune rhs v' acc + | AliasTarget.Empty -> + let v = Mem.find rhs mem in + let v' = Val.prune_length_eq_zero v in + update_mem_in_prune rhs v' acc + | AliasTarget.Fgets -> + let strlen_loc = Loc.of_c_strlen rhs in + let v' = Val.prune_ge_one (Mem.find strlen_loc mem) in + update_mem_in_prune strlen_loc v' acc + | AliasTarget.IteratorHasNext _ -> + prune_has_next ~true_branch:true rhs acc + | _ -> + acc + in + AliasTargets.fold accum_prune_var (Mem.find_alias_id x mem) astate + | Exp.UnOp (Unop.LNot, Exp.Var x, _) -> + let accum_prune_not_var rhs tgt acc = + match tgt with + | AliasTarget.Simple {i} when IntLit.iszero i -> + let v = Mem.find rhs mem in + let v' = Val.prune_eq_zero v in + update_mem_in_prune rhs v' acc + | AliasTarget.Empty -> + let v = Mem.find rhs mem in + let v' = Val.prune_length_ge_one v in + update_mem_in_prune rhs v' acc + | AliasTarget.IteratorHasNext _ -> + prune_has_next ~true_branch:false rhs acc + | _ -> + acc + in + AliasTargets.fold accum_prune_not_var (Mem.find_alias_id x mem) astate | _ -> astate @@ -632,7 +633,7 @@ module Prune = struct let prune_simple_alias = let prune_alias_core ~val_prune_eq ~val_prune_le:_ ~make_pruning_exp integer_type_widths x e ({mem} as astate) = - Option.value_map (Mem.find_simple_alias x mem) ~default:astate ~f:(fun (lv, i) -> + List.fold (Mem.find_simple_alias x mem) ~init:astate ~f:(fun acc (lv, i) -> let lhs = Mem.find lv mem in let rhs = let v' = eval integer_type_widths e mem in @@ -640,7 +641,7 @@ module Prune = struct in let v = val_prune_eq lhs rhs in let pruning_exp = make_pruning_exp ~lhs ~rhs in - update_mem_in_prune lv v ~pruning_exp astate ) + update_mem_in_prune lv v ~pruning_exp acc ) in gen_prune_alias_functions ~prune_alias_core @@ -648,8 +649,8 @@ module Prune = struct let prune_size_alias = let prune_alias_core ~val_prune_eq ~val_prune_le ~make_pruning_exp integer_type_widths x e ({mem} as astate) = - Option.value_map (Mem.find_size_alias x mem) ~default:astate - ~f:(fun (alias_typ, lv, java_tmp) -> + List.fold (Mem.find_size_alias x mem) ~init:astate + ~f:(fun astate (alias_typ, lv, java_tmp) -> let array_v = Mem.find lv mem in let size = Val.get_array_blk array_v |> ArrayBlk.sizeof |> Val.of_itv diff --git a/infer/tests/codetoanalyze/java/bufferoverrun/ArrayListTest.java b/infer/tests/codetoanalyze/java/bufferoverrun/ArrayListTest.java index 447342d20..c67e11184 100644 --- a/infer/tests/codetoanalyze/java/bufferoverrun/ArrayListTest.java +++ b/infer/tests/codetoanalyze/java/bufferoverrun/ArrayListTest.java @@ -159,4 +159,28 @@ class ArrayListTest { } // b.size is zero here int j = b.get(0); } + + void multi_adds_in_loop_iterator_ok(ArrayList b) { + ArrayList a1 = new ArrayList<>(); + ArrayList a2 = new ArrayList<>(); + for (Integer i : b) { + a1.add(i); + a2.add(i); + } + int j; + j = a1.get(b.size() - 1); + j = a2.get(b.size() - 1); + } + + void multi_adds_in_loop_iterator_bad(ArrayList b) { + ArrayList a1 = new ArrayList<>(); + ArrayList a2 = new ArrayList<>(); + for (Integer i : b) { + a1.add(i); + a2.add(i); + } + int j; + j = a1.get(b.size() + 1); + j = a2.get(b.size() + 1); + } } diff --git a/infer/tests/codetoanalyze/java/bufferoverrun/issues.exp b/infer/tests/codetoanalyze/java/bufferoverrun/issues.exp index 464aaa77d..4f2cda028 100644 --- a/infer/tests/codetoanalyze/java/bufferoverrun/issues.exp +++ b/infer/tests/codetoanalyze/java/bufferoverrun/issues.exp @@ -14,6 +14,7 @@ codetoanalyze/java/bufferoverrun/ArrayListTest.java, ArrayListTest.add_in_loop_b codetoanalyze/java/bufferoverrun/ArrayListTest.java, ArrayListTest.add_in_loop_iterator2_bad(java.util.ArrayList):void, 7, BUFFER_OVERRUN_L2, no_bucket, ERROR, [,Array declaration,Assignment,,Parameter `b.elements[*]`,Array access: Offset: [0, b.length] Size: b.length] codetoanalyze/java/bufferoverrun/ArrayListTest.java, ArrayListTest.add_in_loop_iterator_bad(java.util.ArrayList):void, 5, BUFFER_OVERRUN_L1, no_bucket, ERROR, [,Parameter `b.elements[*]`,Assignment,,Array declaration,Array access: Offset: b.length + 1 Size: b.length] codetoanalyze/java/bufferoverrun/ArrayListTest.java, ArrayListTest.alloc_is_negative_bad():void, 2, INFERBO_ALLOC_IS_NEGATIVE, no_bucket, ERROR, [Allocation: Length: -1] +codetoanalyze/java/bufferoverrun/ArrayListTest.java, ArrayListTest.multi_adds_in_loop_iterator_bad(java.util.ArrayList):void, 8, BUFFER_OVERRUN_L1, no_bucket, ERROR, [,Parameter `b.elements[*]`,Assignment,,Array declaration,Array access: Offset: b.length + 1 Size: b.length] codetoanalyze/java/bufferoverrun/ArrayListTest.java, ArrayListTest.remove_in_loop_iterator_good_FP(java.util.ArrayList):void, 14, BUFFER_OVERRUN_L1, no_bucket, ERROR, [,Parameter `b.elements[*]`,Assignment,,Parameter `b.elements[*]`,Array access: Offset: b.length Size: b.length] codetoanalyze/java/bufferoverrun/ArrayMember.java, codetoanalyze.java.bufferoverrun.ArrayMember.load_array_member_Bad():void, 4, BUFFER_OVERRUN_L1, no_bucket, ERROR, [,Parameter `this.buf[*]`,Assignment,,Array declaration,Array access: Offset: [max(10, this.buf[*].lb), min(10, this.buf[*].ub)] Size: 10] codetoanalyze/java/bufferoverrun/CompressedData.java, codetoanalyze.java.bufferoverrun.CompressedData.decompressData(codetoanalyze.java.bufferoverrun.CompressedData$D):int, 9, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Parameter `this.yy`,,Parameter `d.cci[*].s`,Assignment,Binary operation: ([0, this.yy - 1] × d.cci[*].s):signed32] diff --git a/infer/tests/codetoanalyze/java/performance/issues.exp b/infer/tests/codetoanalyze/java/performance/issues.exp index c5c3eae41..9e6933df8 100644 --- a/infer/tests/codetoanalyze/java/performance/issues.exp +++ b/infer/tests/codetoanalyze/java/performance/issues.exp @@ -30,17 +30,17 @@ codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.arraylist_remov codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.arraylist_set_overrun_bad():void, 3, BUFFER_OVERRUN_L1, no_bucket, ERROR, [,Array declaration,Through,Array access: Offset: 1 Size: 1] codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.arraylist_set_underrun_bad():void, 2, BUFFER_OVERRUN_L1, no_bucket, ERROR, [,Array declaration,Array access: Offset: 0 Size: 0] codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.boolean_control_var_linear():void, 1, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 2 + 19 ⋅ this.arr.length + 4 ⋅ (this.arr.length + 1), O(this.arr.length), degree = 1,{this.arr.length + 1},Loop at line 290,{this.arr.length},Loop at line 290] -codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.call_init_with_put_linear(java.util.ArrayList):void, 1, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 13 + 16 ⋅ (a.length + 1), O(a.length), degree = 1,{a.length + 1},call to HashMap ArrayListTest.init_with_put_linear(ArrayList),Loop at line 299] +codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.call_init_with_put_linear(java.util.ArrayList):void, 1, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 13 + 13 ⋅ a.length + 3 ⋅ (a.length + 1), O(a.length), degree = 1,{a.length + 1},call to HashMap ArrayListTest.init_with_put_linear(ArrayList),Loop at line 299,{a.length},call to HashMap ArrayListTest.init_with_put_linear(ArrayList),Loop at line 299] codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.call_sortArrayList(java.util.ArrayList):void, 1, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 4 + list.length × log(list.length), O(list.length × log(list.length)), degree = 1 + 1⋅log,{list.length},call to void ArrayListTest.sortArrayList(ArrayList),Modeled call to Collections.sort,{list.length},call to void ArrayListTest.sortArrayList(ArrayList),Modeled call to Collections.sort] codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.constructor_add_all(java.util.ArrayList,java.util.ArrayList):void, 4, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 10 + 5 ⋅ (l.length + list.length) + 3 ⋅ (l.length + list.length + 1), O((l.length + list.length)), degree = 1,{l.length + list.length + 1},Loop at line 240,{l.length + list.length},Loop at line 240] codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.constructor_add_all_sym(java.util.ArrayList,java.util.ArrayList):void, 4, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 10 + 5 ⋅ (l.length + list.length) + 3 ⋅ (l.length + list.length + 1), O((l.length + list.length)), degree = 1,{l.length + list.length + 1},Loop at line 247,{l.length + list.length},Loop at line 247] codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.constructor_linear(java.util.ArrayList):void, 2, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 6 + 5 ⋅ list.length + 3 ⋅ (list.length + 1), O(list.length), degree = 1,{list.length + 1},Loop at line 222,{list.length},Loop at line 222] codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.constructor_modify(java.util.ArrayList):void, 8, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 18 + 5 ⋅ (list.length + 4) + 3 ⋅ (list.length + 5), O(list.length), degree = 1,{list.length + 5},Loop at line 233,{list.length + 4},Loop at line 233] codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.empty_list_constant(int):void, 3, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here] -codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.init_with_put_linear(java.util.ArrayList):java.util.HashMap, 2, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 7 + 16 ⋅ (a.length + 1), O(a.length), degree = 1,{a.length + 1},Loop at line 299] +codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.init_with_put_linear(java.util.ArrayList):java.util.HashMap, 2, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 7 + 13 ⋅ a.length + 3 ⋅ (a.length + 1), O(a.length), degree = 1,{a.length + 1},Loop at line 299,{a.length},Loop at line 299] codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.iterate_over_arraylist(java.util.ArrayList):void, 1, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 5 + 5 ⋅ list.length, O(list.length), degree = 1,{list.length},Loop at line 15] codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.iterate_over_arraylist_shortcut_FP(java.util.ArrayList):boolean, 1, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 4 + 11 ⋅ list.length + 2 ⋅ list.length × (11-max(10, list.elements)) + 3 ⋅ (list.length + 1) × (11-max(10, list.elements)), O(list.length × (-list.elements + 11)), degree = 2,{11-max(10, list.elements)},Loop at line 188,{list.length + 1},Loop at line 188,{11-max(10, list.elements)},Loop at line 188,{list.length},Loop at line 188] -codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.iterate_over_arraylist_with_inner(java.util.ArrayList):void, 3, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 7 + 14 ⋅ (list1.length + 1), O(list1.length), degree = 1,{list1.length + 1},Loop at line 179] +codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.iterate_over_arraylist_with_inner(java.util.ArrayList):void, 3, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 7 + 11 ⋅ list1.length + 3 ⋅ (list1.length + 1), O(list1.length), degree = 1,{list1.length + 1},Loop at line 179,{list1.length},Loop at line 179] codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.iterate_over_local_arraylist(java.util.ArrayList):void, 2, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 7 + 5 ⋅ list.length, O(list.length), degree = 1,{list.length},Loop at line 20] codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.iterate_while_has_next(java.util.ArrayList):void, 3, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 4 + 10 ⋅ list.length + 3 ⋅ (list.length + 1), O(list.length), degree = 1,{list.length + 1},Loop at line 171,{list.length},Loop at line 171] codetoanalyze/java/performance/ArrayListTest.java, ArrayListTest.iterate_with_iterator(java.util.ArrayList):void, 1, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 4 + 8 ⋅ list.length + 3 ⋅ (list.length + 1), O(list.length), degree = 1,{list.length + 1},Loop at line 165,{list.length},Loop at line 165] @@ -89,7 +89,7 @@ codetoanalyze/java/performance/Compound_loop.java, codetoanalyze.java.performanc codetoanalyze/java/performance/Compound_loop.java, codetoanalyze.java.performance.Compound_loop.while_and_or(int):void, 3, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Assignment,Binary operation: ([0, +oo] + 1):signed32] codetoanalyze/java/performance/Continue.java, codetoanalyze.java.performance.Continue.continue_outer_loop():int, 2, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 7963049, O(1), degree = 0] codetoanalyze/java/performance/Cost_test.java, codetoanalyze.java.performance.Cost_test.FN_loop2(int):int, 2, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 2 + 13 ⋅ k, O(k), degree = 1,{k},Loop at line 92] -codetoanalyze/java/performance/Cost_test.java, codetoanalyze.java.performance.Cost_test.call_inputstream_read_linear(java.io.InputStream):void, 4, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 28604, O(1), degree = 0] +codetoanalyze/java/performance/Cost_test.java, codetoanalyze.java.performance.Cost_test.call_inputstream_read_linear(java.io.InputStream):void, 4, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 27304, O(1), degree = 0] codetoanalyze/java/performance/Cost_test.java, codetoanalyze.java.performance.Cost_test.ignore_boolean_symbols_linear(boolean,int):void, 1, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 2 + 6 ⋅ n + 2 ⋅ (1+max(0, n)), O(n), degree = 1,{1+max(0, n)},Loop at line 135,{n},Loop at line 135] codetoanalyze/java/performance/Cost_test.java, codetoanalyze.java.performance.Cost_test.loop0_bad():int, 2, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 1202, O(1), degree = 0] codetoanalyze/java/performance/Cost_test.java, codetoanalyze.java.performance.Cost_test.loop1_bad():int, 3, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 1303, O(1), degree = 0] @@ -151,7 +151,7 @@ codetoanalyze/java/performance/JsonUtils.java, libraries.marauder.analytics.util codetoanalyze/java/performance/ListTest.java, ListTest.asList_linear(java.lang.String[]):void, 2, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 7 + 8 ⋅ array.length + 3 ⋅ (array.length + 1), O(array.length), degree = 1,{array.length + 1},Loop at line 42,{array.length},Loop at line 42] codetoanalyze/java/performance/ListTest.java, ListTest.call_iterate_elements_linear(java.util.List,java.util.List):void, 1, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 26 + 5 ⋅ (l2.length + l1.length) + 3 ⋅ (l2.length + l1.length + 1), O((l2.length + l1.length)), degree = 1,{l2.length + l1.length + 1},call to void ListTest.iterate_elements_linear(List),Loop at line 59,{l2.length + l1.length},call to void ListTest.iterate_elements_linear(List),Loop at line 59] codetoanalyze/java/performance/ListTest.java, ListTest.indexOfImpl_linear(java.util.List,java.lang.Object):int, 2, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 4 + 11 ⋅ list.length + 3 ⋅ (list.length + 1), O(list.length), degree = 1,{list.length + 1},Loop at line 18,{list.length},Loop at line 18] -codetoanalyze/java/performance/ListTest.java, ListTest.iter_multiple_list1_linear(java.util.List,java.util.List):void, 8, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 17 + 15 ⋅ (l2.length + l1.length + 1), O((l2.length + l1.length)), degree = 1,{l2.length + l1.length + 1},Loop at line 76] +codetoanalyze/java/performance/ListTest.java, ListTest.iter_multiple_list1_linear(java.util.List,java.util.List):void, 8, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 17 + 12 ⋅ (l2.length + l1.length) + 3 ⋅ (l2.length + l1.length + 1), O((l2.length + l1.length)), degree = 1,{l2.length + l1.length + 1},Loop at line 76,{l2.length + l1.length},Loop at line 76] codetoanalyze/java/performance/ListTest.java, ListTest.iter_multiple_list2_linear(java.util.List,java.util.List):void, 7, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 14 + 8 ⋅ (l2.length + l1.length) + 3 ⋅ (l2.length + l1.length + 1), O((l2.length + l1.length)), degree = 1,{l2.length + l1.length + 1},Loop at line 88,{l2.length + l1.length},Loop at line 88] codetoanalyze/java/performance/ListTest.java, ListTest.iter_multiple_list3_linear(java.util.List,java.util.List,java.util.List):void, 8, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 18 + 8 ⋅ (l2.length + l1.length + a.length) + 3 ⋅ (l2.length + l1.length + a.length + 1), O((l2.length + l1.length + a.length)), degree = 1,{l2.length + l1.length + a.length + 1},Loop at line 99,{l2.length + l1.length + a.length},Loop at line 99] codetoanalyze/java/performance/ListTest.java, ListTest.iterate_elements_linear(java.util.List):void, 2, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 4 + 5 ⋅ l.length + 3 ⋅ (l.length + 1), O(l.length), degree = 1,{l.length + 1},Loop at line 59,{l.length},Loop at line 59]