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
master
David Pichardie 5 years ago committed by Facebook GitHub Bot
parent 3ccacb1f6e
commit 50c98db391

@ -12,7 +12,13 @@ type field = Fieldname.t * Typ.t * Annot.Item.t [@@deriving compare]
type fields = field list 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 for a structured value. *)
type t = type t =
@ -23,7 +29,7 @@ type t =
; methods: Procname.t list (** methods defined *) ; methods: Procname.t list (** methods defined *)
; exported_objc_methods: Procname.t list (** methods in ObjC interface, subset of [methods] *) ; exported_objc_methods: Procname.t list (** methods in ObjC interface, subset of [methods] *)
; annots: Annot.Item.t (** annotations *) ; 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 *) } ; dummy: bool (** dummy struct for class including static method *) }
type lookup = Typ.Name.t -> t option 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 let internal_mk_struct ?default ?fields ?statics ?methods ?exported_objc_methods ?supers ?annots
?java_class_kind ?dummy () = ?java_class_info ?dummy () =
let default_ = let default_ =
{ fields= [] { fields= []
; statics= [] ; statics= []
@ -69,14 +75,14 @@ let internal_mk_struct ?default ?fields ?statics ?methods ?exported_objc_methods
; supers= [] ; supers= []
; subs= Typ.Name.Set.empty ; subs= Typ.Name.Set.empty
; annots= Annot.Item.empty ; annots= Annot.Item.empty
; java_class_kind= None ; java_class_info= None
; dummy= false } ; dummy= false }
in in
let mk_struct_ ?(default = default_) ?(fields = default.fields) ?(statics = default.statics) let mk_struct_ ?(default = default_) ?(fields = default.fields) ?(statics = default.statics)
?(methods = default.methods) ?(exported_objc_methods = default.exported_objc_methods) ?(methods = default.methods) ?(exported_objc_methods = default.exported_objc_methods)
?(supers = default.supers) ?(subs = default.subs) ?(annots = default.annots) ?(supers = default.supers) ?(subs = default.subs) ?(annots = default.annots)
?(dummy = default.dummy) () = ?(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 in
mk_struct_ ?default ?fields ?statics ?methods ?exported_objc_methods ?supers ?annots ?dummy () mk_struct_ ?default ?fields ?statics ?methods ?exported_objc_methods ?supers ?annots ?dummy ()

@ -13,18 +13,24 @@ type field = Fieldname.t * Typ.t * Annot.Item.t [@@deriving compare]
type fields = field list 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 for a structured value. *)
type t = private type t =
{ fields: fields (** non-static fields *) { fields: fields (** non-static fields *)
; statics: fields (** 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 *) ; subs: Typ.Name.Set.t (** subclasses, initialized after merging type environments *)
; methods: Procname.t list (** methods defined *) ; methods: Procname.t list (** methods defined *)
; exported_objc_methods: Procname.t list (** methods in ObjC interface, subset of [methods] *) ; exported_objc_methods: Procname.t list (** methods in ObjC interface, subset of [methods] *)
; annots: Annot.Item.t (** annotations *) ; 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 *) } ; dummy: bool (** dummy struct for class including static method *) }
type lookup = Typ.Name.t -> t option type lookup = Typ.Name.t -> t option
@ -42,7 +48,7 @@ val internal_mk_struct :
-> ?exported_objc_methods:Procname.t list -> ?exported_objc_methods:Procname.t list
-> ?supers:Typ.Name.t list -> ?supers:Typ.Name.t list
-> ?annots:Annot.Item.t -> ?annots:Annot.Item.t
-> ?java_class_kind:java_class_kind -> ?java_class_info:java_class_info
-> ?dummy:bool -> ?dummy:bool
-> unit -> unit
-> t -> t

