[inferbo] Revise semantics of `values` function of Java enum class

Summary: The semantics of the `values` function of Java enum class was missing, when it is called outside the class initializer. This diff gets the size of the enum elements from the summary of class initializer function, `<clinit>`.

Reviewed By: ezgicicek

Differential Revision: D20094880

fbshipit-source-id: 7362bba1c
master
Sungkeun Cho 5 years ago committed by Facebook Github Bot
parent cbd506011f
commit 7a0be26596

@ -175,29 +175,43 @@ module TransferFunctions = struct
false false
let is_java_enum_values ~caller_pname ~callee_pname = let is_java_enum_values tenv callee_pname =
match Option.exists (Procname.get_class_type_name callee_pname) ~f:(fun callee_class_name ->
(Procname.get_class_type_name caller_pname, Procname.get_class_type_name callee_pname) PatternMatch.is_java_enum tenv callee_class_name
with && String.equal (Procname.get_method callee_pname) "values" )
| Some caller_class_name, Some callee_class_name
when Typ.equal_name caller_class_name callee_class_name ->
let assign_java_enum_values get_summary id ~caller_pname ~callee_pname mem =
let caller_class_name = Procname.get_class_type_name caller_pname in
let callee_class_name = Procname.get_class_type_name callee_pname in
let is_caller_class_initializer =
IOption.exists2 caller_class_name callee_class_name
~f:(fun caller_class_name callee_class_name ->
Procname.is_java_class_initializer caller_pname Procname.is_java_class_initializer caller_pname
&& String.equal (Procname.get_method callee_pname) "values" && Typ.equal_name caller_class_name callee_class_name )
| _, _ -> in
false match callee_class_name with
let assign_java_enum_values id callee_pname mem =
match Procname.get_class_type_name callee_pname with
| Some (JavaClass class_name as typename) -> | Some (JavaClass class_name as typename) ->
let clinit_mem =
if is_caller_class_initializer then Some (Dom.Mem.unset_oenv mem)
else get_summary (Procname.Java (Procname.Java.get_class_initializer typename))
in
Option.value_map clinit_mem ~default:mem ~f:(fun clinit_mem ->
let loc =
let class_var = let class_var =
Loc.of_var let class_mangled = Mangled.from_string (JavaClassName.to_string class_name) in
(Var.of_pvar Loc.of_var (Var.of_pvar (Pvar.mk_global class_mangled))
(Pvar.mk_global (Mangled.from_string (JavaClassName.to_string class_name))))
in in
let fn = Fieldname.make typename "$VALUES" in let fn = Fieldname.make typename "$VALUES" in
let v = Dom.Mem.find (Loc.append_field class_var ~fn) mem in Loc.append_field class_var ~fn
Dom.Mem.add_stack (Loc.of_id id) v mem in
let v = Dom.Mem.find loc clinit_mem in
let mem = Dom.Mem.add_stack (Loc.of_id id) v mem in
if is_caller_class_initializer then mem
else
let arr_locs = Dom.Val.get_all_locs v in
let arr_v = Dom.Mem.find_set arr_locs clinit_mem in
Dom.Mem.add_heap_set arr_locs arr_v mem )
| _ -> | _ ->
assert false assert false
@ -359,11 +373,13 @@ module TransferFunctions = struct
mem mem
| Prune (exp, _, _, _) -> | Prune (exp, _, _, _) ->
Sem.Prune.prune integer_type_widths exp mem Sem.Prune.prune integer_type_widths exp mem
| Call ((id, _), Const (Cfun callee_pname), _, _, _) when is_java_enum_values tenv callee_pname
->
let mem = Dom.Mem.add_stack_loc (Loc.of_id id) mem in
assign_java_enum_values get_summary id ~caller_pname:(Summary.get_proc_name summary)
~callee_pname mem
| Call (((id, _) as ret), Const (Cfun callee_pname), params, location, _) -> ( | Call (((id, _) as ret), Const (Cfun callee_pname), params, location, _) -> (
let mem = Dom.Mem.add_stack_loc (Loc.of_id id) mem in let mem = Dom.Mem.add_stack_loc (Loc.of_id id) mem in
if is_java_enum_values ~caller_pname:(Summary.get_proc_name summary) ~callee_pname then
assign_java_enum_values id callee_pname mem
else
let fun_arg_list = let fun_arg_list =
List.map params ~f:(fun (exp, typ) -> List.map params ~f:(fun (exp, typ) ->
ProcnameDispatcher.Call.FuncArg.{exp; typ; arg_payload= ()} ) ProcnameDispatcher.Call.FuncArg.{exp; typ; arg_payload= ()} )

@ -15,6 +15,8 @@ let if_none_evalopt ~f x = match x with None -> f () | Some _ -> x
let if_none_eval = value_default_f let if_none_eval = value_default_f
let exists2 x y ~f = match (x, y) with Some x, Some y -> f x y | _, _ -> false
module Let_syntax = struct module Let_syntax = struct
include Option.Let_syntax include Option.Let_syntax

@ -22,6 +22,9 @@ val if_none_eval : f:(unit -> 'a) -> 'a option -> 'a
built with [if_none_evalopt]. This is exactly the same as [value_default_f] but with a better built with [if_none_evalopt]. This is exactly the same as [value_default_f] but with a better
name. *) name. *)
val exists2 : 'a option -> 'b option -> f:('a -> 'b -> bool) -> bool
(** Like [Option.exists] but gets two parameters. *)
include sig include sig
[@@@warning "-32-60"] [@@@warning "-32-60"]

@ -1,4 +1,5 @@
codetoanalyze/java/bufferoverrun/Array.java, codetoanalyze.java.bufferoverrun.Array.array_length_Bad():void, 2, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 10 Size: 5] codetoanalyze/java/bufferoverrun/Array.java, codetoanalyze.java.bufferoverrun.Array.array_length_Bad():void, 1, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here]
codetoanalyze/java/bufferoverrun/Array.java, codetoanalyze.java.bufferoverrun.Array.array_length_Bad():void, 4, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 10 Size: 5]
codetoanalyze/java/bufferoverrun/Array.java, codetoanalyze.java.bufferoverrun.Array.call_iterate_collection_Bad():void, 6, BUFFER_OVERRUN_L1, no_bucket, ERROR, [Array declaration,Through,Through,Through,Through,Through,Call,<Length trace>,Array declaration,Array access: Offset: 5 Size: 5 by call to `void Array.iterate_collection_Bad(ArrayList)` ] codetoanalyze/java/bufferoverrun/Array.java, codetoanalyze.java.bufferoverrun.Array.call_iterate_collection_Bad():void, 6, BUFFER_OVERRUN_L1, no_bucket, ERROR, [Array declaration,Through,Through,Through,Through,Through,Call,<Length trace>,Array declaration,Array access: Offset: 5 Size: 5 by call to `void Array.iterate_collection_Bad(ArrayList)` ]
codetoanalyze/java/bufferoverrun/Array.java, codetoanalyze.java.bufferoverrun.Array.collection_remove_from_empty_Bad():java.util.ArrayList, 1, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 0 Size: 0] codetoanalyze/java/bufferoverrun/Array.java, codetoanalyze.java.bufferoverrun.Array.collection_remove_from_empty_Bad():java.util.ArrayList, 1, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 0 Size: 0]
codetoanalyze/java/bufferoverrun/Array.java, codetoanalyze.java.bufferoverrun.Array.negative_alloc_Bad():void, 0, INFERBO_ALLOC_IS_NEGATIVE, no_bucket, ERROR, [Allocation: Length: -1] codetoanalyze/java/bufferoverrun/Array.java, codetoanalyze.java.bufferoverrun.Array.negative_alloc_Bad():void, 0, INFERBO_ALLOC_IS_NEGATIVE, no_bucket, ERROR, [Allocation: Length: -1]

@ -28,4 +28,8 @@ public class IteratorTest {
for (Color c : Color.values()) {} for (Color c : Color.values()) {}
} }
} }
public void enum_iter() {
for (Color c : Color.values()) {}
}
} }

Loading…
Cancel
Save