Summary: This diff uses a type parameter of `Provider.get` to decide whether assigning expensive cost to the function call or not. For example, if the type is small one like `Provider<Integer>`, it be evaluated to have a unit cost, otherwise a linear cost. To get the return type of `Provider.get`, I added a simple analyzer that collects "casted" types backwards. In Sil, while the function call statement loses the return type, e.g, ``` n$5=_fun_Object Provider.get()(n$3:javax.inject.Provider*); ``` the `n$5`'s value is usually casted to a specific type at some point later. ``` *&$irvar0:java.lang.Object*=n$5 n$8=*&$irvar0:java.lang.Object* n$9=_fun___cast(n$8:java.lang.Object*,sizeof(t=java.lang.Integer;sub_t=( sub )(cast)):void) ``` So, the analyzer starts from the cast statements backward, collecting the types to cast for each variables. Reviewed By: ezgicicek Differential Revision: D20345268 fbshipit-source-id: 704b42ec1master
parent
1c9bafc2a9
commit
020cd199b5
@ -0,0 +1,103 @@
|
|||||||
|
(*
|
||||||
|
* 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
|
||||||
|
module CFG = ProcCfg.Backward (ProcCfg.NormalOneInstrPerNode)
|
||||||
|
module F = Format
|
||||||
|
|
||||||
|
module Loc = struct
|
||||||
|
type t = Ident of Ident.t | Pvar of Pvar.t [@@deriving compare]
|
||||||
|
|
||||||
|
let pp f = function Ident id -> Ident.pp f id | Pvar pvar -> Pvar.pp Pp.text f pvar
|
||||||
|
end
|
||||||
|
|
||||||
|
module Val = struct
|
||||||
|
include AbstractDomain.FiniteSet (struct
|
||||||
|
include Typ
|
||||||
|
|
||||||
|
let pp = pp Pp.text
|
||||||
|
end)
|
||||||
|
|
||||||
|
let is_integer_type =
|
||||||
|
let is_integer_type = function
|
||||||
|
| Typ.{desc= Tstruct (JavaClass name)} ->
|
||||||
|
JavaClassName.(equal name java_lang_integer)
|
||||||
|
| _ ->
|
||||||
|
false
|
||||||
|
in
|
||||||
|
fun x -> (not (is_empty x)) && for_all is_integer_type x
|
||||||
|
end
|
||||||
|
|
||||||
|
module Dom = struct
|
||||||
|
include AbstractDomain.Map (Loc) (Val)
|
||||||
|
|
||||||
|
let lookup l m = Option.value (find_opt l m) ~default:Val.bottom
|
||||||
|
|
||||||
|
let lookup_ident id m = lookup (Loc.Ident id) m
|
||||||
|
|
||||||
|
let lookup_pvar pvar m = lookup (Loc.Pvar pvar) m
|
||||||
|
|
||||||
|
let cast id typ m =
|
||||||
|
let f = function None -> Some (Val.singleton typ) | Some prev -> Some (Val.add typ prev) in
|
||||||
|
update (Loc.Ident id) f m
|
||||||
|
|
||||||
|
|
||||||
|
let load id pvar m = add (Loc.Pvar pvar) (lookup_ident id m) m
|
||||||
|
|
||||||
|
let store pvar id m = add (Loc.Ident id) (lookup_pvar pvar m) m
|
||||||
|
end
|
||||||
|
|
||||||
|
module TransferFunctions = struct
|
||||||
|
module CFG = CFG
|
||||||
|
module Domain = Dom
|
||||||
|
|
||||||
|
type extras = unit
|
||||||
|
|
||||||
|
let exec_instr mem _pdata _node = function
|
||||||
|
| Sil.Load {id; e= Exp.Lvar pvar} ->
|
||||||
|
Dom.load id pvar mem
|
||||||
|
| Sil.Store {e1= Exp.Lvar pvar; e2= Exp.Var id} ->
|
||||||
|
Dom.store pvar id mem
|
||||||
|
| Sil.Call (_, Const (Cfun callee_pname), (Exp.Var id, _) :: (Exp.Sizeof {typ}, _) :: _, _, _)
|
||||||
|
when Procname.equal callee_pname BuiltinDecl.__cast ->
|
||||||
|
Dom.cast id typ mem
|
||||||
|
| _ ->
|
||||||
|
mem
|
||||||
|
|
||||||
|
|
||||||
|
let pp_session_name node f = F.fprintf f "Cast type analysis %a" CFG.Node.pp_id (CFG.Node.id node)
|
||||||
|
end
|
||||||
|
|
||||||
|
module Analyzer = AbstractInterpreter.MakeWTO (TransferFunctions)
|
||||||
|
|
||||||
|
let compute_invariant_map summary tenv =
|
||||||
|
let pdata = ProcData.make summary tenv () in
|
||||||
|
Analyzer.exec_pdesc ~do_narrowing:false ~initial:Dom.bottom pdata
|
||||||
|
|
||||||
|
|
||||||
|
type get_cast_type = Ident.t -> Val.t
|
||||||
|
|
||||||
|
let compute_get_cast_type =
|
||||||
|
let cache_get, cache_set = Procname.UnitCache.create () in
|
||||||
|
fun {Callbacks.exe_env; summary} ->
|
||||||
|
let pname = Summary.get_proc_name summary in
|
||||||
|
let casted_types =
|
||||||
|
match cache_get pname with
|
||||||
|
| Some casted_types ->
|
||||||
|
casted_types
|
||||||
|
| None ->
|
||||||
|
let pdesc = Summary.get_proc_desc summary in
|
||||||
|
let cfg = CFG.from_pdesc pdesc in
|
||||||
|
let tenv = Exe_env.get_tenv exe_env pname in
|
||||||
|
let inv_map = compute_invariant_map summary tenv in
|
||||||
|
let exit_node_id = CFG.exit_node cfg |> CFG.Node.id in
|
||||||
|
let casted_types =
|
||||||
|
Analyzer.extract_post exit_node_id inv_map |> Option.value ~default:Dom.bottom
|
||||||
|
in
|
||||||
|
cache_set pname casted_types ; casted_types
|
||||||
|
in
|
||||||
|
fun id -> Dom.lookup_ident id casted_types
|
@ -0,0 +1,18 @@
|
|||||||
|
(*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
module Val : sig
|
||||||
|
type t
|
||||||
|
|
||||||
|
val is_integer_type : t -> bool
|
||||||
|
end
|
||||||
|
|
||||||
|
type get_cast_type = Ident.t -> Val.t
|
||||||
|
|
||||||
|
val compute_get_cast_type : Callbacks.proc_callback_args -> Ident.t -> Val.t
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
import javax.inject.*;
|
||||||
|
|
||||||
|
class ProviderTest {
|
||||||
|
/* Assume that injecting A is expensive, on the other hand injecting Integer is cheap. */
|
||||||
|
class A {}
|
||||||
|
|
||||||
|
@Inject private Provider<A> mProviderA;
|
||||||
|
@Inject private Provider<Integer> mProviderInteger;
|
||||||
|
|
||||||
|
void use_provided_A(A a) {}
|
||||||
|
|
||||||
|
void use_provided_Integer(Integer i) {}
|
||||||
|
|
||||||
|
void expensive_get_A_constant() {
|
||||||
|
use_provided_A(mProviderA.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void cheap_get_Integer_linear() {
|
||||||
|
use_provided_Integer(mProviderInteger.get());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue