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