diff --git a/ivette/src/frama-c/kernel/ASTview.tsx b/ivette/src/frama-c/kernel/ASTview.tsx index 19b1d4628518368367c9439abb0888a1edaf1bc9..e5f61224461ff97dda393a73bfdf3c5b98aff9b9 100644 --- a/ivette/src/frama-c/kernel/ASTview.tsx +++ b/ivette/src/frama-c/kernel/ASTview.tsx @@ -533,17 +533,21 @@ async function studia(props: StudiaProps): Promise<StudiaInfos> { // This field contains all the current function's callers, as inferred by Eva. const Callers = Editor.createField<Eva.CallSite[]>([]); +// This field contains the function pointed to by the current hovered marker, +// as inferred by Eva. +const Callees = Editor.createField<Fct[]>([]); + // This field contains information on markers. type GetMarkerData = (key: Ast.marker) => Ast.markerAttributesData | undefined; const GetMarkerData = Editor.createField<GetMarkerData>(() => undefined); const ContextMenuHandler = createContextMenuHandler(); function createContextMenuHandler(): Editor.Extension { - const data = { tree: Tree, callers: Callers }; + const data = { tree: Tree, callers: Callers, callees: Callees }; const deps = { ...data, update: UpdateSelection, getData: GetMarkerData }; return Editor.createEventHandler(deps, { contextmenu: (inputs, view, event) => { - const { tree, callers, update, getData } = inputs; + const { tree, callers, callees, update, getData } = inputs; const coords = { x: event.clientX, y: event.clientY }; const position = view.posAtCoords(coords); if (!position) return; const node = coveringNode(tree, position); @@ -572,6 +576,13 @@ function createContextMenuHandler(): Editor.Extension { const label = `Go to definition of ${attrs.name}`; items.push({ label, onClick }); } + else if (attrs?.isFunctionPointer) { + Lodash.forEach(callees, (fct) => { + const onClick = (): void => update({ location: { fct } }); + const label = `Go to definition of ${fct} (indirect)`; + items.push({ label, onClick }); + }); + } const enabled = attrs?.isLval; const onClick = (kind: access): void => { if (attrs && node.marker) @@ -675,6 +686,11 @@ function useFctCallers(fct: Fct): Eva.CallSite[] { return States.useRequest(Eva.getCallers, fct) ?? []; } +// Server request handler returning the given function's callers. +function useCallees(marker: Marker): Fct[] { + return States.useRequest(Eva.getCallees, marker) ?? []; +} + // Server request handler returning the tainted lvalues. function useFctTaints(fct: Fct): Eva.LvalueTaints[] { return States.useRequest(Eva.taintedLvalues, fct, { onError: [] }) ?? []; @@ -751,6 +767,10 @@ export default function ASTview(): JSX.Element { const taints = useFctTaints(fct); React.useEffect(() => TaintedLvalues.set(view, taints), [view, taints]); + // Retrieving data on currently hovered marker. + const callees = useCallees(hovered); + React.useEffect(() => Callees.set(view, callees), [view, callees]); + return ( <> <TitleBar> diff --git a/ivette/src/frama-c/kernel/api/ast/index.ts b/ivette/src/frama-c/kernel/api/ast/index.ts index e0b4ccbdead11fe6eacec4f4a7197d1c71bf1e5b..a02d403f09a8950ca65dd1ef150851b3eb60356a 100644 --- a/ivette/src/frama-c/kernel/api/ast/index.ts +++ b/ivette/src/frama-c/kernel/api/ast/index.ts @@ -147,6 +147,8 @@ export interface markerAttributesData { isLval: boolean; /** Whether it is a function symbol */ isFunction: boolean; + /** Whether it is a function pointer */ + isFunctionPointer: boolean; /** Whether it is a function declaration */ isFunDecl: boolean; /** Function scope of the marker, if applicable */ @@ -165,6 +167,7 @@ export const jMarkerAttributesData: Json.Decoder<markerAttributesData> = descr: Json.jString, isLval: Json.jBoolean, isFunction: Json.jBoolean, + isFunctionPointer: Json.jBoolean, isFunDecl: Json.jBoolean, scope: Json.jOption(Json.jString), sloc: Json.jOption(jSource), @@ -175,7 +178,8 @@ export const byMarkerAttributesData: Compare.Order<markerAttributesData> = Compare.byFields <{ marker: marker, labelKind: string, titleKind: string, name: string, descr: string, isLval: boolean, isFunction: boolean, - isFunDecl: boolean, scope?: string, sloc?: source }>({ + isFunctionPointer: boolean, isFunDecl: boolean, scope?: string, + sloc?: source }>({ marker: byMarker, labelKind: Compare.alpha, titleKind: Compare.alpha, @@ -183,6 +187,7 @@ export const byMarkerAttributesData: Compare.Order<markerAttributesData> = descr: Compare.string, isLval: Compare.boolean, isFunction: Compare.boolean, + isFunctionPointer: Compare.boolean, isFunDecl: Compare.boolean, scope: Compare.defined(Compare.string), sloc: Compare.defined(bySource), @@ -240,8 +245,8 @@ export const markerAttributes: State.Array<marker,markerAttributesData> = marker /** Default value for `markerAttributesData` */ export const markerAttributesDataDefault: markerAttributesData = { marker: markerDefault, labelKind: '', titleKind: '', name: '', descr: '', - isLval: false, isFunction: false, isFunDecl: false, scope: undefined, - sloc: undefined }; + isLval: false, isFunction: false, isFunctionPointer: false, + isFunDecl: false, scope: undefined, sloc: undefined }; const getMainFunction_internal: Server.GetRequest<null,fct | undefined> = { kind: Server.RqKind.GET, diff --git a/ivette/src/frama-c/plugins/eva/api/general/index.ts b/ivette/src/frama-c/plugins/eva/api/general/index.ts index b5914a0f5c92d93d34d73efa8ad3201517c21811..18e0bc29ab498a0ad6855da61bf86fff71305bc2 100644 --- a/ivette/src/frama-c/plugins/eva/api/general/index.ts +++ b/ivette/src/frama-c/plugins/eva/api/general/index.ts @@ -135,6 +135,16 @@ const getCallers_internal: Server.GetRequest<fct,CallSite[]> = { /** Get the list of call site of a function */ export const getCallers: Server.GetRequest<fct,CallSite[]>= getCallers_internal; +const getCallees_internal: Server.GetRequest<marker,fct[]> = { + kind: Server.RqKind.GET, + name: 'plugins.eva.general.getCallees', + input: jMarker, + output: Json.jArray(jFct), + signals: [], +}; +/** Return the functions pointed to by a function pointer */ +export const getCallees: Server.GetRequest<marker,fct[]>= getCallees_internal; + /** Data for array rows [`functions`](#functions) */ export interface functionsData { /** Entry identifier. */ diff --git a/src/plugins/eva/api/general_requests.ml b/src/plugins/eva/api/general_requests.ml index 469c5cdbe61413fca9fe24cb5a40da8986f73826..cd68e1dc38623cdf42d756b002feeab6d2a1f8ba 100644 --- a/src/plugins/eva/api/general_requests.ml +++ b/src/plugins/eva/api/general_requests.ml @@ -86,6 +86,27 @@ let () = Request.register ~package ~input:(module Kernel_ast.Function) ~output:(module Data.Jlist (CallSite)) callers +let eval_callee stmt lval = + let expr = Eva_utils.lval_to_exp lval in + Results.(before stmt |> eval_callee expr |> default []) + +let callees = function + | Printer_tag.PLval (_kf, Kstmt stmt, (Mem _, NoOffset as lval)) + when Cil.(isFunctionType (typeOfLval lval)) -> + eval_callee stmt lval + | Printer_tag.PLval (_kf, Kstmt stmt, lval) + when Cil.(isFunPtrType (Cil.typeOfLval lval)) -> + eval_callee stmt (Mem (Eva_utils.lval_to_exp lval), NoOffset) + | _ -> [] + +let () = Request.register ~package + ~kind:`GET ~name:"getCallees" + ~descr:(Markdown.plain + "Return the functions pointed to by a function pointer") + ~input:(module Kernel_ast.Marker) + ~output:(module Data.Jlist (Kernel_ast.Function)) + callees + (* ----- Functions ---------------------------------------------------------- *) module Functions = diff --git a/src/plugins/eva/api/values_request.ml b/src/plugins/eva/api/values_request.ml index e6a33b7f6ad16d25b99b4fee80ba2de6447193c8..1e109a39629a2e95115db3b403be1112542972ba 100644 --- a/src/plugins/eva/api/values_request.ml +++ b/src/plugins/eva/api/values_request.ml @@ -115,7 +115,8 @@ let probe_property = function | _ -> raise Not_found let probe_marker = function - | Printer_tag.PLval (_, _, (Var vi, NoOffset)) + | Printer_tag.PLval (_, _, lval) + when Cil.(isFunctionType (typeOfLval lval)) -> raise Not_found | PVDecl (_, _, vi) when Cil.isFunctionType vi.vtype -> raise Not_found | PLval (_, _, l) -> Plval l | PExp (_, _, e) -> Pexpr e diff --git a/src/plugins/server/kernel_ast.ml b/src/plugins/server/kernel_ast.ml index 02fc76f1bc0aba538c3b951c7d44b4ac7fa4de13..f8c00bd913afce64be30134efa71558302ec8837 100644 --- a/src/plugins/server/kernel_ast.ml +++ b/src/plugins/server/kernel_ast.ml @@ -320,6 +320,13 @@ struct | Some vi -> Globals.Functions.mem vi | None -> false + let is_function_pointer = function + | PLval (_, _, (Mem _, NoOffset as lval)) + when Cil.(isFunctionType (typeOfLval lval)) -> true + | PLval (_, _, lval) + when Cil.(isFunPtrType (Cil.typeOfLval lval)) -> true + | _ -> false + let is_fundecl = function | PVDecl(Some _,Kglobal,vi) -> vi.vglob && Globals.Functions.mem vi | _ -> false @@ -377,6 +384,14 @@ struct ~get:(fun (tag, _) -> is_function tag) model + let () = + States.column + ~name:"isFunctionPointer" + ~descr:(Md.plain "Whether it is a function pointer") + ~data:(module Jbool) + ~get:(fun (tag, _) -> is_function_pointer tag) + model + let () = States.column ~name:"isFunDecl"