@ -30,10 +30,10 @@ let create () = TypenameHash.create 1000
(** Construct a struct type in a type environment *) (** Construct a struct type in a type environment *)
let mk_struct tenv ?default ?fields ?statics ?methods ?exported_objc_methods ?supers ?annots 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 = let struct_typ =
Struct.internal_mk_struct ?default ?fields ?statics ?methods ?exported_objc_methods ?supers Struct.internal_mk_struct ?default ?fields ?statics ?methods ?exported_objc_methods ?supers
?annots ?java_class_kind ?dummy () ?annots ?java_class_info ?dummy ()
in in
TypenameHash.replace tenv name struct_typ ; TypenameHash.replace tenv name struct_typ ;
struct_typ struct_typ
@ -224,13 +224,15 @@ let get_summary_formals tenv ~get_summary ~get_formals =
`NotFound `NotFound
| Some class_name -> ( | Some class_name -> (
match lookup tenv class_name with match lookup tenv class_name with
| Some {Struct.java_class_kind= Some Interface; subs} | Some {Struct.java_class_info= Some info; subs}
when Int.equal (Typ.Name.Set.cardinal subs) 1 -> 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 let unique_sub = Typ.Name.Set.choose subs in
Logging.d_printfln_escaped "Found a unique sub-class %a" Typ.Name.pp unique_sub ; 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 let sub_pname = Procname.replace_class pname unique_sub in
get_summary_formals_aux sub_pname |> found_from_subclass sub_pname 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 -> 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 ; Logging.d_printfln_escaped "Found an arbitrary sub-class %a" Typ.Name.pp sub ;
let sub_pname = Procname.replace_class pname sub in let sub_pname = Procname.replace_class pname sub in

@ -41,7 +41,7 @@ val mk_struct :
-> ?exported_objc_methods:Procname.t list -> ?exported_objc_methods:Procname.t list
-> ?supers:Typ.Name.t list -> ?supers:Typ.Name.t list
-> ?annots:Annot.Item.t -> ?annots:Annot.Item.t
-> ?java_class_kind:Struct.java_class_kind -> ?java_class_info:Struct.java_class_info
-> ?dummy:bool -> ?dummy:bool
-> Typ.Name.t -> Typ.Name.t
-> Struct.t -> Struct.t

@ -78,11 +78,9 @@ let add_nonnull_to_fields fields tenv =
match Typ.name typ with match Typ.name typ with
| Some typ_name -> ( | Some typ_name -> (
match Tenv.lookup tenv typ_name with 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 let fields_with_annot = List.map ~f:(add_nonnull_to_selected_field field) fields in
ignore ignore (Tenv.mk_struct tenv ~default:struct_typ ~fields:fields_with_annot typ_name)
(Tenv.mk_struct tenv ~fields:fields_with_annot ~statics ~supers ~methods ~annots
typ_name)
| None -> | None ->
() ) () )
| None -> | None ->

@ -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 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 = type program =
{ classpath_channel: Javalib.class_path { classpath_channel: Javalib.class_path
; mutable classmap: classmap ; mutable classmap: classmap
; mutable java_location_map: java_location_map
; callees: callee_status Procname.Hash.t } ; callees: callee_status Procname.Hash.t }
let get_classmap program = program.classmap 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 mem_classmap cn program = JBasics.ClassMap.mem cn program.classmap
let get_classpath_channel program = program.classpath_channel let get_classpath_channel program = program.classpath_channel
@ -258,6 +273,7 @@ let load_program ~classpath classes =
let program = let program =
{ classpath_channel= Javalib.class_path classpath { classpath_channel= Javalib.class_path classpath
; classmap= JBasics.ClassMap.empty ; classmap= JBasics.ClassMap.empty
; java_location_map= JBasics.ClassMap.empty
; callees= Procname.Hash.create 128 } ; callees= Procname.Hash.create 128 }
in in
JBasics.ClassSet.iter (fun cn -> ignore (lookup_node cn program)) classes ; JBasics.ClassSet.iter (fun cn -> ignore (lookup_node cn program)) classes ;

@ -26,6 +26,10 @@ type program
val get_classmap : program -> classmap 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 mem_classmap : JBasics.class_name -> program -> bool
val cleanup : program -> unit val cleanup : program -> unit

@ -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 (* 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 should_capture program package_opt source_basename node =
let classname = Javalib.get_name node in let classname = Javalib.get_name node in
let match_package pkg cn = 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 = let select test procedure cn node =
if test node then try procedure cn node with Bir.Subroutine -> () if test node then try procedure cn node with Bir.Subroutine -> ()
in 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 () = let () =
JBasics.ClassMap.iter JBasics.ClassMap.iter
(select (select

@ -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}

@ -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] *)

@ -324,7 +324,16 @@ and get_class_struct_typ =
let methods = let methods =
Javalib.m_fold (fun m procnames -> translate_method_name program tenv m :: procnames) node [] Javalib.m_fold (fun m procnames -> translate_method_name program tenv m :: procnames) node []
in 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 in
fun program tenv cn -> fun program tenv cn ->
let name = typename_of_classname cn in let name = typename_of_classname cn in

Loading…
Cancel
Save