@ -19,18 +19,25 @@ module IgnoreVar = struct
end
(* * access path that does not ignore the type ( like the original AccessPath.t ) but which instead
ignores the root variable for comparisons ; this is taken care of by the root type * )
ignores the root variable for comparisons . * )
type path = ( IgnoreVar . t * Typ . t ) * AccessPath . access list [ @@ deriving compare , equal ]
type root =
| Global of Mangled . t
| Class of Typ . name
| Parameter of int (* * method parameter represented by its 0-indexed position *)
type t =
| Global of { path : AccessPath . t } (* * [AccessPath] so as to include root var in comparison *)
| Class of { typename : Typ . Name . t } (* * Java-only class object identified by typename *)
| Parameter of { index : int ; path : path }
(* * method parameter represented by its 0-indexed position *)
[ @@ deriving compare , equal ]
type t = { root : root ; path : path } [ @@ deriving compare , equal ]
let get_typ tenv =
let class_type = Typ . ( mk ( Tstruct Name . Java . java_lang_class ) ) in
let some_ptr_to_class_type = Some Typ . ( mk ( Tptr ( class_type , Pk_pointer ) ) ) in
function
| Class _ ->
some_ptr_to_class_type
| Global { path } | Parameter { path } ->
AccessPath . get_typ path tenv
let get_typ tenv t = AccessPath . get_typ t . path tenv
let rec norm_path tenv ( ( typ , ( accesses : AccessPath . access list ) ) as path ) =
match accesses with
@ -45,38 +52,18 @@ let rec norm_path tenv ((typ, (accesses : AccessPath.access list)) as path) =
let equal_across_threads tenv t1 t2 =
match ( t1 . root , t2 . root ) with
| Global _ , Global _ | Class _ , Class _ ->
(* globals and class objects must be identical across threads *)
equal t1 t2
| Parameter _ , Parameter _ ->
let ( ( _ , typ1 ) , accesses1 ) , ( ( _ , typ2 ) , accesses2 ) = ( t1 . path , t2 . path ) in
match ( t1 , t2 ) with
| Parameter { path = ( _ , typ1 ) , accesses1 } , Parameter { path = ( _ , typ2 ) , accesses2 } ->
(* parameter position/names can be ignored across threads, if types and accesses are equal *)
let path1 , path2 = ( norm_path tenv ( typ1 , accesses1 ) , norm_path tenv ( typ2 , accesses2 ) ) in
[ % equal : Typ . t * AccessPath . access list ] path1 path2
| _ , _ ->
false
let is_class_object = function { root = Class _ } -> true | _ -> false
(* using an indentifier for a class object, create an access path representing that lock;
this is for synchronizing on Java class objects only * )
let path_of_java_class =
let typ = Typ . ( mk ( Tstruct Name . Java . java_lang_class ) ) in
let typ' = Typ . ( mk ( Tptr ( typ , Pk_pointer ) ) ) in
fun class_id ->
let ident = Ident . create_normal class_id 0 in
AccessPath . of_id ident typ'
let make_global path mangled = { root = Global mangled ; path }
(* globals and class objects must be identical across threads *)
equal t1 t2
let make_parameter path index = { root = Parameter index ; path }
let make_class path typename = { root = Class typename ; path }
let is_class_object = function Class _ -> true | _ -> false
(* * convert an expression to a canonical form for a lock identifier *)
let rec make formal_map ( hilexp : HilExp . t ) =
match hilexp with
| AccessExpression access_exp -> (
@ -86,69 +73,58 @@ let rec make formal_map (hilexp : HilExp.t) =
(* ignore logical variables *)
None
| Var . ProgramVar pvar when Pvar . is_global pvar ->
Some ( make_global path ( Pvar . get_name pvar ) )
Some ( Global { path } )
| Var . ProgramVar _ ->
FormalMap . get_formal_index ( fst path ) formal_map
(* ignores non-formals *)
| > Option . map ~ f : ( make_parameter path ) )
| > Option . map ~ f : ( fun index -> Parameter { index ; path } ) )
| Constant ( Cclass class_id ) ->
(* this is a synchronized/lock ( CLASSNAME.class ) construct *)
let path = path_of_java_class class_id in
(* this is a synchronized ( CLASSNAME.class ) or class object construct *)
let typename = Ident . name_to_string class_id | > Typ . Name . Java . from_string in
Some ( make_class path typename )
Some ( Class { typename } )
| Cast ( _ , hilexp ) | Exception hilexp | UnaryOperator ( _ , hilexp , _ ) ->
make formal_map hilexp
| BinaryOperator _ | Closure _ | Constant _ | Sizeof _ ->
None
let make_java_synchronized formals procname =
match procname with
| Procname . Java java_pname when Procname . Java . is_static java_pname ->
(* this is crafted so as to match synchronized ( CLASSNAME.class ) constructs *)
let typename = Procname . Java . get_class_type_name java_pname in
let path = Typ . Name . name typename | > Ident . string_to_name | > path_of_java_class in
Some ( make_class path typename )
| Procname . Java _ ->
FormalMap . get_formal_base 0 formals | > Option . map ~ f : ( fun base -> make_parameter ( base , [] ) 0 )
| _ ->
L . die InternalError " Non-Java methods cannot be synchronized.@ \n "
let pp fmt { root ; path } =
let pp fmt t =
let pp_path fmt ( ( var , typ ) , accesses ) =
F . fprintf fmt " (%a:%a) " Var . pp var ( Typ . pp_full Pp . text ) typ ;
if not ( List . is_empty accesses ) then F . fprintf fmt " .%a " AccessPath . pp_access_list accesses
in
match root with
| Global mangled ->
F . fprintf fmt " G<%a>{%a} " Mangled . pp mangled pp_path path
| Class typename ->
F . fprintf fmt " C<%a>{%a} " Typ . Name . pp typename pp_path path
| Parameter idx ->
F . fprintf fmt " P<%i>{%a} " idx pp_path path
let root_class { path = ( _ , { Typ . desc } ) , _ } =
match desc with Typ . Tstruct name | Typ . Tptr ( { desc = Tstruct name } , _ ) -> Some name | _ -> None
match t with
| Global { path } ->
F . fprintf fmt " G{%a} " pp_path path
| Class { typename } ->
F . fprintf fmt " C{%s} " ( Typ . Name . name typename )
| Parameter { index ; path } ->
F . fprintf fmt " P<%i>{%a} " index pp_path path
let root_class = function
| Class { typename } ->
Some typename
| Global { path = ( _ , { desc } ) , _ } | Parameter { path = ( _ , { desc } ) , _ } -> (
match desc with
| Tstruct typename | Tptr ( { desc = Tstruct typename } , _ ) ->
Some typename
| _ ->
None )
let describe fmt lock =
let describe_lock fmt lock = ( MF . wrap_monospaced AccessPath . pp ) fmt lock . path in
let describe_typename = MF . wrap_monospaced Typ . Name . pp in
let describe_owner fmt lock =
root_class lock | > Option . iter ~ f : ( F . fprintf fmt " in %a " describe_typename )
let describe fmt t =
let describe_root fmt t =
root_class t | > Option . iter ~ f : ( F . fprintf fmt " in %a " ( MF . wrap_monospaced Typ . Name . pp ) )
in
F . fprintf fmt " %a%a " describe_lock lock describe_owner lock
let describe_class_object fmt typename = F . fprintf fmt " %s.class " ( Typ . Name . name typename ) in
match t with
| Class { typename } ->
MF . wrap_monospaced describe_class_object fmt typename
| Global { path } | Parameter { path } ->
F . fprintf fmt " %a%a " ( MF . wrap_monospaced AccessPath . pp ) path describe_root t
let compare_wrt_reporting { path = ( _ , typ1 ) , _ } { path = ( _ , typ2 ) , _ } =
(* use string comparison on types as a stable order to decide whether to report a deadlock *)
String . compare ( Typ . to_string typ1 ) ( Typ . to_string typ2 )
(* * A substitution from formal position indices to actuals. Since we only care about locks, use
[ None ] to denote an argument that cannot be resolved to a lock object . * )
type subst = t option Array . t
let pp_subst fmt subst =
@ -169,15 +145,26 @@ let make_subst formal_map actuals =
subst
let apply_subst ( subst : subst ) lock =
match lock. roo t with
let apply_subst ( subst : subst ) t =
match t with
| Global _ | Class _ ->
Some lock
| Parameter index -> (
Some t
| Parameter { index ; path = _ , [] } -> (
try
(* Special case for when the parameter is used without additional accesses, eg [x] as opposed to [x.f[].g]. *)
subst . ( index )
with Invalid_argument _ -> None )
| Parameter { index ; path } -> (
try
(* Here we know that there are additional accesses on the parameter *)
match subst . ( index ) with
| None ->
None
| Some actual ->
Some { actual with path = AccessPath . append actual . path ( snd lock . path ) }
| Some ( Class _ as t' ) as c ->
L . internal_error " Cannot dereference class object %a in path %a@. " pp t' pp t ;
c
| Some ( Parameter param ) ->
Some ( Parameter { param with path = AccessPath . append param . path ( snd path ) } )
| Some ( Global global ) ->
Some ( Global { path = AccessPath . append global . path ( snd path ) } )
with Invalid_argument _ -> None )