(* * Copyright (c) 2017-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. *) open! IStd (** To be used in 'list_constraint *) type accept_more and end_of_list (** To be used in 'emptyness *) type empty and non_empty (* Type shorthands *) type typ = Typ.t type c = Typ.Procname.C.t type objc_cpp = Typ.Procname.ObjC_Cpp.t type java = Typ.Procname.Java.t type qual_name = QualifiedCppName.t type templated_name = qual_name * Typ.template_arg list type 'marker mtyp = typ type 'captured_types capt = unit -> 'captured_types (* Typ helpers *) let template_args_of_template_spec_info = function | Typ.NoTemplate -> [] | Typ.Template {args} -> args let templated_name_of_class_name class_name = let open Typ in match class_name with | CStruct qual_name | CUnion qual_name | ObjcClass qual_name | ObjcProtocol qual_name -> (qual_name, []) | CppClass (qual_name, template_spec_info) -> (qual_name, template_args_of_template_spec_info template_spec_info) | JavaClass mangled_name -> (QualifiedCppName.of_list [Mangled.to_string mangled_name], []) let templated_name_of_java java = let qual_name = QualifiedCppName.of_list [Typ.Procname.Java.get_class_name java; Typ.Procname.Java.get_method java] in (qual_name, []) (* Intermediate matcher types *) type ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out) name_matcher = { on_objc_cpp: 'context -> 'f_in -> objc_cpp -> ('f_out * 'captured_types capt) option ; on_qual_name: 'context -> 'f_in -> qual_name -> ('f_out * 'captured_types capt) option ; get_markers: 'markers_in -> 'markers_out } type ( 'f_in , 'f_out , 'captured_types_in , 'captured_types_out , 'markers_in , 'markers_out , 'list_constraint ) template_arg = { eat_template_arg: 'f_in * 'captured_types_in capt * Typ.template_arg list -> ('f_out * 'captured_types_out capt * Typ.template_arg list) option ; add_marker: 'markers_in -> 'markers_out } type ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out, 'list_constraint) templ_matcher = { on_objc_cpp: 'context -> 'f_in -> objc_cpp -> ('f_out * 'captured_types capt * Typ.template_arg list) option ; on_templated_name: 'context -> 'f_in -> templated_name -> ('f_out * 'captured_types capt * Typ.template_arg list) option ; get_markers: 'markers_in -> 'markers_out } type ('context, 'f_in, 'f_out, 'captured_types, 'emptyness) path_extra = | PathEmpty : ('context, 'f, 'f, unit, empty) path_extra | PathNonEmpty : { on_objc_cpp: 'context -> 'f_in -> objc_cpp -> ('f_out * 'captured_types capt) option } -> ('context, 'f_in, 'f_out, 'captured_types, non_empty) path_extra type ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out, 'emptyness) path_matcher = { on_templated_name: 'context -> 'f_in -> templated_name -> ('f_out * 'captured_types capt) option ; path_extra: ('context, 'f_in, 'f_out, 'captured_types, 'emptyness) path_extra ; get_markers: 'markers_in -> 'markers_out } (* Combinators *) let empty : ('context, 'f, 'f, unit, 'markers, 'markers, empty) path_matcher = let get_markers m = m in let get_capture () = () in let on_templated_name _context f (qual_name, template_args) = match (QualifiedCppName.extract_last qual_name, template_args) with | None, [] -> Some (f, get_capture) | None, _ -> assert false | Some _, _ -> None in {on_templated_name; path_extra= PathEmpty; get_markers} let name_cons : ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out, _) path_matcher -> string -> ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out) name_matcher = fun m name -> let {on_templated_name; get_markers} = m in let fuzzy_name_regexp = name |> Str.quote |> Printf.sprintf "^%s\\(<[a-z0-9]+>\\)?$" |> Str.regexp in let on_qual_name context f qual_name = match QualifiedCppName.extract_last qual_name with | Some (last, rest) when Str.string_match fuzzy_name_regexp last 0 -> on_templated_name context f (rest, []) | _ -> None in let on_objc_cpp context f (objc_cpp : Typ.Procname.ObjC_Cpp.t) = if String.equal name objc_cpp.method_name then on_templated_name context f (templated_name_of_class_name objc_cpp.class_name) else None in {on_objc_cpp; on_qual_name; get_markers} let name_cons_f : ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out, _) path_matcher -> ('context -> string -> bool) -> ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out) name_matcher = fun m f_name -> let {on_templated_name; get_markers} = m in let on_qual_name context f qual_name = match QualifiedCppName.extract_last qual_name with | Some (last, rest) when f_name context last -> on_templated_name context f (rest, []) | _ -> None in let on_objc_cpp context f (objc_cpp : Typ.Procname.ObjC_Cpp.t) = if f_name context objc_cpp.method_name then on_templated_name context f (templated_name_of_class_name objc_cpp.class_name) else None in {on_objc_cpp; on_qual_name; get_markers} let all_names_cons : ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out, non_empty) path_matcher -> ('context, 'f_in, 'f_out, 'captured_tpes, 'markers_in, 'markers_out, non_empty) path_matcher = fun m -> let {on_templated_name; get_markers; path_extra= PathNonEmpty {on_objc_cpp}} = m in let rec on_templated_name_rec context f templated_name = match on_templated_name context f templated_name with | Some _ as some -> some | None -> ( let qual_name, _template_args = templated_name in match QualifiedCppName.extract_last qual_name with | None -> None | Some (_last, rest) -> on_templated_name_rec context f (rest, []) ) in let on_templated_name = on_templated_name_rec in let on_objc_cpp context f (objc_cpp : Typ.Procname.ObjC_Cpp.t) = match on_objc_cpp context f objc_cpp with | Some _ as some -> some | None -> on_templated_name context f (templated_name_of_class_name objc_cpp.class_name) in {on_templated_name; get_markers; path_extra= PathNonEmpty {on_objc_cpp}} let templ_begin : ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out) name_matcher -> ( 'context , 'f_in , 'f_out , 'captured_types , 'markers_in , 'markers_out , accept_more ) templ_matcher = fun m -> let {on_objc_cpp; on_qual_name; get_markers} = m in let on_templated_name context f (qual_name, template_args) = match on_qual_name context f qual_name with | None -> None | Some (f, captured_types) -> Some (f, captured_types, template_args) in let on_objc_cpp context f (objc_cpp : Typ.Procname.ObjC_Cpp.t) = match on_objc_cpp context f objc_cpp with | None -> None | Some (f, captured_types) -> let template_args = template_args_of_template_spec_info objc_cpp.template_args in Some (f, captured_types, template_args) in {on_objc_cpp; on_templated_name; get_markers} let templ_cons : ( 'context , 'f_in , 'f_interm , 'captured_types_in , 'markers_interm , 'markers_out , accept_more ) templ_matcher -> ( 'f_interm , 'f_out , 'captured_types_in , 'captured_types_out , 'markers_in , 'markers_interm , 'lc ) template_arg -> ('context, 'f_in, 'f_out, 'captured_types_out, 'markers_in, 'markers_out, 'lc) templ_matcher = fun m template_arg -> let {on_objc_cpp; on_templated_name; get_markers} = m in let {eat_template_arg; add_marker} = template_arg in let get_markers m = get_markers (add_marker m) in let on_templated_name context f templated_name = on_templated_name context f templated_name |> Option.bind ~f:eat_template_arg in let on_objc_cpp context f objc_cpp = on_objc_cpp context f objc_cpp |> Option.bind ~f:eat_template_arg in {on_objc_cpp; on_templated_name; get_markers} let templ_end : ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out, _) templ_matcher -> ( 'context , 'f_in , 'f_out , 'captured_types , 'markers_in , 'markers_out , non_empty ) path_matcher = let match_empty_templ_args (f, captured_types, template_args) = match template_args with [] -> Some (f, captured_types) | _ -> None in fun m -> let {on_objc_cpp; on_templated_name; get_markers} = m in let on_templated_name context f templated_name = on_templated_name context f templated_name |> Option.bind ~f:match_empty_templ_args in let on_objc_cpp context f objc_cpp = on_objc_cpp context f objc_cpp |> Option.bind ~f:match_empty_templ_args in {on_templated_name; path_extra= PathNonEmpty {on_objc_cpp}; get_markers} module type Common = sig type ('context, 'f) matcher type ('context, 'f) dispatcher val make_dispatcher : ('context, 'f) matcher list -> ('context, 'f) dispatcher (* Template arguments *) val any_typ : ('f, 'f, 'captured_types, 'captured_types, 'markers, 'markers, accept_more) template_arg (** Eats a type *) val capt_typ : 'marker -> ( 'marker mtyp -> 'f , 'f , 'captured_types , 'marker mtyp * 'captured_types , 'markers , 'marker * 'markers , accept_more ) template_arg (** Captures a type than can be back-referenced *) val capt_int : ( Int64.t -> 'f , 'f , 'captured_types , 'captured_types , 'markers , 'markers , accept_more ) template_arg (** Captures an int *) val capt_all : ( Typ.template_arg list -> 'f , 'f , 'captured_types , 'captured_types , 'markers , 'markers , end_of_list ) template_arg (** Captures all template args *) val ( ~- ) : string -> ('context, 'f, 'f, unit, 'markers, 'markers) name_matcher (** Starts a path with a name *) val ( ~+ ) : ('context -> string -> bool) -> ('context, 'f, 'f, unit, 'markers, 'markers) name_matcher (** Starts a path with a matching name that satisfies the given function *) val ( &+ ) : ( 'context , 'f_in , 'f_interm , 'captured_types_in , 'markers_interm , 'markers_out , accept_more ) templ_matcher -> ( 'f_interm , 'f_out , 'captured_types_in , 'captured_types_out , 'markers_in , 'markers_interm , 'lc ) template_arg -> ('context, 'f_in, 'f_out, 'captured_types_out, 'markers_in, 'markers_out, 'lc) templ_matcher (** Separate template arguments *) val ( < ) : ('context, 'f_in, 'f_interm, 'captured_types_in, 'markers_interm, 'markers_out) name_matcher -> ( 'f_interm , 'f_out , 'captured_types_in , 'captured_types_out , 'markers_in , 'markers_interm , 'lc ) template_arg -> ('context, 'f_in, 'f_out, 'captured_types_out, 'markers_in, 'markers_out, 'lc) templ_matcher (** Starts template arguments after a name *) val ( >:: ) : ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out, _) templ_matcher -> string -> ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out) name_matcher (** Ends template arguments and starts a name *) val ( &+...>:: ) : ( 'context , 'f_in , 'f_out , 'captured_types , 'markers_in , 'markers_out , accept_more ) templ_matcher -> string -> ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out) name_matcher (** Ends template arguments with eats-ALL and starts a name *) val ( &:: ) : ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out) name_matcher -> string -> ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out) name_matcher (** Separates names (accepts ALL template arguments on the left one) *) val ( &::+ ) : ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out) name_matcher -> ('context -> string -> bool) -> ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out) name_matcher val ( <>:: ) : ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out) name_matcher -> string -> ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out) name_matcher (** Separates names (accepts NO template arguments on the left one) *) end module Common = struct (* Template arguments *) let add_no_marker capture_markers = capture_markers (** Eats all template args *) let any_template_args : ('f, 'f, 'captured_types, 'captured_types, 'markers, 'markers, end_of_list) template_arg = let eat_template_arg (f, captured_types, _) = Some (f, captured_types, []) in {eat_template_arg; add_marker= add_no_marker} (** Eats a type *) let any_typ : ('f, 'f, 'captured_types, 'captured_types, 'markers, 'markers, accept_more) template_arg = let eat_template_arg (f, captured_types, template_args) = match template_args with Typ.TType _ :: rest -> Some (f, captured_types, rest) | _ -> None in {eat_template_arg; add_marker= add_no_marker} (** Captures a type than can be back-referenced *) let capt_typ : 'marker -> ( 'marker mtyp -> 'f , 'f , 'captured_types , 'marker mtyp * 'captured_types , 'markers , 'marker * 'markers , accept_more ) template_arg = fun marker -> let eat_template_arg (f, captured_types, template_args) = match template_args with | Typ.TType ty :: rest -> let captured_types () = (ty, captured_types ()) in Some (f ty, captured_types, rest) | _ -> None in let add_marker capture_markers = (marker, capture_markers) in {eat_template_arg; add_marker} (** Captures an int *) let capt_int : ( Int64.t -> 'f , 'f , 'captured_types , 'captured_types , 'markers , 'markers , accept_more ) template_arg = let eat_template_arg (f, captured_types, template_args) = match template_args with Typ.TInt i :: rest -> Some (f i, captured_types, rest) | _ -> None in {eat_template_arg; add_marker= add_no_marker} (** Captures all template args *) let capt_all : ( Typ.template_arg list -> 'f , 'f , 'captured_types , 'captured_types , 'markers , 'markers , end_of_list ) template_arg = let eat_template_arg (f, captured_types, template_args) = Some (f template_args, captured_types, []) in {eat_template_arg; add_marker= add_no_marker} let ( ! ) templ_matcher () = templ_end templ_matcher let ( &::! ) path_matcher name = name_cons path_matcher name let ( &::+! ) path_matcher f_name = name_cons_f path_matcher f_name let ( &::.*! ) path_matcher () = all_names_cons path_matcher let ( ~- ) name = empty &::! name let ( ~+ ) f_name = name_cons_f empty f_name let ( &+ ) templ_matcher template_arg = templ_cons templ_matcher template_arg let ( &+...>! ) templ_matcher () = templ_matcher &+ any_template_args >! () let ( <...>! ) name_matcher () = name_matcher ! () let ( < ) name_matcher template_arg = name_matcher :: ) templ_matcher name = templ_matcher >! () &::! name let ( &+...>:: ) templ_matcher name = templ_matcher &+...>! () &::! name let ( &:: ) name_matcher name = name_matcher <...>! () &::! name let ( &::+ ) name_matcher f_name = name_matcher <...>! () &::+! f_name let ( <>:: ) name_matcher name = name_matcher :: name end module Call = struct include Common (** Little abstraction over arguments: currently actual args, we'll want formal args later *) module FuncArg = struct type t = Exp.t * Typ.t let typ (_, ty) = ty let exp (e, _) = e let get_var_exn arg = match exp arg with | Exp.Var v -> v | e -> Logging.(die InternalError) "Expected Lvar, got %a:%a" Exp.pp e (Typ.pp Pp.text) (typ arg) end type ('context, 'f_in, 'f_out, 'captured_types) proc_matcher = { on_objc_cpp: 'context -> 'f_in -> objc_cpp -> ('f_out * 'captured_types) option ; on_c: 'context -> 'f_in -> c -> ('f_out * 'captured_types) option ; on_java: 'context -> 'f_in -> java -> ('f_out * 'captured_types) option } type ('context, 'f_in, 'f_out, 'captured_types) on_args = 'context -> 'captured_types -> 'f_in * FuncArg.t list -> ('f_out * FuncArg.t list) option type ('context, 'f_in, 'f_proc_out, 'f_out, 'captured_types, 'markers) args_matcher = { on_proc: ('context, 'f_in, 'f_proc_out, 'captured_types) proc_matcher ; on_args: ('context, 'f_proc_out, 'f_out, 'captured_types) on_args ; markers: 'markers } type ('context, 'captured_types, 'markers) one_arg_matcher = { match_arg: 'context -> 'captured_types -> FuncArg.t -> bool ; marker_static_checker: 'markers -> bool } type ('arg_in, 'arg_out, 'f_in, 'f_out) arg_capture = {get_captured_value: FuncArg.t -> 'arg_in; do_capture: 'f_in -> 'arg_out -> 'f_out} type ('context, 'arg_in, 'arg_out, 'f_in, 'f_out, 'captured_types, 'markers) one_arg = { one_arg_matcher: ('context, 'captured_types, 'markers) one_arg_matcher ; capture: ('arg_in, 'arg_out, 'f_in, 'f_out) arg_capture } type ('arg_in, 'arg_out, 'f_in, 'f_out) arg_preparer = { on_empty: ('f_in -> 'arg_out -> 'f_out) -> 'f_in -> ('f_out * FuncArg.t list) option ; wrapper: 'arg_in -> 'arg_out } type ('context, 'f_in, 'f_out, 'captured_types, 'markers) func_arg = { eat_func_arg: ('context, 'f_in, 'f_out, 'captured_types) on_args ; marker_static_checker: 'markers -> bool } type ('context, 'f) matcher = { on_objc_cpp: 'context -> objc_cpp -> FuncArg.t list -> 'f option ; on_c: 'context -> c -> FuncArg.t list -> 'f option ; on_java: 'context -> java -> FuncArg.t list -> 'f option } type ('context, 'f) pre_result = | DoesNotMatch | Matches of 'f | RetryWith of ('context, 'f) matcher let pre_bind_opt opt ~f = match opt with None -> DoesNotMatch | Some x -> f x let pre_map_opt opt ~f = match opt with None -> DoesNotMatch | Some x -> Matches (f x) type ('context, 'f_in, 'f_out, 'captured_types) func_args_end = on_args:('context, 'f_in, 'f_out, 'captured_types) on_args -> 'context -> FuncArg.t list -> 'f_in * 'captured_types -> ('context, 'f_out) pre_result type ('context, 'f_in, 'f_out) all_args_matcher = { on_objc_cpp: 'context -> 'f_in -> objc_cpp -> FuncArg.t list -> ('context, 'f_out) pre_result ; on_c: 'context -> 'f_in -> c -> FuncArg.t list -> ('context, 'f_out) pre_result ; on_java: 'context -> 'f_in -> java -> FuncArg.t list -> ('context, 'f_out) pre_result } type ('context, 'f) dispatcher = 'context -> Typ.Procname.t -> FuncArg.t list -> 'f option let args_begin : ('context, 'f_in, 'f_out, 'captured_types, unit, 'markers, non_empty) path_matcher -> ('context, 'f_in, 'f_out, 'f_out, 'captured_types, 'markers) args_matcher = let on_args _context _capt f_args = Some f_args in fun m -> let {on_templated_name; path_extra= PathNonEmpty {on_objc_cpp}; get_markers} = m in let markers = get_markers () in let get_captures (f, captured_types) = (f, captured_types ()) in let on_c context f (c : c) = let template_args = template_args_of_template_spec_info c.template_args in on_templated_name context f (c.name, template_args) |> Option.map ~f:get_captures in let on_java context f (java : java) = on_templated_name context f (templated_name_of_java java) |> Option.map ~f:get_captures in let on_objc_cpp context f objc_cpp = on_objc_cpp context f objc_cpp |> Option.map ~f:get_captures in let on_proc : (_, _, _, _) proc_matcher = {on_objc_cpp; on_c; on_java} in {on_proc; on_args; markers} let args_cons : ('context, 'f_in, 'f_proc_out, 'f_interm, 'captured_types, 'markers) args_matcher -> ('context, 'f_interm, 'f_out, 'captured_types, 'markers) func_arg -> ('context, 'f_in, 'f_proc_out, 'f_out, 'captured_types, 'markers) args_matcher = fun m func_arg -> let {on_proc; on_args; markers} = m in let {marker_static_checker; eat_func_arg} = func_arg in assert (marker_static_checker markers) ; let on_args context capt f_args = on_args context capt f_args |> Option.bind ~f:(eat_func_arg context capt) in {on_proc; on_args; markers} let args_end : ('context, 'f_in, 'f_proc_out, 'f_out, 'captured_types, 'markers) args_matcher -> ('context, 'f_proc_out, 'f_out, 'captured_types) func_args_end -> ('context, 'f_in, 'f_out) all_args_matcher = fun m func_args_end -> let {on_proc= {on_c; on_java; on_objc_cpp}; on_args} = m in let on_c context f c args = on_c context f c |> pre_bind_opt ~f:(func_args_end ~on_args context args) in let on_java context f java args = on_java context f java |> pre_bind_opt ~f:(func_args_end ~on_args context args) in let on_objc_cpp context f objc_cpp args = on_objc_cpp context f objc_cpp |> pre_bind_opt ~f:(func_args_end ~on_args context args) in {on_c; on_java; on_objc_cpp} let make_matcher : ('context, 'f_in, 'f_out) all_args_matcher -> 'f_in -> ('context, 'f_out) matcher = fun m f -> let ({on_c; on_java; on_objc_cpp} : (_, _, _) all_args_matcher) = m in let on_objc_cpp context objc_cpp args = match on_objc_cpp context f objc_cpp args with | DoesNotMatch -> None | Matches res -> Some res | RetryWith {on_objc_cpp} -> on_objc_cpp context objc_cpp args in let on_c context c args = match on_c context f c args with | DoesNotMatch -> None | Matches res -> Some res | RetryWith {on_c} -> on_c context c args in let on_java context java args = match on_java context f java args with | DoesNotMatch -> None | Matches res -> Some res | RetryWith {on_java} -> on_java context java args in {on_objc_cpp; on_c; on_java} (** Simple implementation of a dispatcher, could be optimized later *) let make_dispatcher : ('context, 'f) matcher list -> ('context, 'f) dispatcher = fun matchers -> let on_objc_cpp context objc_cpp args = List.find_map matchers ~f:(fun (matcher : _ matcher) -> matcher.on_objc_cpp context objc_cpp args ) in let on_c context c args = List.find_map matchers ~f:(fun (matcher : _ matcher) -> matcher.on_c context c args) in let on_java context java args = List.find_map matchers ~f:(fun (matcher : _ matcher) -> matcher.on_java context java args) in fun context procname args -> match procname with | ObjC_Cpp objc_cpp -> on_objc_cpp context objc_cpp args | C c -> on_c context c args | Java java -> on_java context java args | _ -> None (* Function args *) let no_marker_checker _markers = true (** Matches any arg *) let match_any_arg : (_, _, _) one_arg_matcher = let match_arg _context _capt _arg = true in {match_arg; marker_static_checker= no_marker_checker} let mk_match_typ_nth : ('markers -> 'marker) -> ('captured_types -> 'marker mtyp) -> 'marker -> ('context, 'captured_types, 'markers) one_arg_matcher = fun get_m get_c marker -> let marker_static_checker markers = Polymorphic_compare.( = ) marker (get_m markers) in let match_arg _context capt arg = Typ.equal (FuncArg.typ arg) (get_c capt) in {match_arg; marker_static_checker} (** Matches first captured type *) let match_typ1 : 'marker -> ('context, 'marker mtyp * _, 'marker * _) one_arg_matcher = let pos1 (x, _) = x in fun marker -> mk_match_typ_nth pos1 pos1 marker (** Matches second captured type *) let match_typ2 : 'marker -> ('context, _ * ('marker mtyp * _), _ * ('marker * _)) one_arg_matcher = let pos2 (_, (x, _)) = x in fun marker -> mk_match_typ_nth pos2 pos2 marker (** Matches third captured type *) let match_typ3 : 'marker -> ('context, _ * (_ * ('marker mtyp * _)), _ * (_ * ('marker * _))) one_arg_matcher = let pos3 (_, (_, (x, _))) = x in fun marker -> mk_match_typ_nth pos3 pos3 marker (** Matches the type matched by the given path_matcher *) let match_typ : ('context, _, _, unit, unit, unit, non_empty) path_matcher -> ('context, _, _) one_arg_matcher = fun m -> let ({on_templated_name} : (_, _, _, unit, unit, unit, non_empty) path_matcher) = m in let rec match_typ context typ = match typ with | {Typ.desc= Tstruct name} -> name |> templated_name_of_class_name |> on_templated_name context () |> Option.is_some | {Typ.desc= Tptr (typ, _ptr_kind)} -> match_typ context typ | _ -> false in let match_arg context _capt arg = match_typ context (FuncArg.typ arg) in {match_arg; marker_static_checker= no_marker_checker} (* Function argument capture *) (** Do not capture this argument *) let no_capture : (_, _, 'f, 'f) arg_capture = let get_captured_value _arg = () in let do_capture f _v = f in {get_captured_value; do_capture} (** Capture the argument *) let capture_arg : (FuncArg.t, 'wrapped_arg, 'wrapped_arg -> 'f, 'f) arg_capture = let get_captured_value arg = arg in let do_capture f v = f v in {get_captured_value; do_capture} (** Capture the argument expression *) let capture_arg_exp : (Exp.t, 'wrapped_arg, 'wrapped_arg -> 'f, 'f) arg_capture = let get_captured_value arg = FuncArg.exp arg in let do_capture f v = f v in {get_captured_value; do_capture} (** Capture the argument local var or fail *) let capture_arg_var_exn : (Ident.t, 'wrapped_arg, 'wrapped_arg -> 'f, 'f) arg_capture = let get_captured_value arg = FuncArg.get_var_exn arg in let do_capture f v = f v in {get_captured_value; do_capture} let mandatory_arg = let on_empty _do_capture _f = None in let wrapper = Fn.id in {on_empty; wrapper} let optional_arg = let on_empty do_capture f = Some (do_capture f None, []) in let wrapper = Option.some in {on_empty; wrapper} let make_arg : ('arg_in, 'arg_out, 'f_in, 'f_out) arg_preparer -> ('context, 'arg_in, 'arg_out, 'f_in, 'f_out, _, _) one_arg -> ('context, 'f_in, 'f_out, _, _) func_arg = fun arg_preparer one_arg -> let {on_empty; wrapper} = arg_preparer in let {one_arg_matcher; capture} = one_arg in let {match_arg; marker_static_checker} = one_arg_matcher in let {get_captured_value; do_capture} = capture in let eat_func_arg context capt (f, args) = match args with | [] -> on_empty do_capture f | arg :: rest when match_arg context capt arg -> Some (arg |> get_captured_value |> wrapper |> do_capture f, rest) | _ -> None in {eat_func_arg; marker_static_checker} let any_arg : ('context, unit, _, 'f, 'f, _, _) one_arg = {one_arg_matcher= match_any_arg; capture= no_capture} let capt_arg : ('context, FuncArg.t, 'wrapped_arg, 'wrapped_arg -> 'f, 'f, _, _) one_arg = {one_arg_matcher= match_any_arg; capture= capture_arg} let capt_exp : ('context, Exp.t, 'wrapped_arg, 'wrapped_arg -> 'f, 'f, _, _) one_arg = {one_arg_matcher= match_any_arg; capture= capture_arg_exp} let capt_var_exn : ('context, Ident.t, 'wrapped_arg, 'wrapped_arg -> 'f, 'f, _, _) one_arg = {one_arg_matcher= match_any_arg; capture= capture_arg_var_exn} let capt_arg_of_typ m = {one_arg_matcher= match_typ (m <...>! ()); capture= capture_arg} let capt_exp_of_typ m = {one_arg_matcher= match_typ (m <...>! ()); capture= capture_arg_exp} let typ1 : 'marker -> ('context, unit, _, 'f, 'f, _, _) one_arg = fun m -> {one_arg_matcher= match_typ1 m; capture= no_capture} let typ2 : 'marker -> ('context, unit, _, 'f, 'f, _, _) one_arg = fun m -> {one_arg_matcher= match_typ2 m; capture= no_capture} let typ3 : 'marker -> ('context, unit, _, 'f, 'f, _, _) one_arg = fun m -> {one_arg_matcher= match_typ3 m; capture= no_capture} (* Function args end *) (** Matches if there is no function arguments left *) let no_args_left : ('context, _, _, _) func_args_end = let match_empty_args = function Some (f, []) -> Matches f | _ -> DoesNotMatch in fun ~on_args context args (f, capt) -> on_args context capt (f, args) |> match_empty_args (** Matches any function arguments *) let any_func_args : ('context, _, _, _) func_args_end = fun ~on_args context args (f, capt) -> on_args context capt (f, args) |> pre_map_opt ~f:fst (** If [func_args_end1] does not match, use [func_args_end2] *) let alternative_args_end : ('context, 'f_in, 'f_out, 'captured_types) func_args_end -> ('context, 'f_in, 'f_out, 'captured_types) func_args_end -> ('context, 'f_in, 'f_out, 'captured_types) func_args_end = fun func_args_end1 func_args_end2 ~on_args context args f_capt -> match func_args_end1 ~on_args context args f_capt with | DoesNotMatch -> func_args_end2 ~on_args context args f_capt | otherwise -> otherwise (** Retries matching with another matcher *) let args_end_retry : _ matcher -> ('context, _, _, _) func_args_end = fun m ~on_args:_ _context _args _f_capt -> RetryWith m (** Retries matching with another matcher if the function does not have the exact number/types of args *) let exact_args_or_retry : ('context, 'f) matcher -> ('context, _, _, _) func_args_end = fun m -> alternative_args_end no_args_left (args_end_retry m) let wrong_args_internal_error : _ matcher = let on_procname procname = Logging.(die InternalError) "Unexpected number/types of arguments for %a" Typ.Procname.pp procname in let on_c _context c _args = on_procname (C c) in let on_java _context java _args = on_procname (Java java) in let on_objc_cpp _context objc_cpp _args = on_procname (ObjC_Cpp objc_cpp) in {on_c; on_java; on_objc_cpp} let ( $! ) path_matcher () = args_begin path_matcher let ( >$! ) templ_matcher () = templ_matcher >! () $! () let ( $*--> ) all_args_matcher f = make_matcher all_args_matcher f let ( $+! ) args_matcher func_arg = args_cons args_matcher func_arg let ( $!! ) one_arg () = make_arg mandatory_arg one_arg let ( $?! ) one_arg () = make_arg optional_arg one_arg let ( $+ ) args_matcher one_arg = args_matcher $+! (one_arg $!! ()) let ( $+? ) args_matcher one_arg = args_matcher $+! (one_arg $?! ()) let ( >$ ) templ_matcher one_arg = templ_matcher >$! () $+ one_arg let ( $* ) args_matcher func_args_end = args_end args_matcher func_args_end let ( $--> ) args_matcher f = args_matcher $* no_args_left $*--> f let ( $ ) name_matcher func_arg = name_matcher <...>! () $! () $+ func_arg let ( <>$ ) name_matcher one_arg = name_matcher $ one_arg let ( $+...$--> ) args_matcher f = args_matcher $* any_func_args $*--> f let ( >--> ) templ_matcher f = templ_matcher >$! () $+...$--> f let ( >$$--> ) templ_matcher f = templ_matcher >$! () $--> f let ( $$--> ) name_matcher f = name_matcher <...>! () $! () $--> f let ( <>$$--> ) name_matcher f = name_matcher $$--> f let ( &--> ) name_matcher f = name_matcher <...>! () $! () $+...$--> f let ( <>--> ) name_matcher f = name_matcher --> f let ( &::.*--> ) name_matcher f = name_matcher <...>! () &::.*! () $! () $+...$--> f let ( $!--> ) args_matcher f = args_matcher $* exact_args_or_retry wrong_args_internal_error $*--> f end module type NameCommon = sig include Common val ( >--> ) : ('context, 'f_in, 'f_out, 'captured_types, unit, 'markers, _) templ_matcher -> 'f_in -> ('context, 'f_out) matcher val ( <>--> ) : ('context, 'f_in, 'f_out, 'captured_types, unit, 'markers) name_matcher -> 'f_in -> ('context, 'f_out) matcher val ( &--> ) : ('context, 'f_in, 'f_out, 'captured_types, unit, 'markers) name_matcher -> 'f_in -> ('context, 'f_out) matcher val ( &::.*--> ) : ('context, 'f_in, 'f_out, 'captured_types, unit, 'markers) name_matcher -> 'f_in -> ('context, 'f_out) matcher (** After a name, accepts ALL template arguments, accepts ALL path tails (names, templates), accepts ALL function arguments, binds the function *) end module NameCommon = struct include Common type ('context, 'f) matcher = { on_templated_name: 'context -> templated_name -> 'f option ; on_objc_cpp: 'context -> objc_cpp -> 'f option } let make_matcher : ('context, 'f_in, 'f_out, _, _, _, non_empty) path_matcher -> 'f_in -> ('context, 'f_out) matcher = fun m f -> let ({on_templated_name; path_extra= PathNonEmpty {on_objc_cpp}} : ('context, 'f_in, 'f_out, _, _, _, non_empty) path_matcher) = m in let on_templated_name context templated_name = templated_name |> on_templated_name context f |> Option.map ~f:fst in let on_objc_cpp context objc_cpp = objc_cpp |> on_objc_cpp context f |> Option.map ~f:fst in {on_templated_name; on_objc_cpp} let ( &-->! ) path_matcher f = make_matcher path_matcher f let ( >--> ) templ_matcher f = templ_matcher >! () &-->! f let ( <>--> ) name_matcher f = name_matcher --> f let ( &--> ) name_matcher f = name_matcher <...>! () &-->! f let ( &::.*--> ) name_matcher f = name_matcher <...>! () &::.*! () &-->! f end module ProcName = struct include NameCommon type ('context, 'f) dispatcher = 'context -> Typ.Procname.t -> 'f option let make_dispatcher : ('context, 'f) matcher list -> ('context, 'f) dispatcher = fun matchers -> let on_objc_cpp context objc_cpp = List.find_map matchers ~f:(fun (matcher : _ matcher) -> matcher.on_objc_cpp context objc_cpp) in let on_templated_name context templated_name = List.find_map matchers ~f:(fun (matcher : _ matcher) -> matcher.on_templated_name context templated_name ) in let on_java context (java : Typ.Procname.Java.t) = let templated_name = templated_name_of_java java in on_templated_name context templated_name in let on_c context (c : c) = let template_args = template_args_of_template_spec_info c.template_args in let templated_name = (c.name, template_args) in on_templated_name context templated_name in fun context procname -> match procname with | ObjC_Cpp objc_cpp -> on_objc_cpp context objc_cpp | C c -> on_c context c | Java java -> on_java context java | _ -> None end module TypName = struct include NameCommon type ('context, 'f) dispatcher = 'context -> Typ.name -> 'f option let make_dispatcher : ('context, 'f) matcher list -> ('context, 'f) dispatcher = fun matchers context typname -> let templated_name = templated_name_of_class_name typname in List.find_map matchers ~f:(fun (matcher : _ matcher) -> matcher.on_templated_name context templated_name ) end