From 3fc4ccbc14e7bf1c02c7e04a36f93a2c6d3cf0b7 Mon Sep 17 00:00:00 2001 From: Nikos Gorogiannis Date: Wed, 23 Jan 2019 05:24:18 -0800 Subject: [PATCH] [classloads] load super classes recursively Reviewed By: mbouaziz Differential Revision: D13771265 fbshipit-source-id: da600f912 --- infer/src/concurrency/classLoads.ml | 96 +++++++++---------- infer/src/concurrency/classLoadsDomain.ml | 11 ++- infer/src/concurrency/classLoadsDomain.mli | 4 +- .../codetoanalyze/java/classloads/Super.java | 28 ++++++ 4 files changed, 86 insertions(+), 53 deletions(-) create mode 100644 infer/tests/codetoanalyze/java/classloads/Super.java diff --git a/infer/src/concurrency/classLoads.ml b/infer/src/concurrency/classLoads.ml index 482b6db7a..ca84f78a6 100644 --- a/infer/src/concurrency/classLoads.ml +++ b/infer/src/concurrency/classLoads.ml @@ -6,7 +6,6 @@ *) open! IStd module L = Logging -module F = Format module Payload = SummaryPayload.Make (struct type t = ClassLoadsDomain.summary @@ -16,24 +15,42 @@ module Payload = SummaryPayload.Make (struct let of_payloads (payloads : Payloads.t) = payloads.class_loads end) -let get_java_class = function - | Typ.Procname.Java java_pname -> - Some (Typ.Procname.Java.get_class_name java_pname) - | _ -> - None - - -let rec exp_fold_over_fields ~f (exp : Exp.t) acc = +let do_call pdesc callee loc init = + Payload.read pdesc callee |> Option.fold ~init ~f:(ClassLoadsDomain.integrate_summary callee loc) + + +(** 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 *) + 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 (* TODO Cast? Const literals for class objects? Arrays? *) | Var _ | Const _ | Lvar _ | Sizeof _ | Closure _ -> - acc + init | 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) -> - 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) -> - 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) = @@ -44,23 +61,22 @@ let class_of_type (typ : Typ.t) = None -let add_field_loads_of_exp exp loc astate = - let f _field typ init = - class_of_type typ |> Option.map ~f:Typ.Name.name - |> Option.fold ~init ~f:(ClassLoadsDomain.add_load loc) +let add_field_loads_of_exp proc_desc tenv exp loc init = + let f init _field typ = + class_of_type typ |> Option.fold ~init ~f:(load_class proc_desc tenv loc) 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 | Call (_, Const (Cfun callee), _, loc, _) -> - Payload.read pdesc callee - |> Option.fold ~init:astate ~f:(ClassLoadsDomain.integrate_summary callee loc) + do_call pdesc callee loc astate | 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) -> - 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 @@ -74,7 +90,7 @@ let report_loads proc_desc summary astate = Reporting.log_warning summary ~loc ~ltr IssueType.class_load msg 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 -> let method_strname = Typ.Procname.get_method pname in let fullname = clazz ^ "." ^ method_strname in @@ -82,38 +98,16 @@ let report_loads proc_desc summary astate = ClassLoadsDomain.iter report_load astate ) -(* if [pdesc] is *not* a class initializer (to avoid infinite recursion), return the - 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 analyze_procedure {Callbacks.proc_desc; tenv; summary} = let proc_name = Procdesc.get_proc_name proc_desc in L.debug Analysis Verbose "CL: ANALYZING %a@." Typ.Procname.pp proc_name ; let loc = Procdesc.get_loc proc_desc in - (* add a load for the method's class *) + (* load the method's class *) let init = - let class_opt = get_java_class proc_name in - L.debug Analysis Verbose "CL: CLASS = %a@." (Pp.option F.pp_print_string) class_opt ; - 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) + Typ.Procname.get_class_type_name proc_name + |> Option.fold ~init:ClassLoadsDomain.empty ~f:(load_class proc_desc tenv loc) 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 ; let result = Payload.update_summary post summary in L.debug Analysis Verbose "CL: FINISHED ANALYZING %a@." Typ.Procname.pp proc_name ; diff --git a/infer/src/concurrency/classLoadsDomain.ml b/infer/src/concurrency/classLoadsDomain.ml index 85ec2ce83..6aeff6404 100644 --- a/infer/src/concurrency/classLoadsDomain.ml +++ b/infer/src/concurrency/classLoadsDomain.ml @@ -28,12 +28,21 @@ let add ({Event.trace} as x) astate = 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 ; let new_event = Event.make clazz loc in 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 = L.debug Analysis Verbose "CL: ADDING SUMMARY OF %a@." Typ.Procname.pp callee_pname ; let callsite = CallSite.make callee_pname loc in diff --git a/infer/src/concurrency/classLoadsDomain.mli b/infer/src/concurrency/classLoadsDomain.mli index 69462bce1..5b2575cfd 100644 --- a/infer/src/concurrency/classLoadsDomain.mli +++ b/infer/src/concurrency/classLoadsDomain.mli @@ -17,7 +17,9 @@ type summary = t 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 diff --git a/infer/tests/codetoanalyze/java/classloads/Super.java b/infer/tests/codetoanalyze/java/classloads/Super.java new file mode 100644 index 000000000..bd795d652 --- /dev/null +++ b/infer/tests/codetoanalyze/java/classloads/Super.java @@ -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() {} +}