@ -41,7 +41,11 @@ module Branch = struct
let is_top = function Top -> true | True | False | Lt _ | Gt _ | Le _ | Ge _ -> false
let is_top = function Top -> true | True | False | Lt _ | Gt _ | Le _ | Ge _ -> false
end
end
module ConfigChecks = AbstractDomain . SafeInvertedMap ( ConfigName ) ( Branch )
module ConfigChecks = struct
include AbstractDomain . SafeInvertedMap ( ConfigName ) ( Branch )
let add_all x ~ into = fold ( fun k v acc -> add k v acc ) x into
end
module Field = struct
module Field = struct
include Fieldname
include Fieldname
@ -162,29 +166,140 @@ end
module ConfigLifted = AbstractDomain . Flat ( ConfigName )
module ConfigLifted = AbstractDomain . Flat ( ConfigName )
module TempBool = struct
type t =
| Bot (* * bottom *)
| True of ConfigChecks . t (* * config checks when the temporary boolean is true *)
| False of ConfigChecks . t (* * config checks when the temporary boolean is false *)
| Joined of { true _ : ConfigChecks . t ; false _ : ConfigChecks . t }
(* * config checks when the temporary boolean is true or false *)
let pp =
let pp_helper f ( b , config_checks ) =
F . fprintf f " @[%b when %a checked@] " b ConfigChecks . pp config_checks
in
fun f -> function
| Bot ->
AbstractDomain . BottomLiftedUtils . pp_bottom f
| True config_checks ->
pp_helper f ( true , config_checks )
| False config_checks ->
pp_helper f ( false , config_checks )
| Joined { true _ ; false _ } ->
F . fprintf f " @[%a@ %a@] " pp_helper ( true , true _ ) pp_helper ( false , false _ )
let leq ~ lhs ~ rhs =
match ( lhs , rhs ) with
| Bot , _ ->
true
| _ , Bot ->
false
| True c1 , True c2
| False c1 , False c2
| True c1 , Joined { true _ = c2 }
| False c1 , Joined { false _ = c2 } ->
ConfigChecks . leq ~ lhs : c1 ~ rhs : c2
| Joined { true _ = t1 ; false _ = f1 } , Joined { true _ = t2 ; false _ = f2 } ->
ConfigChecks . leq ~ lhs : t1 ~ rhs : t2 && ConfigChecks . leq ~ lhs : f1 ~ rhs : f2
| _ , _ ->
false
let join x y =
match ( x , y ) with
| Bot , v | v , Bot ->
v
| True c1 , True c2 ->
True ( ConfigChecks . join c1 c2 )
| False c1 , False c2 ->
False ( ConfigChecks . join c1 c2 )
| True c1 , False c2 | False c2 , True c1 ->
Joined { true _ = c1 ; false _ = c2 }
| True c1 , Joined { true _ = c2 ; false _ } | Joined { true _ = c1 ; false _ } , True c2 ->
Joined { true _ = ConfigChecks . join c1 c2 ; false _ }
| False c1 , Joined { true _ ; false _ = c2 } | Joined { true _ ; false _ = c1 } , False c2 ->
Joined { true _ ; false _ = ConfigChecks . join c1 c2 }
| Joined { true _ = t1 ; false _ = f1 } , Joined { true _ = t2 ; false _ = f2 } ->
Joined { true _ = ConfigChecks . join t1 t2 ; false _ = ConfigChecks . join f1 f2 }
let widen ~ prev ~ next ~ num_iters =
match ( prev , next ) with
| Bot , v | v , Bot ->
v
| True c1 , True c2 ->
True ( ConfigChecks . widen ~ prev : c1 ~ next : c2 ~ num_iters )
| False c1 , False c2 ->
False ( ConfigChecks . widen ~ prev : c1 ~ next : c2 ~ num_iters )
| True c1 , False c2 | False c2 , True c1 ->
Joined { true _ = c1 ; false _ = c2 }
| True c1 , Joined { true _ = c2 ; false _ } | Joined { true _ = c1 ; false _ } , True c2 ->
Joined { true _ = ConfigChecks . widen ~ prev : c1 ~ next : c2 ~ num_iters ; false _ }
| False c1 , Joined { true _ ; false _ = c2 } | Joined { true _ ; false _ = c1 } , False c2 ->
Joined { true _ ; false _ = ConfigChecks . widen ~ prev : c1 ~ next : c2 ~ num_iters }
| Joined { true _ = t1 ; false _ = f1 } , Joined { true _ = t2 ; false _ = f2 } ->
Joined
{ true _ = ConfigChecks . widen ~ prev : t1 ~ next : t2 ~ num_iters
; false _ = ConfigChecks . widen ~ prev : f1 ~ next : f2 ~ num_iters }
let bottom = Bot
let make ~ is_true config_checks = if is_true then True config_checks else False config_checks
let get_config_checks_true = function
| True config_checks | Joined { true _ = config_checks } ->
Some config_checks
| _ ->
None
let get_config_checks_false = function
| False config_checks | Joined { false _ = config_checks } ->
Some config_checks
| _ ->
None
let get_config_checks ~ is_true x =
if is_true then get_config_checks_true x else get_config_checks_false x
end
module Val = struct
module Val = struct
type t = { config : ConfigLifted . t ; fields : Fields . t }
type t = { config : ConfigLifted . t ; fields : Fields . t ; temp_bool : TempBool . t }
let pp f { config ; fields } =
let pp f { config ; fields ; temp_bool } =
F . fprintf f " @[@[config:@,%a@]@ @[fields:@,%a@]@] " ConfigLifted . pp config Fields . pp fields
F . fprintf f " @[@[config:@,%a@]@ @[fields:@,%a@]@ @[temp_bool:@,%a@]@] " ConfigLifted . pp config
Fields . pp fields TempBool . pp temp_bool
let leq ~ lhs ~ rhs =
let leq ~ lhs ~ rhs =
ConfigLifted . leq ~ lhs : lhs . config ~ rhs : rhs . config && Fields . leq ~ lhs : lhs . fields ~ rhs : rhs . fields
ConfigLifted . leq ~ lhs : lhs . config ~ rhs : rhs . config
&& Fields . leq ~ lhs : lhs . fields ~ rhs : rhs . fields
&& TempBool . leq ~ lhs : lhs . temp_bool ~ rhs : rhs . temp_bool
let join x y = { config = ConfigLifted . join x . config y . config ; fields = Fields . join x . fields y . fields }
let join x y =
{ config = ConfigLifted . join x . config y . config
; fields = Fields . join x . fields y . fields
; temp_bool = TempBool . join x . temp_bool y . temp_bool }
let widen ~ prev ~ next ~ num_iters =
let widen ~ prev ~ next ~ num_iters =
{ config = ConfigLifted . widen ~ prev : prev . config ~ next : next . config ~ num_iters
{ config = ConfigLifted . widen ~ prev : prev . config ~ next : next . config ~ num_iters
; fields = Fields . widen ~ prev : prev . fields ~ next : next . fields ~ num_iters }
; fields = Fields . widen ~ prev : prev . fields ~ next : next . fields ~ num_iters
; temp_bool = TempBool . widen ~ prev : prev . temp_bool ~ next : next . temp_bool ~ num_iters }
let bottom = { config = ConfigLifted . bottom ; fields = Fields . bottom }
let bottom = { config = ConfigLifted . bottom ; fields = Fields . bottom ; temp_bool = TempBool . bottom }
let of_config config = { bottom with config = ConfigLifted . v config }
let of_config config = { bottom with config = ConfigLifted . v config }
let of_field fn = { bottom with fields = Fields . singleton fn }
let of_field fn = { bottom with fields = Fields . singleton fn }
let of_temp_bool ~ is_true config_checks =
{ bottom with temp_bool = TempBool . make ~ is_true config_checks }
end
end
module Mem = struct
module Mem = struct
@ -328,10 +443,10 @@ module Dom = struct
let apply_cmp_branch cmp_branch res =
let apply_cmp_branch cmp_branch res =
Option . bind res ~ f : ( function
Option . bind res ~ f : ( function
| ` Unconditional config , Branch . True ->
| ` Unconditional ( config , Branch . True ) ->
Some ( ` Unconditional config , cmp_branch )
Some ( ` Unconditional ( config , cmp_branch ) )
| ` Conditional fields , Branch . True ->
| ` Conditional ( fields , Branch . True ) ->
Some ( ` Conditional fields , cmp_branch )
Some ( ` Conditional ( fields , cmp_branch ) )
| _ ->
| _ ->
None )
None )
@ -339,13 +454,18 @@ module Dom = struct
let rec get_config_check_prune ~ is_true_branch e mem =
let rec get_config_check_prune ~ is_true_branch e mem =
match ( e : Exp . t ) with
match ( e : Exp . t ) with
| Var id -> (
| Var id -> (
let { Val . config ; fields } = Mem . lookup ( Loc . of_id id ) mem in
let { Val . config ; fields ; temp_bool } = Mem . lookup ( Loc . of_id id ) mem in
let branch = if is_true_branch then Branch . True else Branch . False in
let branch = if is_true_branch then Branch . True else Branch . False in
match ConfigLifted . get config with
match ConfigLifted . get config with
| Some config ->
| Some config ->
Some ( ` Unconditional config , branch )
Some ( ` Unconditional ( config , branch ) )
| None when not ( Fields . is_empty fields ) ->
Some ( ` Conditional ( fields , branch ) )
| None ->
| None ->
Option . some_if ( not ( Fields . is_empty fields ) ) ( ` Conditional fields , branch ) )
(* Note: If the [id] is not a config value and not a field value, address it as a
temporary variable . * )
TempBool . get_config_checks ~ is_true : is_true_branch temp_bool
| > Option . map ~ f : ( fun config_checks -> ` TempBool config_checks ) )
| UnOp ( LNot , e , _ ) ->
| UnOp ( LNot , e , _ ) ->
get_config_check_prune ~ is_true_branch : ( not is_true_branch ) e mem
get_config_check_prune ~ is_true_branch : ( not is_true_branch ) e mem
| BinOp ( ( Eq | Ne | Lt | Gt | Le | Ge ) , Const _ , Const _ ) ->
| BinOp ( ( Eq | Ne | Lt | Gt | Le | Ge ) , Const _ , Const _ ) ->
@ -375,9 +495,11 @@ module Dom = struct
let prune e ( { config_checks ; field_checks ; mem } as astate ) =
let prune e ( { config_checks ; field_checks ; mem } as astate ) =
get_config_check_prune ~ is_true_branch : true e mem
get_config_check_prune ~ is_true_branch : true e mem
| > Option . value_map ~ default : astate ~ f : ( function
| > Option . value_map ~ default : astate ~ f : ( function
| ` Unconditional config , branch ->
| ` Unconditional ( config , branch ) ->
{ astate with config_checks = ConfigChecks . add config branch config_checks }
{ astate with config_checks = ConfigChecks . add config branch config_checks }
| ` Conditional fields , branch ->
| ` TempBool config_checks' ->
{ astate with config_checks = ConfigChecks . add_all config_checks' ~ into : config_checks }
| ` Conditional ( fields , branch ) ->
{ astate with
{ astate with
field_checks =
field_checks =
Fields . fold ( fun field acc -> FieldChecks . add field branch acc ) fields field_checks
Fields . fold ( fun field acc -> FieldChecks . add field branch acc ) fields field_checks
@ -511,6 +633,7 @@ module TransferFunctions = struct
let dispatch : ( Tenv . t , unit , unit ) dispatcher =
let dispatch : ( Tenv . t , unit , unit ) dispatcher =
make_dispatcher
make_dispatcher
[ + PatternMatch . Java . implements_lang " Boolean " & :: " booleanValue " & - -> ()
[ + PatternMatch . Java . implements_lang " Boolean " & :: " booleanValue " & - -> ()
; + PatternMatch . Java . implements_lang " Boolean " & :: " valueOf " & - -> ()
; + PatternMatch . Java . implements_lang " Long " & :: " longValue " & - -> () ]
; + PatternMatch . Java . implements_lang " Long " & :: " longValue " & - -> () ]
in
in
fun tenv pname -> dispatch tenv pname | > Option . is_some
fun tenv pname -> dispatch tenv pname | > Option . is_some
@ -563,13 +686,17 @@ module TransferFunctions = struct
astate
astate
let exec_instr astate { interproc = { tenv ; analyze_dependency } ; get_instantiated_cost } node idx
let exec_instr ( { Dom . config_checks } as astate )
instr =
{ interproc = { tenv ; analyze_dependency } ; get_instantiated_cost } node idx instr =
match ( instr : Sil . instr ) with
match ( instr : Sil . instr ) with
| Load { id ; e = Lvar pvar } ->
| Load { id ; e = Lvar pvar } ->
Dom . load_config id pvar astate
Dom . load_config id pvar astate
| Load { id ; e = Lfield ( _ , fn , _ ) } ->
| Load { id ; e = Lfield ( _ , fn , _ ) } ->
Dom . load_field id fn astate
Dom . load_field id fn astate
| Store { e1 = Lvar pvar ; e2 = Const zero } when Const . iszero_int_float zero ->
Dom . add_mem ( Loc . of_pvar pvar ) ( Val . of_temp_bool ~ is_true : false config_checks ) astate
| Store { e1 = Lvar pvar ; e2 = Const one } when Const . isone_int_float one ->
Dom . add_mem ( Loc . of_pvar pvar ) ( Val . of_temp_bool ~ is_true : true config_checks ) astate
| Store { e1 = Lvar pvar ; e2 = Var id } ->
| Store { e1 = Lvar pvar ; e2 = Var id } ->
Dom . store_config pvar id astate
Dom . store_config pvar id astate
| Store { e1 = Lfield ( _ , fn , _ ) ; e2 = Var id } ->
| Store { e1 = Lfield ( _ , fn , _ ) ; e2 = Var id } ->