|
|
|
(*
|
|
|
|
* Copyright (c) 2013 - present Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*)
|
|
|
|
|
|
|
|
open! Utils
|
|
|
|
|
|
|
|
module F = Format
|
|
|
|
module L = Logging
|
|
|
|
|
|
|
|
(** Annotations. *)
|
|
|
|
|
|
|
|
(** Method signature with annotations. *)
|
|
|
|
type annotated_signature =
|
|
|
|
{ ret : Sil.item_annotation * Sil.typ; (** Annotated return type. *)
|
|
|
|
params: (Mangled.t * Sil.item_annotation * Sil.typ) list } (** Annotated parameters. *)
|
|
|
|
|
|
|
|
let param_equal (s1, ia1, t1) (s2, ia2, t2) =
|
|
|
|
Mangled.equal s1 s2 &&
|
|
|
|
Sil.item_annotation_compare ia1 ia2 = 0 &&
|
|
|
|
Sil.typ_equal t1 t2
|
|
|
|
|
|
|
|
let equal as1 as2 =
|
|
|
|
let ia1, t1 = as1.ret
|
|
|
|
and ia2, t2 = as2.ret in
|
|
|
|
Sil.item_annotation_compare ia1 ia2 = 0 &&
|
|
|
|
Sil.typ_equal t1 t2 &&
|
|
|
|
IList.for_all2 param_equal as1.params as2.params
|
|
|
|
|
|
|
|
let visibleForTesting = "com.google.common.annotations.VisibleForTesting"
|
|
|
|
let suppressLint = "android.annotation.SuppressLint"
|
|
|
|
|
|
|
|
|
|
|
|
let get_field_type_and_annotation fn = function
|
|
|
|
| Sil.Tptr (Sil.Tstruct struct_typ, _)
|
|
|
|
| Sil.Tstruct struct_typ ->
|
|
|
|
(try
|
|
|
|
let (_, t, a) = IList.find (fun (f, _, _) ->
|
|
|
|
Sil.fld_equal f fn)
|
|
|
|
(struct_typ.Sil.instance_fields @ struct_typ.Sil.static_fields) in
|
|
|
|
Some (t, a)
|
|
|
|
with Not_found -> None)
|
|
|
|
| _ -> None
|
|
|
|
|
|
|
|
(** Return the annotations on the declaring class of [pname]. Only works for Java *)
|
|
|
|
let get_declaring_class_annotations pname tenv =
|
|
|
|
match Tenv.proc_extract_declaring_class_typ tenv pname with
|
|
|
|
| Some { Sil.struct_annotations } -> Some struct_annotations
|
|
|
|
| None -> None
|
|
|
|
|
|
|
|
let ia_iter f =
|
|
|
|
let ann_iter (a, _) = f a in
|
|
|
|
IList.iter ann_iter
|
|
|
|
|
|
|
|
let ma_iter f ((ia, ial) : Sil.method_annotation) =
|
|
|
|
IList.iter (ia_iter f) (ia:: ial)
|
|
|
|
|
|
|
|
let ma_has_annotation_with
|
|
|
|
(ma: Sil.method_annotation)
|
|
|
|
(predicate: Sil.annotation -> bool): bool =
|
|
|
|
let found = ref false in
|
|
|
|
ma_iter
|
|
|
|
(fun a -> if predicate a then found := true)
|
|
|
|
ma;
|
|
|
|
!found
|
|
|
|
|
|
|
|
let ia_has_annotation_with
|
|
|
|
(ia: Sil.item_annotation)
|
|
|
|
(predicate: Sil.annotation -> bool): bool =
|
|
|
|
let found = ref false in
|
|
|
|
ia_iter
|
|
|
|
(fun a -> if predicate a then found := true)
|
|
|
|
ia;
|
|
|
|
!found
|
|
|
|
|
|
|
|
(** Check if there is an annotation which ends with the given name *)
|
|
|
|
let ia_ends_with ia ann_name =
|
|
|
|
let found = ref false in
|
|
|
|
let filter s =
|
|
|
|
let sl = String.length s in
|
|
|
|
let al = String.length ann_name in
|
|
|
|
sl >= al && String.sub s (sl - al) al = ann_name in
|
|
|
|
ia_iter (fun a -> if filter a.Sil.class_name then found := true) ia;
|
|
|
|
!found
|
|
|
|
|
|
|
|
let ia_contains ia ann_name =
|
|
|
|
let found = ref false in
|
|
|
|
ia_iter (fun a -> if ann_name = a.Sil.class_name then found := true) ia;
|
|
|
|
!found
|
|
|
|
|
|
|
|
let ia_get ia ann_name =
|
|
|
|
let found = ref None in
|
|
|
|
ia_iter (fun a -> if ann_name = a.Sil.class_name then found := Some a) ia;
|
|
|
|
!found
|
|
|
|
|
|
|
|
let ma_contains ma ann_names =
|
|
|
|
let found = ref false in
|
|
|
|
ma_iter (fun a -> if IList.exists (string_equal a.Sil.class_name) ann_names then found := true) ma;
|
|
|
|
!found
|
|
|
|
|
|
|
|
let initializer_ = "Initializer"
|
|
|
|
let inject = "Inject"
|
|
|
|
let inject_view = "InjectView"
|
|
|
|
let bind = "Bind"
|
|
|
|
let bind_array = "BindArray"
|
|
|
|
let bind_bitmap = "BindBitmap"
|
|
|
|
let bind_drawable = "BindDrawable"
|
|
|
|
let bind_string = "BindString"
|
|
|
|
let suppress_view_nullability = "SuppressViewNullability"
|
|
|
|
let false_on_null = "FalseOnNull"
|
|
|
|
let mutable_ = "Mutable"
|
|
|
|
let nullable = "Nullable"
|
|
|
|
let nonnull = "Nonnull"
|
|
|
|
let camel_nonnull = "NonNull"
|
|
|
|
let notnull = "NotNull"
|
|
|
|
let present = "Present"
|
|
|
|
let strict = "com.facebook.infer.annotation.Strict"
|
|
|
|
let true_on_null = "TrueOnNull"
|
|
|
|
let verify_annotation = "com.facebook.infer.annotation.Verify"
|
|
|
|
let expensive = "Expensive"
|
|
|
|
let performance_critical = "PerformanceCritical"
|
|
|
|
let no_allocation = "NoAllocation"
|
|
|
|
let ignore_allocations = "IgnoreAllocations"
|
|
|
|
let suppress_warnings = "SuppressWarnings"
|
|
|
|
let privacy_source = "PrivacySource"
|
|
|
|
let privacy_sink = "PrivacySink"
|
|
|
|
let integrity_source = "IntegritySource"
|
|
|
|
let integrity_sink = "IntegritySink"
|
|
|
|
|
|
|
|
let ia_is_nullable ia =
|
|
|
|
ia_ends_with ia nullable
|
|
|
|
|
|
|
|
let ia_is_present ia =
|
|
|
|
ia_ends_with ia present
|
|
|
|
|
|
|
|
let ia_is_nonnull ia =
|
|
|
|
IList.exists
|
|
|
|
(ia_ends_with ia)
|
|
|
|
[nonnull; notnull; camel_nonnull]
|
|
|
|
|
|
|
|
let ia_is_false_on_null ia =
|
|
|
|
ia_ends_with ia false_on_null
|
|
|
|
|
|
|
|
let ia_is_true_on_null ia =
|
|
|
|
ia_ends_with ia true_on_null
|
|
|
|
|
|
|
|
let ia_is_initializer ia =
|
|
|
|
ia_ends_with ia initializer_
|
|
|
|
|
|
|
|
let field_injector_readwrite_list =
|
|
|
|
[
|
|
|
|
inject_view;
|
|
|
|
bind;
|
|
|
|
bind_array;
|
|
|
|
bind_bitmap;
|
|
|
|
bind_drawable;
|
|
|
|
bind_string;
|
|
|
|
suppress_view_nullability;
|
|
|
|
]
|
|
|
|
|
|
|
|
let field_injector_readonly_list =
|
|
|
|
inject
|
|
|
|
::
|
|
|
|
field_injector_readwrite_list
|
|
|
|
|
|
|
|
(** Annotations for readonly injectors.
|
|
|
|
The injector framework initializes the field but does not write null into it. *)
|
|
|
|
let ia_is_field_injector_readonly ia =
|
|
|
|
IList.exists
|
|
|
|
(ia_ends_with ia)
|
|
|
|
field_injector_readonly_list
|
|
|
|
|
|
|
|
(** Annotations for read-write injectors.
|
|
|
|
The injector framework initializes the field and can write null into it. *)
|
|
|
|
let ia_is_field_injector_readwrite ia =
|
|
|
|
IList.exists
|
|
|
|
(ia_ends_with ia)
|
|
|
|
field_injector_readwrite_list
|
|
|
|
|
|
|
|
let ia_is_mutable ia =
|
|
|
|
ia_ends_with ia mutable_
|
|
|
|
|
|
|
|
let ia_get_strict ia =
|
|
|
|
ia_get ia strict
|
|
|
|
|
|
|
|
let ia_is_verify ia =
|
|
|
|
ia_contains ia verify_annotation
|
|
|
|
|
|
|
|
let ia_is_expensive ia =
|
|
|
|
ia_ends_with ia expensive
|
|
|
|
|
|
|
|
let ia_is_performance_critical ia =
|
|
|
|
ia_ends_with ia performance_critical
|
|
|
|
|
|
|
|
let ia_is_no_allocation ia =
|
|
|
|
ia_ends_with ia no_allocation
|
|
|
|
|
|
|
|
let ia_is_ignore_allocations ia =
|
|
|
|
ia_ends_with ia ignore_allocations
|
|
|
|
|
|
|
|
let ia_is_suppress_warnings ia =
|
|
|
|
ia_ends_with ia suppress_warnings
|
|
|
|
|
|
|
|
let ia_is_privacy_source ia =
|
|
|
|
ia_ends_with ia privacy_source
|
|
|
|
|
|
|
|
let ia_is_privacy_sink ia =
|
|
|
|
ia_ends_with ia privacy_sink
|
|
|
|
|
|
|
|
let ia_is_integrity_source ia =
|
|
|
|
ia_ends_with ia integrity_source
|
|
|
|
|
|
|
|
let ia_is_integrity_sink ia =
|
|
|
|
ia_ends_with ia integrity_sink
|
|
|
|
|
|
|
|
type annotation =
|
|
|
|
| Nullable
|
|
|
|
| Present
|
|
|
|
|
|
|
|
let ia_is ann ia = match ann with
|
|
|
|
| Nullable -> ia_is_nullable ia
|
|
|
|
| Present -> ia_is_present ia
|
|
|
|
|
|
|
|
(** Get a method signature with annotations from a proc_attributes. *)
|
|
|
|
let get_annotated_signature proc_attributes : annotated_signature =
|
|
|
|
let method_annotation = proc_attributes.ProcAttributes.method_annotation in
|
|
|
|
let formals = proc_attributes.ProcAttributes.formals in
|
|
|
|
let ret_type = proc_attributes.ProcAttributes.ret_type in
|
|
|
|
let (ia, ial0) = method_annotation in
|
|
|
|
let natl =
|
|
|
|
let rec extract ial parl = match ial, parl with
|
|
|
|
| ia :: ial', (name, typ) :: parl' ->
|
|
|
|
(name, ia, typ) :: extract ial' parl'
|
|
|
|
| [], (name, typ) :: parl' ->
|
|
|
|
(name, Sil.item_annotation_empty, typ) :: extract [] parl'
|
|
|
|
| [], [] ->
|
|
|
|
[]
|
|
|
|
| _ :: _, [] ->
|
|
|
|
assert false in
|
|
|
|
IList.rev (extract (IList.rev ial0) (IList.rev formals)) in
|
|
|
|
let annotated_signature = { ret = (ia, ret_type); params = natl } in
|
|
|
|
annotated_signature
|
|
|
|
|
|
|
|
|
|
|
|
(** Check if the annotated signature is for a wrapper of an anonymous inner class method.
|
|
|
|
These wrappers have the same name as the original method, every type is Object, and the parameters
|
|
|
|
are called x0, x1, x2. *)
|
|
|
|
let annotated_signature_is_anonymous_inner_class_wrapper ann_sig proc_name =
|
|
|
|
let check_ret (ia, t) =
|
|
|
|
Sil.item_annotation_is_empty ia && PatternMatch.type_is_object t in
|
|
|
|
let x_param_found = ref false in
|
|
|
|
let name_is_x_number name =
|
|
|
|
let name_str = Mangled.to_string name in
|
|
|
|
let len = String.length name_str in
|
|
|
|
len >= 2 &&
|
|
|
|
String.sub name_str 0 1 = "x" &&
|
|
|
|
let s = String.sub name_str 1 (len - 1) in
|
|
|
|
let is_int =
|
|
|
|
try
|
|
|
|
ignore (int_of_string s);
|
|
|
|
x_param_found := true;
|
|
|
|
true
|
|
|
|
with Failure _ -> false in
|
|
|
|
is_int in
|
|
|
|
let check_param (name, ia, t) =
|
|
|
|
if Mangled.to_string name = "this" then true
|
|
|
|
else
|
|
|
|
name_is_x_number name &&
|
|
|
|
Sil.item_annotation_is_empty ia &&
|
|
|
|
PatternMatch.type_is_object t in
|
|
|
|
Procname.java_is_anonymous_inner_class proc_name
|
|
|
|
&& check_ret ann_sig.ret
|
|
|
|
&& IList.for_all check_param ann_sig.params
|
|
|
|
&& !x_param_found
|
|
|
|
|
|
|
|
(** Check if the given parameter has a Nullable annotation in the given signature *)
|
|
|
|
let param_is_nullable pvar ann_sig =
|
|
|
|
IList.exists
|
|
|
|
(fun (param, annot, _) ->
|
|
|
|
Mangled.equal param (Pvar.get_name pvar) && ia_is_nullable annot)
|
|
|
|
ann_sig.params
|
|
|
|
|
|
|
|
(** Pretty print a method signature with annotations. *)
|
|
|
|
let pp_annotated_signature proc_name fmt annotated_signature =
|
|
|
|
let pp_ia fmt ia = if ia <> [] then F.fprintf fmt "%a " Sil.pp_item_annotation ia in
|
|
|
|
let pp_annotated_param fmt (p, ia, t) =
|
|
|
|
F.fprintf fmt " %a%a %a" pp_ia ia (Sil.pp_typ_full pe_text) t Mangled.pp p in
|
|
|
|
let ia, ret_type = annotated_signature.ret in
|
|
|
|
F.fprintf fmt "%a%a %s (%a )"
|
|
|
|
pp_ia ia
|
|
|
|
(Sil.pp_typ_full pe_text) ret_type
|
|
|
|
(Procname.to_simplified_string proc_name)
|
|
|
|
(pp_comma_seq pp_annotated_param) annotated_signature.params
|
|
|
|
|
|
|
|
let mk_ann_str s = { Sil.class_name = s; Sil.parameters = [] }
|
|
|
|
let mk_ann = function
|
|
|
|
| Nullable -> mk_ann_str nullable
|
|
|
|
| Present -> mk_ann_str present
|
|
|
|
let mk_ia ann ia =
|
|
|
|
if ia_is ann ia then ia
|
|
|
|
else (mk_ann ann, true) :: ia
|
|
|
|
let mark_ia ann ia x =
|
|
|
|
if x then mk_ia ann ia else ia
|
|
|
|
|
|
|
|
let mk_ia_strict ia =
|
|
|
|
if ia_get_strict ia <> None then ia
|
|
|
|
else (mk_ann_str strict, true) :: ia
|
|
|
|
let mark_ia_strict ia x =
|
|
|
|
if x then mk_ia_strict ia else ia
|
|
|
|
|
|
|
|
(** Mark the annotated signature with the given annotation map. *)
|
|
|
|
let annotated_signature_mark proc_name ann asig (b, bs) =
|
|
|
|
let ia, t = asig.ret in
|
|
|
|
let ret' = mark_ia ann ia b, t in
|
|
|
|
let mark_param (s, ia, t) x =
|
|
|
|
let ia' = if x then mk_ia ann ia else ia in
|
|
|
|
(s, ia', t) in
|
|
|
|
let params' =
|
|
|
|
let fail () =
|
|
|
|
L.stdout
|
|
|
|
"INTERNAL ERROR: annotation for procedure %s has wrong number of arguments@."
|
|
|
|
(Procname.to_unique_id proc_name);
|
|
|
|
L.stdout " ANNOTATED SIGNATURE: %a@." (pp_annotated_signature proc_name) asig;
|
|
|
|
assert false in
|
|
|
|
let rec combine l1 l2 = match l1, l2 with
|
|
|
|
| (p, ia, t):: l1', l2' when Mangled.to_string p = "this" ->
|
|
|
|
(p, ia, t) :: combine l1' l2'
|
|
|
|
| (s, ia, t):: l1', x:: l2' ->
|
|
|
|
mark_param (s, ia, t) x :: combine l1' l2'
|
|
|
|
| [], _:: _ -> fail ()
|
|
|
|
| _:: _, [] -> fail ()
|
|
|
|
| [], [] -> [] in
|
|
|
|
combine asig.params bs in
|
|
|
|
{ ret = ret'; params = params'}
|
|
|
|
|
|
|
|
(** Mark the return of the annotated signature with the given annotation. *)
|
|
|
|
let annotated_signature_mark_return ann asig =
|
|
|
|
let ia, t = asig.ret in
|
|
|
|
let ret' = mark_ia ann ia true, t in
|
|
|
|
{ asig with ret = ret'}
|
|
|
|
|
|
|
|
(** Mark the return of the annotated signature @Strict. *)
|
|
|
|
let annotated_signature_mark_return_strict asig =
|
|
|
|
let ia, t = asig.ret in
|
|
|
|
let ret' = mark_ia_strict ia true, t in
|
|
|
|
{ asig with ret = ret'}
|
|
|
|
|
|
|
|
(** Mark the return of the method_annotation with the given annotation. *)
|
|
|
|
let method_annotation_mark_return ann method_annotation =
|
|
|
|
let ia_ret, params = method_annotation in
|
|
|
|
let ia_ret' = mark_ia ann ia_ret true in
|
|
|
|
ia_ret', params
|