make ClassLoads an `interprocedural`

Summary: Making checkers/ its own dune library.

Reviewed By: ngorogiannis

Differential Revision: D21407066

fbshipit-source-id: 80744bcd4
master
Jules Villard 5 years ago committed by Facebook GitHub Bot
parent 34ae47a1b3
commit 74497ea7df

@ -79,7 +79,9 @@ let all_checkers =
; callbacks= [(intraprocedural SelfInBlock.checker, Language.Clang)] }
; { name= "Class loading analysis"
; active= Config.is_checker_enabled ClassLoads
; callbacks= [(Procedure ClassLoads.analyze_procedure, Language.Java)] }
; callbacks=
[(interprocedural Payloads.Fields.class_loads ClassLoads.analyze_procedure, Language.Java)]
}
; { name= "purity"
; active= Config.(is_checker_enabled Purity || is_checker_enabled LoopHoisting)
; callbacks=

@ -4,6 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
module L = Logging
@ -16,19 +17,14 @@ module L = Logging
- catch / throw with exception classes
*)
module Payload = SummaryPayload.Make (struct
type t = ClassLoadsDomain.summary
let field = Payloads.Fields.class_loads
end)
let do_call summary callee loc init =
Payload.read ~caller_summary:summary ~callee_pname:callee
|> Option.fold ~init ~f:(ClassLoadsDomain.integrate_summary callee loc)
let do_call {InterproceduralAnalysis.analyze_dependency} callee loc init =
analyze_dependency callee
|> Option.fold ~init ~f:(fun acc (_, summary) ->
ClassLoadsDomain.integrate_summary callee loc acc summary )
(** fully load a class given the typename *)
let rec load_class summary tenv loc astate class_name =
let rec load_class ({InterproceduralAnalysis.tenv} as analysis_data) loc astate class_name =
(* don't bother if class is already loaded *)
if ClassLoadsDomain.mem_typename class_name astate then astate
else
@ -38,86 +34,87 @@ let rec load_class summary tenv loc astate class_name =
let astate2 =
let class_initializer = Procname.(Java (Java.get_class_initializer class_name)) in
(* NB may recurse if we are in class init but the shortcircuiting above makes it a no-op *)
do_call summary class_initializer loc astate1
do_call analysis_data class_initializer loc astate1
in
(* finally, recursively load all superclasses *)
Tenv.lookup tenv class_name
|> Option.value_map ~default:[] ~f:(fun tstruct -> tstruct.Struct.supers)
|> List.fold ~init:astate2 ~f:(load_class summary tenv loc)
|> List.fold ~init:astate2 ~f:(load_class analysis_data loc)
let load_type summary tenv loc (typ : Typ.t) astate =
let load_type analysis_data loc (typ : Typ.t) astate =
match typ with
| {desc= Tstruct name} | {desc= Tptr ({desc= Tstruct name}, _)} ->
load_class summary tenv loc astate name
load_class analysis_data loc astate name
| _ ->
astate
let rec load_array summary tenv loc (typ : Typ.t) astate =
let rec load_array analysis_data loc (typ : Typ.t) astate =
match typ with
| {desc= Tarray {elt}} ->
load_array summary tenv loc elt astate
load_array analysis_data loc elt astate
| _ ->
load_type summary tenv loc typ astate
load_type analysis_data loc typ astate
let rec add_loads_of_exp summary tenv loc (exp : Exp.t) astate =
let rec add_loads_of_exp analysis_data loc (exp : Exp.t) astate =
match exp with
| Const (Cclass class_ident) ->
(* [X.class] expressions *)
let class_str = Ident.name_to_string class_ident |> JavaClassName.from_string in
let class_name = Typ.JavaClass class_str in
load_class summary tenv loc astate class_name
load_class analysis_data loc astate class_name
| Sizeof {typ= {desc= Tarray {elt}}} ->
(* anewarray / multinewarray *)
load_array summary tenv loc elt astate
load_array analysis_data loc elt astate
| Cast (_, e) | UnOp (_, e, _) | Exn e ->
(* NB Cast is only used for primitive types *)
add_loads_of_exp summary tenv loc e astate
add_loads_of_exp analysis_data loc e astate
| BinOp (_, e1, e2) ->
add_loads_of_exp summary tenv loc e1 astate |> add_loads_of_exp summary tenv loc e2
add_loads_of_exp analysis_data loc e1 astate |> add_loads_of_exp analysis_data loc e2
| Lfield (e, _, typ') ->
(* getfield / getstatic / putfield / putstatic *)
load_type summary tenv loc typ' astate |> add_loads_of_exp summary tenv loc e
load_type analysis_data loc typ' astate |> add_loads_of_exp analysis_data loc e
| Var _ | Const _ | Closure _ | Sizeof _ | Lindex _ | Lvar _ ->
astate
let exec_call summary tenv callee args loc astate =
let exec_call analysis_data callee args loc astate =
match args with
| [_; (Exp.Sizeof {typ}, _)] when Procname.equal callee BuiltinDecl.__instanceof ->
(* this matches downcasts/instanceof and exception handlers *)
load_type summary tenv loc typ astate
load_type analysis_data loc typ astate
| _ ->
(* invokeinterface / invokespecial / invokestatic / invokevirtual / new *)
List.fold args ~init:astate ~f:(fun acc (exp, _) -> add_loads_of_exp summary tenv loc exp acc)
|> do_call summary callee loc
List.fold args ~init:astate ~f:(fun acc (exp, _) -> add_loads_of_exp analysis_data loc exp acc)
|> do_call analysis_data callee loc
let exec_instr summary tenv astate _ (instr : Sil.instr) =
let exec_instr analysis_data astate _ (instr : Sil.instr) =
match instr with
| Call (_, Const (Cfun callee), args, loc, _) ->
exec_call summary tenv callee args loc astate
exec_call analysis_data callee args loc astate
| Load {e= exp; loc} | Prune (exp, loc, _, _) ->
(* NB the java frontend seems to always translate complex guards into a sequence of
instructions plus a prune on logical vars only. So the below is only for completeness. *)
add_loads_of_exp summary tenv loc exp astate
add_loads_of_exp analysis_data loc exp astate
| Store {e1; e2; loc} ->
add_loads_of_exp summary tenv loc e1 astate |> add_loads_of_exp summary tenv loc e2
add_loads_of_exp analysis_data loc e1 astate |> add_loads_of_exp analysis_data loc e2
| _ ->
astate
let report_loads summary astate =
let report_loads {InterproceduralAnalysis.proc_desc; err_log} astate =
let report_load ({ClassLoadsDomain.Event.loc; elem} as event) =
if String.is_prefix ~prefix:"java." elem then ()
else
let ltr = ClassLoadsDomain.Event.make_loc_trace event in
let msg = Format.asprintf "Class %s loaded" elem in
SummaryReporting.log_warning summary ~loc ~ltr IssueType.class_load msg
let attrs = Procdesc.get_attributes proc_desc in
Reporting.log_warning attrs err_log ~loc ~ltr IssueType.class_load msg
in
let pname = Summary.get_proc_name summary in
let pname = Procdesc.get_proc_name proc_desc in
Procname.get_class_name pname
|> Option.iter ~f:(fun clazz ->
let method_strname = Procname.get_method pname in
@ -126,19 +123,16 @@ let report_loads summary astate =
ClassLoadsDomain.iter report_load astate )
let analyze_procedure {Callbacks.exe_env; summary} =
let proc_desc = Summary.get_proc_desc summary in
let analyze_procedure ({InterproceduralAnalysis.proc_desc} as analysis_data) =
let proc_name = Procdesc.get_proc_name proc_desc in
let tenv = Exe_env.get_tenv exe_env proc_name in
L.debug Analysis Verbose "CL: ANALYZING %a@." Procname.pp proc_name ;
let loc = Procdesc.get_loc proc_desc in
(* load the method's class *)
let init =
Procname.get_class_type_name proc_name
|> Option.fold ~init:ClassLoadsDomain.bottom ~f:(load_class summary tenv loc)
|> Option.fold ~init:ClassLoadsDomain.bottom ~f:(load_class analysis_data loc)
in
let post = Procdesc.fold_instrs proc_desc ~init ~f:(exec_instr summary tenv) in
report_loads summary post ;
let result = Payload.update_summary post summary in
let post = Procdesc.fold_instrs proc_desc ~init ~f:(exec_instr analysis_data) in
report_loads analysis_data post ;
L.debug Analysis Verbose "CL: FINISHED ANALYZING %a@." Procname.pp proc_name ;
result
Some post

@ -7,4 +7,5 @@
open! IStd
val analyze_procedure : Callbacks.proc_callback_t
val analyze_procedure :
ClassLoadsDomain.summary InterproceduralAnalysis.t -> ClassLoadsDomain.summary option

Loading…
Cancel
Save