From ba62932760039af166b688e2071c2bcce294b15a Mon Sep 17 00:00:00 2001 From: Jeremy Dubreil Date: Wed, 16 Nov 2016 08:46:07 -0800 Subject: [PATCH] [infer][java] Translate the local variables directly from the bytecode Summary: Translate local variable names using the bytecode directly instead of JBir. The bytecode has more precise type information. We still need to declare the temporary variables intruduced by Sawja until we can base the translation directly on the bytecode. Reviewed By: sblackshear Differential Revision: D4185309 fbshipit-source-id: 81904a0 --- infer/src/java/jFrontend.ml | 8 ++-- infer/src/java/jTrans.ml | 76 ++++++++++++++++++++++++------------- infer/src/java/jTrans.mli | 2 +- 3 files changed, 55 insertions(+), 31 deletions(-) diff --git a/infer/src/java/jFrontend.ml b/infer/src/java/jFrontend.ml index fbe59a7bf..80f5eb869 100644 --- a/infer/src/java/jFrontend.ml +++ b/infer/src/java/jFrontend.ml @@ -79,7 +79,7 @@ let add_cmethod source_file program linereader icfg cm proc_name = let cn, _ = JBasics.cms_split cm.Javalib.cm_class_method_signature in match JTrans.create_cm_procdesc source_file program linereader icfg cm proc_name with | None -> () - | Some (procdesc, impl) -> + | Some (procdesc, _, jbir_code) -> let start_node = Procdesc.get_start_node procdesc in let exit_node = Procdesc.get_exit_node procdesc in let exn_node = @@ -87,11 +87,11 @@ let add_cmethod source_file program linereader icfg cm proc_name = | Some node -> node | None -> failwithf "No exn node found for %s" (Procname.to_string proc_name) in - let instrs = JBir.code impl in + let instrs = JBir.code jbir_code in let context = - JContext.create_context icfg procdesc impl cn source_file program in + JContext.create_context icfg procdesc jbir_code cn source_file program in let method_body_nodes = Array.mapi (JTrans.instruction context) instrs in - add_edges context start_node exn_node [exit_node] method_body_nodes impl false + add_edges context start_node exn_node [exit_node] method_body_nodes jbir_code false let path_of_cached_classname cn = diff --git a/infer/src/java/jTrans.ml b/infer/src/java/jTrans.ml index b95201fd6..ec3ce467d 100644 --- a/infer/src/java/jTrans.ml +++ b/infer/src/java/jTrans.ml @@ -134,30 +134,50 @@ let formals_from_signature program tenv cn ms kind = | Procname.Non_Static -> [(JConfig.this, JTransType.get_class_type program tenv cn)] in IList.rev (IList.fold_left collect init_arg_list (JBasics.ms_args ms)) -let formals program tenv cn impl = +(** Creates the list of formal variables from a procedure based on ... *) +let translate_formals program tenv cn impl = let collect l (vt, var) = let name = Mangled.from_string (JBir.var_name_g var) in let typ = JTransType.param_type program tenv cn var vt in (name, typ):: l in IList.rev (IList.fold_left collect [] (JBir.params impl)) -(** Creates the local and formal variables from a procedure based on the - impl argument. If the meth_kind is Init, we add a parameter field to - the initialiser method. *) -let locals_formals program tenv cn impl = - let form_list = formals program tenv cn impl in - let is_formal p = - IList.exists (fun (p', _) -> Mangled.equal p p') form_list in - let collect l var = - let vname = Mangled.from_string (JBir.var_name_g var) in - let names = (fst (IList.split l)) in - if not (is_formal vname) && (not (IList.mem Mangled.equal vname names)) then - (vname, Typ.Tvoid):: l +(** Creates the list of local variables from the bytecode and add the variables from + the JBir representation *) +let translate_locals program tenv formals bytecode jbir_code = + let formal_set = + IList.fold_left + (fun set (var, _) -> Mangled.MangledSet.add var set) + Mangled.MangledSet.empty + formals in + let collect (seen_vars, l) (var, typ) = + if Mangled.MangledSet.mem var seen_vars then + (seen_vars, l) else - l in - let vars = JBir.vars impl in - let loc_list = IList.rev (Array.fold_left collect [] vars) in - (loc_list, form_list) + (Mangled.MangledSet.add var seen_vars, (var, typ) :: l) in + let with_bytecode_vars = + (* Do not consider parameters as local variables *) + let init = (formal_set, []) in + match bytecode.JCode.c_local_variable_table with + | None -> init + | Some variable_table -> + IList.fold_left + (fun accu (_, _, var_name, var_type, _) -> + let var = Mangled.from_string var_name + and typ = JTransType.value_type program tenv var_type in + collect accu (var, typ)) + init + variable_table in + (* TODO (#4040807): Needs to add the JBir temporary variables since other parts of the + code are still relying on those *) + let with_jbir_vars = + Array.fold_left + (fun accu jbir_var -> + let var = Mangled.from_string (JBir.var_name_g jbir_var) in + collect accu (var, Typ.Tvoid)) + with_bytecode_vars + (JBir.vars jbir_code) in + snd with_jbir_vars let get_constant (c : JBir.const) = match c with @@ -221,7 +241,7 @@ let get_implementation cm = bytecode with this instruction. hack around this problem by converting all invokedynamic's to invokestatic's that call a method with the same signature as the lambda on java.lang.Object. this isn't great, but it's a lot better than crashing *) - let code = Lazy.force t in + let bytecode = Lazy.force t in let c_code = Array.map (function @@ -229,9 +249,12 @@ let get_implementation cm = JCode.OpInvoke (`Static JBasics.java_lang_object, ms) | opcode -> opcode) - code.JCode.c_code in - let code' = { code with JCode.c_code; } in - JBir.transform ~bcv: false ~ch_link: false ~formula: false ~formula_cmd:[] cm code' + bytecode.JCode.c_code in + let hacked_bytecode = { bytecode with JCode.c_code; } in + let jbir_code = + JBir.transform + ~bcv: false ~ch_link: false ~formula: false ~formula_cmd:[] cm hacked_bytecode in + (hacked_bytecode, jbir_code) let update_constr_loc cn ms loc_start = if (JBasics.ms_name ms) = JConfig.constructor_name then @@ -309,14 +332,15 @@ let create_cm_procdesc source_file program linereader icfg cm proc_name = let m = Javalib.ConcreteMethod cm in let cn, ms = JBasics.cms_split (Javalib.get_class_method_signature m) in try - let impl = get_implementation cm in + let bytecode, jbir_code = get_implementation cm in let procdesc = - let locals, formals = locals_formals program tenv cn impl in + let formals = translate_formals program tenv cn jbir_code in + let locals = translate_locals program tenv formals bytecode jbir_code in let loc_start = - let loc = get_location source_file impl 0 in + let loc = get_location source_file jbir_code 0 in fix_method_definition_line linereader proc_name loc in let loc_exit = - get_location source_file impl (Array.length (JBir.code impl) - 1) in + get_location source_file jbir_code (Array.length (JBir.code jbir_code) - 1) in let method_annotation = JAnnotation.translate_method proc_name cm.Javalib.cm_annotations in update_constr_loc cn ms loc_start; @@ -348,7 +372,7 @@ let create_cm_procdesc source_file program linereader icfg cm proc_name = Procdesc.set_exit_node procdesc exit_node; Procdesc.Node.add_locals_ret_declaration start_node proc_attributes locals; procdesc in - Some (procdesc, impl) + Some (procdesc, bytecode, jbir_code) with JBir.Subroutine -> L.do_err "create_procdesc raised JBir.Subroutine on %a@." diff --git a/infer/src/java/jTrans.mli b/infer/src/java/jTrans.mli index d856f98de..3c74663a2 100644 --- a/infer/src/java/jTrans.mli +++ b/infer/src/java/jTrans.mli @@ -43,7 +43,7 @@ val create_cm_procdesc : JContext.icfg -> JCode.jcode Javalib.concrete_method -> Procname.t -> - (Procdesc.t * JBir.t) option + (Procdesc.t * Javalib_pack.JCode.jcode * JBir.t) option (** translates an instruction into a statement node or prune nodes in the cfg *) val instruction : JContext.t -> int -> JBir.instr -> translation