Skip to content
Snippets Groups Projects
Commit 348db98d authored by Andre Maroneze's avatar Andre Maroneze
Browse files

Merge branch 'feature/ivette/function-pointer-goto-def' into 'master'

[Ivette] ASTview: adds a menu entry "go to definition" on function pointers.

Closes #1246

See merge request frama-c/frama-c!4149
parents d3ff57a6 01101d12
No related branches found
No related tags found
No related merge requests found
......@@ -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>
......
......@@ -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,
......
......@@ -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. */
......
......@@ -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 =
......
......@@ -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
......
......@@ -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"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment