From 50c98db39174feb4be94556e616fcead5cab0f92 Mon Sep 17 00:00:00 2001 From: David Pichardie Date: Wed, 8 Apr 2020 06:42:31 -0700 Subject: [PATCH] Storing class source file locations in Struct (Tenv) records Summary: Java bytecode format does not record the declarations location for classes and fields. We set up a first infrastructure to recover this information. Currently we only track location for classes and only gives the first line of the corresponding source file. We will enhance this location with source file (baby) parsing in a next diff. Reviewed By: mityal Differential Revision: D20868187 fbshipit-source-id: d355475e9 --- infer/src/IR/Struct.ml | 16 +++++++++++----- infer/src/IR/Struct.mli | 16 +++++++++++----- infer/src/IR/Tenv.ml | 12 +++++++----- infer/src/IR/Tenv.mli | 2 +- infer/src/checkers/NullabilityPreanalysis.ml | 6 ++---- infer/src/java/jClasspath.ml | 16 ++++++++++++++++ infer/src/java/jClasspath.mli | 4 ++++ infer/src/java/jFrontend.ml | 15 ++++++++++++++- infer/src/java/jSourceFileInfo.ml | 13 +++++++++++++ infer/src/java/jSourceFileInfo.mli | 12 ++++++++++++ infer/src/java/jTransType.ml | 11 ++++++++++- 11 files changed, 101 insertions(+), 22 deletions(-) create mode 100644 infer/src/java/jSourceFileInfo.ml create mode 100644 infer/src/java/jSourceFileInfo.mli diff --git a/infer/src/IR/Struct.ml b/infer/src/IR/Struct.ml index 7990a5268..217061f1c 100644 --- a/infer/src/IR/Struct.ml +++ b/infer/src/IR/Struct.ml @@ -12,7 +12,13 @@ type field = Fieldname.t * Typ.t * Annot.Item.t [@@deriving compare] type fields = field list -type java_class_kind = Interface | AbstractClass | NormalClass +type java_class_kind = Interface | AbstractClass | NormalClass [@@deriving equal] + +type java_class_info = + { kind: java_class_kind (** class kind in Java *) + ; loc: Location.t option + (** None should correspond to rare cases when it was impossible to fetch the location in + source file *) } (** Type for a structured value. *) type t = @@ -23,7 +29,7 @@ type t = ; methods: Procname.t list (** methods defined *) ; exported_objc_methods: Procname.t list (** methods in ObjC interface, subset of [methods] *) ; annots: Annot.Item.t (** annotations *) - ; java_class_kind: java_class_kind option (** class kind in Java *) + ; java_class_info: java_class_info option (** present if and only if the class is Java *) ; dummy: bool (** dummy struct for class including static method *) } type lookup = Typ.Name.t -> t option @@ -60,7 +66,7 @@ let pp pe name f {fields; supers; methods; exported_objc_methods; annots} = let internal_mk_struct ?default ?fields ?statics ?methods ?exported_objc_methods ?supers ?annots - ?java_class_kind ?dummy () = + ?java_class_info ?dummy () = let default_ = { fields= [] ; statics= [] @@ -69,14 +75,14 @@ let internal_mk_struct ?default ?fields ?statics ?methods ?exported_objc_methods ; supers= [] ; subs= Typ.Name.Set.empty ; annots= Annot.Item.empty - ; java_class_kind= None + ; java_class_info= None ; dummy= false } in let mk_struct_ ?(default = default_) ?(fields = default.fields) ?(statics = default.statics) ?(methods = default.methods) ?(exported_objc_methods = default.exported_objc_methods) ?(supers = default.supers) ?(subs = default.subs) ?(annots = default.annots) ?(dummy = default.dummy) () = - {fields; statics; methods; exported_objc_methods; supers; subs; annots; java_class_kind; dummy} + {fields; statics; methods; exported_objc_methods; supers; subs; annots; java_class_info; dummy} in mk_struct_ ?default ?fields ?statics ?methods ?exported_objc_methods ?supers ?annots ?dummy () diff --git a/infer/src/IR/Struct.mli b/infer/src/IR/Struct.mli index 286c489c5..08487f388 100644 --- a/infer/src/IR/Struct.mli +++ b/infer/src/IR/Struct.mli @@ -13,18 +13,24 @@ type field = Fieldname.t * Typ.t * Annot.Item.t [@@deriving compare] type fields = field list -type java_class_kind = Interface | AbstractClass | NormalClass +type java_class_kind = Interface | AbstractClass | NormalClass [@@deriving equal] + +type java_class_info = + { kind: java_class_kind (** class kind in Java *) + ; loc: Location.t option + (** None should correspond to rare cases when it was impossible to fetch the location in + source file *) } (** Type for a structured value. *) -type t = private +type t = { fields: fields (** non-static fields *) ; statics: fields (** static fields *) - ; supers: Typ.Name.t list (** supers *) + ; supers: Typ.Name.t list (** superclasses *) ; subs: Typ.Name.Set.t (** subclasses, initialized after merging type environments *) ; methods: Procname.t list (** methods defined *) ; exported_objc_methods: Procname.t list (** methods in ObjC interface, subset of [methods] *) ; annots: Annot.Item.t (** annotations *) - ; java_class_kind: java_class_kind option (** class kind in Java *) + ; java_class_info: java_class_info option (** present if and only if the class is Java *) ; dummy: bool (** dummy struct for class including static method *) } type lookup = Typ.Name.t -> t option @@ -42,7 +48,7 @@ val internal_mk_struct : -> ?exported_objc_methods:Procname.t list -> ?supers:Typ.Name.t list -> ?annots:Annot.Item.t - -> ?java_class_kind:java_class_kind + -> ?java_class_info:java_class_info -> ?dummy:bool -> unit -> t diff --git a/infer/src/IR/Tenv.ml b/infer/src/IR/Tenv.ml index f2916a51e..7bd313d70 100644 --- a/infer/src/IR/Tenv.ml +++ b/infer/src/IR/Tenv.ml @@ -30,10 +30,10 @@ let create () = TypenameHash.create 1000 (** Construct a struct type in a type environment *) let mk_struct tenv ?default ?fields ?statics ?methods ?exported_objc_methods ?supers ?annots - ?java_class_kind ?dummy name = + ?java_class_info ?dummy name = let struct_typ = Struct.internal_mk_struct ?default ?fields ?statics ?methods ?exported_objc_methods ?supers - ?annots ?java_class_kind ?dummy () + ?annots ?java_class_info ?dummy () in TypenameHash.replace tenv name struct_typ ; struct_typ @@ -224,13 +224,15 @@ let get_summary_formals tenv ~get_summary ~get_formals = `NotFound | Some class_name -> ( match lookup tenv class_name with - | Some {Struct.java_class_kind= Some Interface; subs} - when Int.equal (Typ.Name.Set.cardinal subs) 1 -> + | Some {Struct.java_class_info= Some info; subs} + when Struct.equal_java_class_kind info.kind Interface + && Int.equal (Typ.Name.Set.cardinal subs) 1 -> let unique_sub = Typ.Name.Set.choose subs in Logging.d_printfln_escaped "Found a unique sub-class %a" Typ.Name.pp unique_sub ; let sub_pname = Procname.replace_class pname unique_sub in get_summary_formals_aux sub_pname |> found_from_subclass sub_pname - | Some {Struct.java_class_kind= Some AbstractClass; subs} -> + | Some {Struct.java_class_info= Some info; subs} + when Struct.equal_java_class_kind info.kind AbstractClass -> Option.value_map (Typ.Name.Set.min_elt_opt subs) ~default:`NotFound ~f:(fun sub -> Logging.d_printfln_escaped "Found an arbitrary sub-class %a" Typ.Name.pp sub ; let sub_pname = Procname.replace_class pname sub in diff --git a/infer/src/IR/Tenv.mli b/infer/src/IR/Tenv.mli index 436690b89..02ce81dea 100644 --- a/infer/src/IR/Tenv.mli +++ b/infer/src/IR/Tenv.mli @@ -41,7 +41,7 @@ val mk_struct : -> ?exported_objc_methods:Procname.t list -> ?supers:Typ.Name.t list -> ?annots:Annot.Item.t - -> ?java_class_kind:Struct.java_class_kind + -> ?java_class_info:Struct.java_class_info -> ?dummy:bool -> Typ.Name.t -> Struct.t diff --git a/infer/src/checkers/NullabilityPreanalysis.ml b/infer/src/checkers/NullabilityPreanalysis.ml index 113c3e292..f3287d8e7 100644 --- a/infer/src/checkers/NullabilityPreanalysis.ml +++ b/infer/src/checkers/NullabilityPreanalysis.ml @@ -78,11 +78,9 @@ let add_nonnull_to_fields fields tenv = match Typ.name typ with | Some typ_name -> ( match Tenv.lookup tenv typ_name with - | Some {fields; statics; supers; methods; annots} -> + | Some ({fields} as struct_typ) -> let fields_with_annot = List.map ~f:(add_nonnull_to_selected_field field) fields in - ignore - (Tenv.mk_struct tenv ~fields:fields_with_annot ~statics ~supers ~methods ~annots - typ_name) + ignore (Tenv.mk_struct tenv ~default:struct_typ ~fields:fields_with_annot typ_name) | None -> () ) | None -> diff --git a/infer/src/java/jClasspath.ml b/infer/src/java/jClasspath.ml index 1b83abdbe..f6d013d1c 100644 --- a/infer/src/java/jClasspath.ml +++ b/infer/src/java/jClasspath.ml @@ -199,13 +199,28 @@ type callee_status = Translated | Missing of JBasics.class_name * JBasics.method type classmap = JCode.jcode Javalib.interface_or_class JBasics.ClassMap.t +(** We store for each classname the location of its declaration. This map is filled during + JFrontend.compute_source_icfg and then it is used in JTransType.get_class_struct_typ before we + lose access to JClasspath.program. At the end, the information seats in each Struct.t (stored in + Tenv.t) *) +type java_location_map = Location.t JBasics.ClassMap.t + type program = { classpath_channel: Javalib.class_path ; mutable classmap: classmap + ; mutable java_location_map: java_location_map ; callees: callee_status Procname.Hash.t } let get_classmap program = program.classmap +let set_java_location program cn loc = + program.java_location_map <- JBasics.ClassMap.add cn loc program.java_location_map + + +let get_java_location program cn = + try Some (JBasics.ClassMap.find cn program.java_location_map) with Caml.Not_found -> None + + let mem_classmap cn program = JBasics.ClassMap.mem cn program.classmap let get_classpath_channel program = program.classpath_channel @@ -258,6 +273,7 @@ let load_program ~classpath classes = let program = { classpath_channel= Javalib.class_path classpath ; classmap= JBasics.ClassMap.empty + ; java_location_map= JBasics.ClassMap.empty ; callees= Procname.Hash.create 128 } in JBasics.ClassSet.iter (fun cn -> ignore (lookup_node cn program)) classes ; diff --git a/infer/src/java/jClasspath.mli b/infer/src/java/jClasspath.mli index 90b811fb4..be7373497 100644 --- a/infer/src/java/jClasspath.mli +++ b/infer/src/java/jClasspath.mli @@ -26,6 +26,10 @@ type program val get_classmap : program -> classmap +val set_java_location : program -> JBasics.class_name -> Location.t -> unit + +val get_java_location : program -> JBasics.class_name -> Location.t option + val mem_classmap : JBasics.class_name -> program -> bool val cleanup : program -> unit diff --git a/infer/src/java/jFrontend.ml b/infer/src/java/jFrontend.ml index 7b6b6b1eb..169f38d46 100644 --- a/infer/src/java/jFrontend.ml +++ b/infer/src/java/jFrontend.ml @@ -163,7 +163,7 @@ let create_icfg source_file program tenv icfg cn node = (* returns true for the set of classes that are selected to be translated in the given - progam *) + program *) let should_capture program package_opt source_basename node = let classname = Javalib.get_name node in let match_package pkg cn = @@ -194,6 +194,19 @@ let compute_source_icfg program tenv source_basename package_opt source_file = let select test procedure cn node = if test node then try procedure cn node with Bir.Subroutine -> () in + let set_java_location cn _node = + let cn_name = JBasics.cn_name cn in + let loc = JSourceFileInfo.class_name_location source_file cn_name in + L.debug Capture Verbose "set_java_location %s with location %a@." cn_name Location.pp_file_pos + loc ; + JClasspath.set_java_location program cn loc + in + (* we must set the java location for all classes in the source file before translation *) + let () = + JBasics.ClassMap.iter + (select (should_capture program package_opt source_basename) set_java_location) + (JClasspath.get_classmap program) + in let () = JBasics.ClassMap.iter (select diff --git a/infer/src/java/jSourceFileInfo.ml b/infer/src/java/jSourceFileInfo.ml new file mode 100644 index 000000000..56a5a1c35 --- /dev/null +++ b/infer/src/java/jSourceFileInfo.ml @@ -0,0 +1,13 @@ +(* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +open! IStd + +(** this is a naive/temporary implementation in a near diff, we will 1) parse the source file to + collect location datas for all class names 2) cache the result for later uses but we may have to + adapt a litle some signatures *) +let class_name_location file _cn : Location.t = {line= 0; col= 0; file} diff --git a/infer/src/java/jSourceFileInfo.mli b/infer/src/java/jSourceFileInfo.mli new file mode 100644 index 000000000..b05a009ae --- /dev/null +++ b/infer/src/java/jSourceFileInfo.mli @@ -0,0 +1,12 @@ +(* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +open! IStd + +val class_name_location : SourceFile.t -> string -> Location.t +(** [class_name_location source class_name] searches in file [source] the declaration location for + class name [class_name] *) diff --git a/infer/src/java/jTransType.ml b/infer/src/java/jTransType.ml index 0d326e57b..c1f83bb53 100644 --- a/infer/src/java/jTransType.ml +++ b/infer/src/java/jTransType.ml @@ -324,7 +324,16 @@ and get_class_struct_typ = let methods = Javalib.m_fold (fun m procnames -> translate_method_name program tenv m :: procnames) node [] in - Tenv.mk_struct tenv ~fields ~statics ~methods ~supers ~annots ~java_class_kind name + let node_name = Javalib.get_name node in + let java_location : Location.t option = JClasspath.get_java_location program node_name in + ( match java_location with + | Some loc -> + L.debug Capture Verbose "Java location %s -> %a@." (JBasics.cn_name node_name) + Location.pp_file_pos loc + | None -> + () ) ; + let java_class_info : Struct.java_class_info = {kind= java_class_kind; loc= java_location} in + Tenv.mk_struct tenv ~fields ~statics ~methods ~supers ~annots ~java_class_info name in fun program tenv cn -> let name = typename_of_classname cn in