[classloads] load super classes recursively

Reviewed By: mbouaziz

Differential Revision: D13771265

fbshipit-source-id: da600f912
master
Nikos Gorogiannis 6 years ago committed by Facebook Github Bot
parent bc6829344f
commit 3fc4ccbc14

@ -6,7 +6,6 @@
*) *)
open! IStd open! IStd
module L = Logging module L = Logging
module F = Format
module Payload = SummaryPayload.Make (struct module Payload = SummaryPayload.Make (struct
type t = ClassLoadsDomain.summary type t = ClassLoadsDomain.summary
@ -16,24 +15,42 @@ module Payload = SummaryPayload.Make (struct
let of_payloads (payloads : Payloads.t) = payloads.class_loads let of_payloads (payloads : Payloads.t) = payloads.class_loads
end) end)
let get_java_class = function let do_call pdesc callee loc init =
| Typ.Procname.Java java_pname -> Payload.read pdesc callee |> Option.fold ~init ~f:(ClassLoadsDomain.integrate_summary callee loc)
Some (Typ.Procname.Java.get_class_name java_pname)
| _ ->
None (** fully load a class given the typename *)
let rec load_class proc_desc tenv loc astate class_name =
(* don't bother if class is already loaded *)
let rec exp_fold_over_fields ~f (exp : Exp.t) acc = if ClassLoadsDomain.mem_typename class_name astate then astate
else
(* load the class itself *)
let astate1 = ClassLoadsDomain.add_typename loc astate class_name in
(* load classes referenced by the class initializer *)
let astate2 =
let class_initializer = Typ.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 proc_desc class_initializer loc astate1
in
(* finally, recursively load all superclasses *)
Tenv.lookup tenv class_name
|> Option.value_map ~default:[] ~f:(fun tstruct -> tstruct.Typ.Struct.supers)
|> List.fold ~init:astate2 ~f:(load_class proc_desc tenv loc)
let rec exp_fold_over_fields ~f ~init (exp : Exp.t) =
match exp with match exp with
(* TODO Cast? Const literals for class objects? Arrays? *) (* TODO Cast? Const literals for class objects? Arrays? *)
| Var _ | Const _ | Lvar _ | Sizeof _ | Closure _ -> | Var _ | Const _ | Lvar _ | Sizeof _ | Closure _ ->
acc init
| Cast (_, e) | UnOp (_, e, _) | Exn e | Lindex (e, _) -> | Cast (_, e) | UnOp (_, e, _) | Exn e | Lindex (e, _) ->
exp_fold_over_fields ~f e acc exp_fold_over_fields ~f ~init e
| BinOp (_, e1, e2) -> | BinOp (_, e1, e2) ->
exp_fold_over_fields ~f e1 acc |> exp_fold_over_fields ~f e2 let init = exp_fold_over_fields ~f ~init e1 in
exp_fold_over_fields ~f ~init e2
| Lfield (e, field, typ) -> | Lfield (e, field, typ) ->
f field typ acc |> exp_fold_over_fields ~f e let init = f init field typ in
exp_fold_over_fields ~f ~init e
let class_of_type (typ : Typ.t) = let class_of_type (typ : Typ.t) =
@ -44,23 +61,22 @@ let class_of_type (typ : Typ.t) =
None None
let add_field_loads_of_exp exp loc astate = let add_field_loads_of_exp proc_desc tenv exp loc init =
let f _field typ init = let f init _field typ =
class_of_type typ |> Option.map ~f:Typ.Name.name class_of_type typ |> Option.fold ~init ~f:(load_class proc_desc tenv loc)
|> Option.fold ~init ~f:(ClassLoadsDomain.add_load loc)
in in
exp_fold_over_fields ~f exp astate exp_fold_over_fields ~f ~init exp
let exec_instr pdesc astate _ (instr : Sil.instr) = let exec_instr pdesc tenv astate _ (instr : Sil.instr) =
match instr with match instr with
| Call (_, Const (Cfun callee), _, loc, _) -> | Call (_, Const (Cfun callee), _, loc, _) ->
Payload.read pdesc callee do_call pdesc callee loc astate
|> Option.fold ~init:astate ~f:(ClassLoadsDomain.integrate_summary callee loc)
| Load (_, exp, _, loc) | Prune (exp, loc, _, _) -> | Load (_, exp, _, loc) | Prune (exp, loc, _, _) ->
add_field_loads_of_exp exp loc astate add_field_loads_of_exp pdesc tenv exp loc astate
| Store (lexp, _, rexp, loc) -> | Store (lexp, _, rexp, loc) ->
add_field_loads_of_exp lexp loc astate |> add_field_loads_of_exp rexp loc add_field_loads_of_exp pdesc tenv lexp loc astate
|> add_field_loads_of_exp pdesc tenv rexp loc
| _ -> | _ ->
astate astate
@ -74,7 +90,7 @@ let report_loads proc_desc summary astate =
Reporting.log_warning summary ~loc ~ltr IssueType.class_load msg Reporting.log_warning summary ~loc ~ltr IssueType.class_load msg
in in
let pname = Procdesc.get_proc_name proc_desc in let pname = Procdesc.get_proc_name proc_desc in
get_java_class pname Typ.Procname.get_class_name pname
|> Option.iter ~f:(fun clazz -> |> Option.iter ~f:(fun clazz ->
let method_strname = Typ.Procname.get_method pname in let method_strname = Typ.Procname.get_method pname in
let fullname = clazz ^ "." ^ method_strname in let fullname = clazz ^ "." ^ method_strname in
@ -82,38 +98,16 @@ let report_loads proc_desc summary astate =
ClassLoadsDomain.iter report_load astate ) ClassLoadsDomain.iter report_load astate )
(* if [pdesc] is *not* a class initializer (to avoid infinite recursion), return the let analyze_procedure {Callbacks.proc_desc; tenv; summary} =
class initializer of [pdesc]'s class *)
let class_initializer_of_method pdesc =
let open Typ.Procname in
match Procdesc.get_proc_name pdesc with
| Java java_pname when Java.is_class_initializer java_pname ->
None
| Java java_pname ->
let class_name = Java.get_class_type_name java_pname in
Some (Java (Java.get_class_initializer class_name))
| _ ->
assert false
let analyze_procedure {Callbacks.proc_desc; summary} =
let proc_name = Procdesc.get_proc_name proc_desc in let proc_name = Procdesc.get_proc_name proc_desc in
L.debug Analysis Verbose "CL: ANALYZING %a@." Typ.Procname.pp proc_name ; L.debug Analysis Verbose "CL: ANALYZING %a@." Typ.Procname.pp proc_name ;
let loc = Procdesc.get_loc proc_desc in let loc = Procdesc.get_loc proc_desc in
(* add a load for the method's class *) (* load the method's class *)
let init = let init =
let class_opt = get_java_class proc_name in Typ.Procname.get_class_type_name proc_name
L.debug Analysis Verbose "CL: CLASS = %a@." (Pp.option F.pp_print_string) class_opt ; |> Option.fold ~init:ClassLoadsDomain.empty ~f:(load_class proc_desc tenv loc)
Option.fold class_opt ~init:ClassLoadsDomain.empty ~f:(ClassLoadsDomain.add_load loc)
in
(* add loads done by the static initialization of this method's class *)
let after_class_init =
class_initializer_of_method proc_desc
|> Option.bind ~f:(Payload.read proc_desc)
(* pretend there is a call to class initializer before the method body *)
|> Option.fold ~init ~f:(ClassLoadsDomain.integrate_summary proc_name loc)
in in
let post = Procdesc.fold_instrs proc_desc ~init:after_class_init ~f:(exec_instr proc_desc) in let post = Procdesc.fold_instrs proc_desc ~init ~f:(exec_instr proc_desc tenv) in
report_loads proc_desc summary post ; report_loads proc_desc summary post ;
let result = Payload.update_summary post summary in let result = Payload.update_summary post summary in
L.debug Analysis Verbose "CL: FINISHED ANALYZING %a@." Typ.Procname.pp proc_name ; L.debug Analysis Verbose "CL: FINISHED ANALYZING %a@." Typ.Procname.pp proc_name ;

@ -28,12 +28,21 @@ let add ({Event.trace} as x) astate =
let union xs ys = fold add xs ys let union xs ys = fold add xs ys
let add_load loc astate clazz = let add_string loc astate clazz =
L.debug Analysis Verbose "CL: LOADING class %s@." clazz ; L.debug Analysis Verbose "CL: LOADING class %s@." clazz ;
let new_event = Event.make clazz loc in let new_event = Event.make clazz loc in
add new_event astate add new_event astate
let mem_typename name astate =
let str_name = Typ.Name.name name in
(* comparison of elements is only over the string component so fake the rest *)
let fake_event = Event.make str_name Location.dummy in
mem fake_event astate
let add_typename loc astate name = Typ.Name.name name |> add_string loc astate
let integrate_summary callee_pname loc astate callee_summary = let integrate_summary callee_pname loc astate callee_summary =
L.debug Analysis Verbose "CL: ADDING SUMMARY OF %a@." Typ.Procname.pp callee_pname ; L.debug Analysis Verbose "CL: ADDING SUMMARY OF %a@." Typ.Procname.pp callee_pname ;
let callsite = CallSite.make callee_pname loc in let callsite = CallSite.make callee_pname loc in

@ -17,7 +17,9 @@ type summary = t
val pp_summary : F.formatter -> summary -> unit val pp_summary : F.formatter -> summary -> unit
val add_load : Location.t -> t -> string -> t val mem_typename : Typ.Name.t -> t -> bool
val add_typename : Location.t -> t -> Typ.Name.t -> t
val integrate_summary : Typ.Procname.t -> Location.t -> t -> summary -> t val integrate_summary : Typ.Procname.t -> Location.t -> t -> summary -> t

@ -0,0 +1,28 @@
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
class Super {
public static void main(String args[]) {
// this loads SuperB and SuperA
System.out.println(SuperB.static_data);
// this loads SuperC and SuperD
SuperD.foo();
}
}
class SuperA {}
class SuperB extends SuperA {
static int static_data = 5;
}
class SuperC {}
class SuperD extends SuperC {
public static void foo() {}
}
Loading…
Cancel
Save