From d5baac56b86aba4de9f28d52c5f6a4e885332878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr> Date: Tue, 17 Jan 2023 19:47:49 +0100 Subject: [PATCH] [kernel] nenamed printer-tag api --- ivette/src/frama-c/kernel/ASTinfo.tsx | 54 +-- ivette/src/frama-c/kernel/ASTview.tsx | 73 +-- ivette/src/frama-c/kernel/Locations.tsx | 4 +- ivette/src/frama-c/kernel/Properties.tsx | 4 +- ivette/src/frama-c/kernel/SourceCode.tsx | 10 +- ivette/src/frama-c/kernel/api/ast/index.ts | 258 ++++------ .../frama-c/kernel/api/properties/index.ts | 36 +- .../frama-c/plugins/eva/api/general/index.ts | 44 +- .../frama-c/plugins/eva/api/values/index.ts | 26 +- ivette/src/frama-c/plugins/eva/valuetable.tsx | 91 ++-- .../plugins/studia/api/studia/index.ts | 12 +- ivette/src/frama-c/states.ts | 31 ++ src/kernel_services/ast_data/globals.ml | 5 + src/kernel_services/ast_data/globals.mli | 3 + .../ast_printing/printer_tag.ml | 59 +-- .../ast_printing/printer_tag.mli | 2 +- src/plugins/eva/api/general_requests.ml | 4 +- src/plugins/gui/pretty_source.ml | 6 +- src/plugins/server/kernel_ast.ml | 456 +++++++++--------- src/plugins/server/kernel_ast.mli | 31 +- src/plugins/server/kernel_properties.ml | 4 +- 21 files changed, 555 insertions(+), 658 deletions(-) diff --git a/ivette/src/frama-c/kernel/ASTinfo.tsx b/ivette/src/frama-c/kernel/ASTinfo.tsx index 332a656b245..72adb6eedde 100644 --- a/ivette/src/frama-c/kernel/ASTinfo.tsx +++ b/ivette/src/frama-c/kernel/ASTinfo.tsx @@ -57,38 +57,6 @@ function makeFilter(filter: string): string[] { return filter.split(':').filter((s) => s.length > 0).sort(); } -// -------------------------------------------------------------------------- -// --- Marker Kinds -// -------------------------------------------------------------------------- - -import Kind = AST.markerKind; -import Var = AST.markerVar - -function getMarkerKind(props: AST.markerInfoData): [string, string] { - switch (props.kind) { - case Kind.declaration: - switch (props.var) { - case Var.function: return ["Declaration", "Function declaration"]; - case Var.variable: return ["Declaration", "Variable declaration"]; - case Var.none: return ["Declaration", "Declaration"]; - } - break; - case Kind.global: return ["Global", "Global declaration or definition"]; - case Kind.lvalue: - switch (props.var) { - case Var.function: return ["Function", "Function"]; - case Var.variable: return ["Variable", "C variable"]; - case Var.none: return ["Lvalue", "C lvalue"]; - } - break; - case Kind.expression: return ["Expression", "C expression"]; - case Kind.statement: return ["Statement", "C statement"]; - case Kind.property: return ["Property", "ACSL property"]; - case Kind.term: return ["Term", "ACSL term"]; - case Kind.type: return ["Type", "C type"]; - } -} - // -------------------------------------------------------------------------- // --- Information Details // -------------------------------------------------------------------------- @@ -159,7 +127,7 @@ interface InfoSectionProps { fct: string | undefined; scroll: React.RefObject<HTMLDivElement> | undefined; marker: AST.marker; - markerInfos: AST.markerInfoData; + attrs: AST.markerAttributesData; scrolled: AST.marker | undefined; selected: AST.marker | undefined; hovered: AST.marker | undefined; @@ -170,9 +138,9 @@ interface InfoSectionProps { } function MarkInfos(props: InfoSectionProps): JSX.Element { - const { marker, markerInfos } = props; + const { marker, attrs } = props; const { fct, scrolled, selected, hovered, excluded } = props; - const scope = markerInfos.scope ?? fct; + const scope = attrs.scope ?? fct; const foreign = !!scope && fct !== scope; const [unfold, setUnfold] = React.useState(true); const [expand, setExpand] = React.useState(false); @@ -186,9 +154,7 @@ function MarkInfos(props: InfoSectionProps): JSX.Element { isSelected && 'selected', isHovered && 'hovered', ); - const [label, title] = getMarkerKind(markerInfos); - const name = markerInfos.name; - const descr = markerInfos.descr ?? `${label} ${name}`; + const { labelKind, titleKind, name, descr } = attrs; const filtered = markerFields.filter((fd) => !excluded.includes(fd.id)); const hasMore = filtered.length < markerFields.length; const displayed = expand ? markerFields : filtered; @@ -241,8 +207,8 @@ function MarkInfos(props: InfoSectionProps): JSX.Element { onClick={onFoldUnfold} /> <Code key="NAME" className="astinfo-markercode"> - <span className="astinfo-markerkind" title={title}> - {label} + <span className="astinfo-markerkind" title={titleKind}> + {labelKind} </span> {name} </Code> <Code key="SCOPE" className="" display={foreign}> @@ -317,7 +283,7 @@ export default function ASTinfo(): JSX.Element { const [setting, setSetting] = Dome.useStringSettings(filterSettings, ''); const [selection] = States.useSelection(); const [hovering] = States.useHovered(); - const allInfos = States.useSyncArray(AST.markerInfo); + const attributes = States.useSyncArray(AST.markerAttributes); const allFields = States.useRequest(AST.getInformation, null) ?? []; const excluded = React.useMemo(() => makeFilter(setting), [setting]); Dome.useEvent(States.MetaSelection, (loc: States.Location) => { @@ -345,15 +311,15 @@ export default function ASTinfo(): JSX.Element { setMarkers(toggleMarker(markers, marker)); // Mark Rendering const renderMark = (marker: AST.marker): JSX.Element | null => { - const markerInfos = allInfos.getData(marker); - if (!markerInfos) return null; + const attrs = attributes.getData(marker); + if (!attrs) return null; return ( <MarkInfos key={marker} fct={fct} scroll={scroll} marker={marker} - markerInfos={markerInfos} + attrs={attrs} scrolled={scrolled} hovered={hovered} selected={selected} diff --git a/ivette/src/frama-c/kernel/ASTview.tsx b/ivette/src/frama-c/kernel/ASTview.tsx index ed3deae15c9..acf1959aabd 100644 --- a/ivette/src/frama-c/kernel/ASTview.tsx +++ b/ivette/src/frama-c/kernel/ASTview.tsx @@ -78,8 +78,8 @@ interface Node extends Range { id: string, children: Tree[] } type Tree = Leaf | Node; // Utility functions on trees. -function isLeaf (t: Tree): t is Leaf { return 'text' in t; } -function isNode (t: Tree): t is Node { return 'id' in t && 'children' in t; } +function isLeaf(t: Tree): t is Leaf { return 'text' in t; } +function isNode(t: Tree): t is Node { return 'id' in t && 'children' in t; } const empty: Tree = { text: '', from: 0, to: 0 }; // Convert an Ivette text (i.e a function's code) into a Tree, adding range @@ -104,6 +104,9 @@ function textToTree(t: text): Tree | undefined { return res; } +// Convert an Ivette text to defined Tree. +function rootText(t: text): Tree { return textToTree(t) ?? empty; } + // Convert an Ivette text into a string to be displayed. function textToString(text: text): string { if (Array.isArray(text)) return text.slice(1).map(textToString).join(''); @@ -113,7 +116,7 @@ function textToString(text: text): string { // Computes, for each markers of a tree, its range. Returns the map containing // all those bindings. -function markersRanges(tree: Tree): Map<string, Range[]>{ +function markersRanges(tree: Tree): Map<string, Range[]> { const ranges: Map<string, Range[]> = new Map(); const toRanges = (tree: Tree): void => { if (!isNode(tree)) return; @@ -156,7 +159,7 @@ const Text = Editor.createTextField<text>(null, textToString); // This aspect computes the tree representing the currently displayed function's // code, represented by the <Text> field. -const Tree = Editor.createAspect({ t: Text }, ({t}) => textToTree(t) ?? empty); +const Tree = Editor.createAspect({ t: Text }, ({ t }) => rootText(t)); // This aspect computes the markers ranges of the currently displayed function's // tree, represented by the <Tree> aspect. @@ -229,7 +232,7 @@ const Hovered = Editor.createField<Marker>(undefined); // The Ivette hovered element must be updated by CodeMirror plugins. This // field add the callback in the CodeMirror internal state. type UpdateHovered = (h: States.Hovered) => void; -const UpdateHovered = Editor.createField<UpdateHovered>(() => { return ; }); +const UpdateHovered = Editor.createField<UpdateHovered>(() => { return; }); // The Hovered field is updated each time the mouse moves through the CodeMirror // document. The handlers updates the Ivette hovered information, which is then @@ -421,7 +424,7 @@ type access = 'Reads' | 'Writes'; interface StudiaProps { marker: string, - info: Ast.markerInfoData, + attrs: Ast.markerAttributesData, kind: access, } @@ -433,11 +436,11 @@ interface StudiaInfos { } async function studia(props: StudiaProps): Promise<StudiaInfos> { - const { marker, info, kind } = props; + const { marker, attrs, kind } = props; const request = kind === 'Reads' ? getReadsLval : getWritesLval; const data = await Server.send(request, marker); const locations = data.direct.map(([f, m]) => ({ fct: f, marker: m })); - const lval = info.name; + const lval = attrs.name; if (locations.length > 0) { const name = `${kind} of ${lval}`; const acc = (kind === 'Reads') ? 'accessing' : 'modifying'; @@ -461,7 +464,7 @@ async function studia(props: StudiaProps): Promise<StudiaInfos> { const Callers = Editor.createField<Eva.CallSite[]>([]); // This field contains information on markers. -type GetMarkerData = (key: string) => Ast.markerInfoData | undefined; +type GetMarkerData = (key: string) => Ast.markerAttributesData | undefined; const GetMarkerData = Editor.createField<GetMarkerData>(() => undefined); const ContextMenuHandler = createContextMenuHandler(); @@ -476,34 +479,32 @@ function createContextMenuHandler(): Editor.Extension { const node = coveringNode(tree, position); if (!node || !node.id) return; const items: Dome.PopupMenuItem[] = []; - const info = getData(node.id); - if (info?.var === 'function') { - if (info.kind === 'declaration') { - const groupedCallers = Lodash.groupBy(callers, e => e.kf); - const locations = callers.map((l) => ({ fct: l.kf, marker: l.stmt })); - Lodash.forEach(groupedCallers, (e) => { - const callerName = e[0].kf; - const callSites = e.length > 1 ? `(${e.length} call sites)` : ''; - items.push({ - label: `Go to caller ${callerName} ` + callSites, - onClick: () => update({ - name: `Call sites of function ${info.name}`, - locations: locations, - index: locations.findIndex(l => l.fct === callerName) - }) - }); + const attrs = getData(node.id); + if (attrs?.isFunDecl) { + const groupedCallers = Lodash.groupBy(callers, e => e.kf); + const locations = callers.map((l) => ({ fct: l.kf, marker: l.stmt })); + Lodash.forEach(groupedCallers, (e) => { + const callerName = e[0].kf; + const callSites = e.length > 1 ? `(${e.length} call sites)` : ''; + items.push({ + label: `Go to caller ${callerName} ` + callSites, + onClick: () => update({ + name: `Call sites of function ${attrs.name}`, + locations: locations, + index: locations.findIndex(l => l.fct === callerName) + }) }); - } else { - const location = { fct: info.name }; - const onClick = (): void => update({ location }); - const label = `Go to definition of ${info.name}`; - items.push({ label, onClick }); - } + }); + } else if (attrs?.isFun) { + const location = { fct: attrs.name }; + const onClick = (): void => update({ location }); + const label = `Go to definition of ${attrs.name}`; + items.push({ label, onClick }); } - const enabled = info?.kind === 'lvalue' || info?.var === 'variable'; + const enabled = attrs?.isLval; const onClick = (kind: access): void => { - if (info && node.id) - studia({ marker: node.id, info, kind }).then(update); + if (attrs && node.id) + studia({ marker: node.id, attrs, kind }).then(update); }; const reads = 'Studia: select reads'; const writes = 'Studia: select writes'; @@ -653,7 +654,7 @@ export default function ASTview(): JSX.Element { React.useEffect(() => Tags.set(view, tags), [view, tags]); // Updating CodeMirror when the <markersInfo> synchronized array is changed. - const info = States.useSyncArray(Ast.markerInfo); + const info = States.useSyncArray(Ast.markerAttributes); const getData = React.useCallback((key) => info.getData(key), [info]); React.useEffect(() => GetMarkerData.set(view, getData), [view, getData]); @@ -688,7 +689,7 @@ export default function ASTview(): JSX.Element { /> <Inset /> </TitleBar> - <Component style={{ fontSize: `${fontSize}px`}} /> + <Component style={{ fontSize: `${fontSize}px` }} /> </> ); } diff --git a/ivette/src/frama-c/kernel/Locations.tsx b/ivette/src/frama-c/kernel/Locations.tsx index ba048e57111..7f955a475a6 100644 --- a/ivette/src/frama-c/kernel/Locations.tsx +++ b/ivette/src/frama-c/kernel/Locations.tsx @@ -34,7 +34,7 @@ import { Label } from 'dome/controls/labels'; import { IconButton } from 'dome/controls/buttons'; import { Space } from 'dome/frame/toolbars'; import { TitleBar } from 'ivette'; -import { markerInfo } from 'frama-c/kernel/api/ast'; +import { markerAttributes } from 'frama-c/kernel/api/ast'; // -------------------------------------------------------------------------- // --- Locations Panel @@ -51,7 +51,7 @@ export default function LocationsTable(): JSX.Element { ), []); const multipleSelections = selection?.multiple; const numberOfSelections = multipleSelections?.allSelections?.length; - const markersInfo = States.useSyncArray(markerInfo); + const markersInfo = States.useSyncArray(markerAttributes); // Renderer for statement markers. const renderMarker: Renderer<string> = diff --git a/ivette/src/frama-c/kernel/Properties.tsx b/ivette/src/frama-c/kernel/Properties.tsx index 5034f8671cc..546732eb09f 100644 --- a/ivette/src/frama-c/kernel/Properties.tsx +++ b/ivette/src/frama-c/kernel/Properties.tsx @@ -206,7 +206,7 @@ function filterEva(p: Property): boolean { case 'not_tainted': case 'not_applicable': return !filter('eva.data_tainted_only') && - !filter('eva.ctrl_tainted_only'); + !filter('eva.ctrl_tainted_only'); case 'direct_taint': return !(filter('eva.ctrl_tainted_only')); case 'indirect_taint': @@ -333,7 +333,7 @@ const byColumn: Arrays.ByColumns<Property> = { }; class PropertyModel - extends Arrays.CompactModel<Json.key<'#property'>, Property> { + extends Arrays.CompactModel<Json.key<'#marker'>, Property> { private filterFun?: string; diff --git a/ivette/src/frama-c/kernel/SourceCode.tsx b/ivette/src/frama-c/kernel/SourceCode.tsx index cc059c919a4..cf2eed18e59 100644 --- a/ivette/src/frama-c/kernel/SourceCode.tsx +++ b/ivette/src/frama-c/kernel/SourceCode.tsx @@ -142,7 +142,7 @@ function createSyncOnUserSelection(): Editor.Extension { const [fct, marker] = await Server.send(Ast.getMarkerAt, cursor); if (fct || marker) { // The forced reload should NOT be necessary but... It is... - await Server.send(Ast.reloadMarkerInfo, null); + await Server.send(Ast.reloadMarkerAttributes, null); const location = { fct, marker }; update({ location }); Locations.set(view, { cursor: location, ivette: location }); @@ -188,7 +188,7 @@ function createContextMenu(): Editor.Extension { if (file === '') return; const label = 'Open file in an external editor'; const pos = getCursorPosition(view); - Dome.popupMenu([ { label, onClick: () => edit(file, pos, command) } ]); + Dome.popupMenu([{ label, onClick: () => edit(file, pos, command) }]); }, }); } @@ -210,15 +210,15 @@ function useFctSource(file: string): string { // Build a callback that retrieves a location's source information. function useSourceGetter(): GetSource { - const markersInfo = States.useSyncArray(Ast.markerInfo); + const attributes = States.useSyncArray(Ast.markerAttributes); const functionsData = States.useSyncArray(Ast.functions).getArray(); return React.useCallback(({ fct, marker }) => { const markerSloc = (marker !== undefined && marker !== '') ? - markersInfo.getData(marker)?.sloc : undefined; + attributes.getData(marker)?.sloc : undefined; const fctSloc = (fct !== undefined && fct !== '') ? functionsData.find((e) => e.name === fct)?.sloc : undefined; return markerSloc ?? fctSloc; - }, [markersInfo, functionsData]); + }, [attributes, functionsData]); } // ----------------------------------------------------------------------------- diff --git a/ivette/src/frama-c/kernel/api/ast/index.ts b/ivette/src/frama-c/kernel/api/ast/index.ts index 093da142747..7cbd4af0072 100644 --- a/ivette/src/frama-c/kernel/api/ast/index.ts +++ b/ivette/src/frama-c/kernel/api/ast/index.ts @@ -37,17 +37,11 @@ import * as Server from 'frama-c/server'; //@ts-ignore import * as State from 'frama-c/states'; -//@ts-ignore -import { byTag } from 'frama-c/kernel/api/data'; //@ts-ignore import { byText } from 'frama-c/kernel/api/data'; //@ts-ignore -import { jTag } from 'frama-c/kernel/api/data'; -//@ts-ignore import { jText } from 'frama-c/kernel/api/data'; //@ts-ignore -import { tag } from 'frama-c/kernel/api/data'; -//@ts-ignore import { text } from 'frama-c/kernel/api/data'; const compute_internal: Server.ExecRequest<null,null> = { @@ -88,206 +82,140 @@ export const bySource: Compare.Order<source> = line: Compare.number, }); -/** Marker kind */ -export enum markerKind { - /** Expression */ - expression = 'expression', - /** Lvalue */ - lvalue = 'lvalue', - /** Declaration */ - declaration = 'declaration', - /** Statement */ - statement = 'statement', - /** Global */ - global = 'global', - /** Term */ - term = 'term', - /** Property */ - property = 'property', - /** Type */ - type = 'type', -} +/** Localizable AST markers */ +export type marker = Json.key<'#marker'>; -/** Decoder for `markerKind` */ -export const jMarkerKind: Json.Decoder<markerKind> = Json.jEnum(markerKind); +/** Decoder for `marker` */ +export const jMarker: Json.Decoder<marker> = Json.jKey<'#marker'>('#marker'); -/** Natural order for `markerKind` */ -export const byMarkerKind: Compare.Order<markerKind> = - Compare.byEnum(markerKind); +/** Natural order for `marker` */ +export const byMarker: Compare.Order<marker> = Compare.string; -const markerKindTags_internal: Server.GetRequest<null,tag[]> = { - kind: Server.RqKind.GET, - name: 'kernel.ast.markerKindTags', - input: Json.jNull, - output: Json.jArray(jTag), - signals: [], -}; -/** Registered tags for the above type. */ -export const markerKindTags: Server.GetRequest<null,tag[]>= markerKindTags_internal; - -/** Marker variable */ -export enum markerVar { - /** None */ - none = 'none', - /** Variable */ - variable = 'variable', +/** Location: function and marker */ +export interface location { /** Function */ - function = 'function', + fct: Json.key<'#fct'>; + /** Marker */ + marker: marker; } -/** Decoder for `markerVar` */ -export const jMarkerVar: Json.Decoder<markerVar> = Json.jEnum(markerVar); - -/** Natural order for `markerVar` */ -export const byMarkerVar: Compare.Order<markerVar> = - Compare.byEnum(markerVar); +/** Decoder for `location` */ +export const jLocation: Json.Decoder<location> = + Json.jObject({ fct: Json.jKey<'#fct'>('#fct'), marker: jMarker,}); -const markerVarTags_internal: Server.GetRequest<null,tag[]> = { - kind: Server.RqKind.GET, - name: 'kernel.ast.markerVarTags', - input: Json.jNull, - output: Json.jArray(jTag), - signals: [], -}; -/** Registered tags for the above type. */ -export const markerVarTags: Server.GetRequest<null,tag[]>= markerVarTags_internal; +/** Natural order for `location` */ +export const byLocation: Compare.Order<location> = + Compare.byFields + <{ fct: Json.key<'#fct'>, marker: marker }>({ + fct: Compare.string, + marker: byMarker, + }); -/** Data for array rows [`markerInfo`](#markerinfo) */ -export interface markerInfoData { +/** Data for array rows [`markerAttributes`](#markerattributes) */ +export interface markerAttributesData { /** Entry identifier. */ key: string; - /** Marker kind */ - kind: markerKind; - /** Marker variable */ - var: markerVar; + /** Marker kind (short) */ + labelKind: string; + /** Marker kind (long) */ + titleKind: string; /** Marker short name */ name: string; /** Marker declaration or description */ descr: string; + /** Whether it is an l-value */ + isLval: boolean; + /** Whether it is a function declaration or definition */ + isFunDecl: boolean; + /** Whether it is a function symbol */ + isFun: boolean; /** Function scope of the marker, if applicable */ scope?: string; /** Source location */ sloc: source; } -/** Decoder for `markerInfoData` */ -export const jMarkerInfoData: Json.Decoder<markerInfoData> = +/** Decoder for `markerAttributesData` */ +export const jMarkerAttributesData: Json.Decoder<markerAttributesData> = Json.jObject({ key: Json.jString, - kind: jMarkerKind, - var: jMarkerVar, + labelKind: Json.jString, + titleKind: Json.jString, name: Json.jString, descr: Json.jString, + isLval: Json.jBoolean, + isFunDecl: Json.jBoolean, + isFun: Json.jBoolean, scope: Json.jOption(Json.jString), sloc: jSource, }); -/** Natural order for `markerInfoData` */ -export const byMarkerInfoData: Compare.Order<markerInfoData> = +/** Natural order for `markerAttributesData` */ +export const byMarkerAttributesData: Compare.Order<markerAttributesData> = Compare.byFields - <{ key: string, kind: markerKind, var: markerVar, name: string, - descr: string, scope?: string, sloc: source }>({ + <{ key: string, labelKind: string, titleKind: string, name: string, + descr: string, isLval: boolean, isFunDecl: boolean, isFun: boolean, + scope?: string, sloc: source }>({ key: Compare.string, - kind: byMarkerKind, - var: byMarkerVar, + labelKind: Compare.alpha, + titleKind: Compare.alpha, name: Compare.alpha, descr: Compare.string, + isLval: Compare.boolean, + isFunDecl: Compare.boolean, + isFun: Compare.boolean, scope: Compare.defined(Compare.string), sloc: bySource, }); -/** Signal for array [`markerInfo`](#markerinfo) */ -export const signalMarkerInfo: Server.Signal = { - name: 'kernel.ast.signalMarkerInfo', +/** Signal for array [`markerAttributes`](#markerattributes) */ +export const signalMarkerAttributes: Server.Signal = { + name: 'kernel.ast.signalMarkerAttributes', }; -const reloadMarkerInfo_internal: Server.GetRequest<null,null> = { +const reloadMarkerAttributes_internal: Server.GetRequest<null,null> = { kind: Server.RqKind.GET, - name: 'kernel.ast.reloadMarkerInfo', + name: 'kernel.ast.reloadMarkerAttributes', input: Json.jNull, output: Json.jNull, signals: [], }; -/** Force full reload for array [`markerInfo`](#markerinfo) */ -export const reloadMarkerInfo: Server.GetRequest<null,null>= reloadMarkerInfo_internal; +/** Force full reload for array [`markerAttributes`](#markerattributes) */ +export const reloadMarkerAttributes: Server.GetRequest<null,null>= reloadMarkerAttributes_internal; -const fetchMarkerInfo_internal: Server.GetRequest< +const fetchMarkerAttributes_internal: Server.GetRequest< number, - { pending: number, updated: markerInfoData[], removed: string[], + { pending: number, updated: markerAttributesData[], removed: string[], reload: boolean } > = { kind: Server.RqKind.GET, - name: 'kernel.ast.fetchMarkerInfo', + name: 'kernel.ast.fetchMarkerAttributes', input: Json.jNumber, output: Json.jObject({ pending: Json.jNumber, - updated: Json.jArray(jMarkerInfoData), + updated: Json.jArray(jMarkerAttributesData), removed: Json.jArray(Json.jString), reload: Json.jBoolean, }), signals: [], }; -/** Data fetcher for array [`markerInfo`](#markerinfo) */ -export const fetchMarkerInfo: Server.GetRequest< +/** Data fetcher for array [`markerAttributes`](#markerattributes) */ +export const fetchMarkerAttributes: Server.GetRequest< number, - { pending: number, updated: markerInfoData[], removed: string[], + { pending: number, updated: markerAttributesData[], removed: string[], reload: boolean } - >= fetchMarkerInfo_internal; - -const markerInfo_internal: State.Array<string,markerInfoData> = { - name: 'kernel.ast.markerInfo', - getkey: ((d:markerInfoData) => d.key), - signal: signalMarkerInfo, - fetch: fetchMarkerInfo, - reload: reloadMarkerInfo, - order: byMarkerInfoData, + >= fetchMarkerAttributes_internal; + +const markerAttributes_internal: State.Array<string,markerAttributesData> = { + name: 'kernel.ast.markerAttributes', + getkey: ((d:markerAttributesData) => d.key), + signal: signalMarkerAttributes, + fetch: fetchMarkerAttributes, + reload: reloadMarkerAttributes, + order: byMarkerAttributesData, }; -/** Marker information */ -export const markerInfo: State.Array<string,markerInfoData> = markerInfo_internal; - -/** Localizable AST markers */ -export type marker = - Json.key<'#stmt'> | Json.key<'#decl'> | Json.key<'#lval'> | - Json.key<'#expr'> | Json.key<'#term'> | Json.key<'#global'> | - Json.key<'#property'> | Json.key<'#type'>; - -/** Decoder for `marker` */ -export const jMarker: Json.Decoder<marker> = - Json.jUnion<Json.key<'#stmt'> | Json.key<'#decl'> | Json.key<'#lval'> | - Json.key<'#expr'> | Json.key<'#term'> | Json.key<'#global'> | - Json.key<'#property'> | Json.key<'#type'>>( - Json.jKey<'#stmt'>('#stmt'), - Json.jKey<'#decl'>('#decl'), - Json.jKey<'#lval'>('#lval'), - Json.jKey<'#expr'>('#expr'), - Json.jKey<'#term'>('#term'), - Json.jKey<'#global'>('#global'), - Json.jKey<'#property'>('#property'), - Json.jKey<'#type'>('#type'), - ); - -/** Natural order for `marker` */ -export const byMarker: Compare.Order<marker> = Compare.structural; - -/** Location: function and marker */ -export interface location { - /** Function */ - fct: Json.key<'#fct'>; - /** Marker */ - marker: marker; -} - -/** Decoder for `location` */ -export const jLocation: Json.Decoder<location> = - Json.jObject({ fct: Json.jKey<'#fct'>('#fct'), marker: jMarker,}); - -/** Natural order for `location` */ -export const byLocation: Compare.Order<location> = - Compare.byFields - <{ fct: Json.key<'#fct'>, marker: marker }>({ - fct: Compare.string, - marker: byMarker, - }); +/** Marker attributes */ +export const markerAttributes: State.Array<string,markerAttributesData> = markerAttributes_internal; const getMainFunction_internal: Server.GetRequest< null, @@ -494,42 +422,22 @@ const setFiles_internal: Server.SetRequest<string[],null> = { /** Set the source file names to analyze. */ export const setFiles: Server.SetRequest<string[],null>= setFiles_internal; -/** <markerFromTerm> input */ -export interface markerFromTermInput { - /** The statement at which we will build the marker. */ - atStmt: marker; - /** The ACSL term. */ - term: string; -} - -/** Decoder for `markerFromTermInput` */ -export const jMarkerFromTermInput: Json.Decoder<markerFromTermInput> = - Json.jObject({ atStmt: jMarker, term: Json.jString,}); - -/** Natural order for `markerFromTermInput` */ -export const byMarkerFromTermInput: Compare.Order<markerFromTermInput> = - Compare.byFields - <{ atStmt: marker, term: string }>({ - atStmt: byMarker, - term: Compare.string, - }); - -const markerFromTerm_internal: Server.GetRequest< - markerFromTermInput, +const parseTerm_internal: Server.GetRequest< + { term: string, stmt: marker }, marker | undefined > = { kind: Server.RqKind.GET, - name: 'kernel.ast.markerFromTerm', - input: jMarkerFromTermInput, + name: 'kernel.ast.parseTerm', + input: Json.jObject({ term: Json.jString, stmt: jMarker,}), output: Json.jOption(jMarker), signals: [], }; -/** Build a marker from an ACSL term. */ -export const markerFromTerm: Server.GetRequest< - markerFromTermInput, +/** Parse an ACSL Term and returns the associated marker */ +export const parseTerm: Server.GetRequest< + { term: string, stmt: marker }, marker | undefined - >= markerFromTerm_internal; + >= parseTerm_internal; /* ------------------------------------- */ diff --git a/ivette/src/frama-c/kernel/api/properties/index.ts b/ivette/src/frama-c/kernel/api/properties/index.ts index fbbd717805e..ab774d1f222 100644 --- a/ivette/src/frama-c/kernel/api/properties/index.ts +++ b/ivette/src/frama-c/kernel/api/properties/index.ts @@ -37,11 +37,17 @@ import * as Server from 'frama-c/server'; //@ts-ignore import * as State from 'frama-c/states'; +//@ts-ignore +import { byMarker } from 'frama-c/kernel/api/ast'; //@ts-ignore import { bySource } from 'frama-c/kernel/api/ast'; //@ts-ignore +import { jMarker } from 'frama-c/kernel/api/ast'; +//@ts-ignore import { jSource } from 'frama-c/kernel/api/ast'; //@ts-ignore +import { marker } from 'frama-c/kernel/api/ast'; +//@ts-ignore import { source } from 'frama-c/kernel/api/ast'; //@ts-ignore import { byTag } from 'frama-c/kernel/api/data'; @@ -238,7 +244,7 @@ export const alarmsTags: Server.GetRequest<null,tag[]>= alarmsTags_internal; /** Data for array rows [`status`](#status) */ export interface statusData { /** Entry identifier. */ - key: Json.key<'#property'>; + key: marker; /** Full description */ descr: string; /** Kind */ @@ -250,7 +256,7 @@ export interface statusData { /** Function */ fct?: Json.key<'#fct'>; /** Instruction */ - kinstr?: Json.key<'#stmt'>; + kinstr?: marker; /** Position */ source: source; /** Alarm name (if the property is an alarm) */ @@ -264,13 +270,13 @@ export interface statusData { /** Decoder for `statusData` */ export const jStatusData: Json.Decoder<statusData> = Json.jObject({ - key: Json.jKey<'#property'>('#property'), + key: jMarker, descr: Json.jString, kind: jPropKind, names: Json.jArray(Json.jString), status: jPropStatus, fct: Json.jOption(Json.jKey<'#fct'>('#fct')), - kinstr: Json.jOption(Json.jKey<'#stmt'>('#stmt')), + kinstr: Json.jOption(jMarker), source: jSource, alarm: Json.jOption(Json.jString), alarm_descr: Json.jOption(Json.jString), @@ -280,17 +286,17 @@ export const jStatusData: Json.Decoder<statusData> = /** Natural order for `statusData` */ export const byStatusData: Compare.Order<statusData> = Compare.byFields - <{ key: Json.key<'#property'>, descr: string, kind: propKind, - names: string[], status: propStatus, fct?: Json.key<'#fct'>, - kinstr?: Json.key<'#stmt'>, source: source, alarm?: string, - alarm_descr?: string, predicate?: string }>({ - key: Compare.string, + <{ key: marker, descr: string, kind: propKind, names: string[], + status: propStatus, fct?: Json.key<'#fct'>, kinstr?: marker, + source: source, alarm?: string, alarm_descr?: string, + predicate?: string }>({ + key: byMarker, descr: Compare.string, kind: byPropKind, names: Compare.array(Compare.string), status: byPropStatus, fct: Compare.defined(Compare.string), - kinstr: Compare.defined(Compare.string), + kinstr: Compare.defined(byMarker), source: bySource, alarm: Compare.defined(Compare.string), alarm_descr: Compare.defined(Compare.string), @@ -314,7 +320,7 @@ export const reloadStatus: Server.GetRequest<null,null>= reloadStatus_internal; const fetchStatus_internal: Server.GetRequest< number, - { pending: number, updated: statusData[], removed: Json.key<'#property'>[], + { pending: number, updated: statusData[], removed: marker[], reload: boolean } > = { kind: Server.RqKind.GET, @@ -323,7 +329,7 @@ const fetchStatus_internal: Server.GetRequest< output: Json.jObject({ pending: Json.jNumber, updated: Json.jArray(jStatusData), - removed: Json.jArray(Json.jKey<'#property'>('#property')), + removed: Json.jArray(jMarker), reload: Json.jBoolean, }), signals: [], @@ -331,11 +337,11 @@ const fetchStatus_internal: Server.GetRequest< /** Data fetcher for array [`status`](#status) */ export const fetchStatus: Server.GetRequest< number, - { pending: number, updated: statusData[], removed: Json.key<'#property'>[], + { pending: number, updated: statusData[], removed: marker[], reload: boolean } >= fetchStatus_internal; -const status_internal: State.Array<Json.key<'#property'>,statusData> = { +const status_internal: State.Array<marker,statusData> = { name: 'kernel.properties.status', getkey: ((d:statusData) => d.key), signal: signalStatus, @@ -344,6 +350,6 @@ const status_internal: State.Array<Json.key<'#property'>,statusData> = { order: byStatusData, }; /** Status of Registered Properties */ -export const status: State.Array<Json.key<'#property'>,statusData> = status_internal; +export const status: State.Array<marker,statusData> = status_internal; /* ------------------------------------- */ 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 85c3985681b..1ccceb03887 100644 --- a/ivette/src/frama-c/plugins/eva/api/general/index.ts +++ b/ivette/src/frama-c/plugins/eva/api/general/index.ts @@ -99,22 +99,19 @@ export interface CallSite { /** Function */ kf: Json.key<'#fct'>; /** Statement */ - stmt: Json.key<'#stmt'>; + stmt: marker; } /** Decoder for `CallSite` */ export const jCallSite: Json.Decoder<CallSite> = - Json.jObject({ - kf: Json.jKey<'#fct'>('#fct'), - stmt: Json.jKey<'#stmt'>('#stmt'), - }); + Json.jObject({ kf: Json.jKey<'#fct'>('#fct'), stmt: jMarker,}); /** Natural order for `CallSite` */ export const byCallSite: Compare.Order<CallSite> = Compare.byFields - <{ kf: Json.key<'#fct'>, stmt: Json.key<'#stmt'> }>({ + <{ kf: Json.key<'#fct'>, stmt: marker }>({ kf: Compare.string, - stmt: Compare.string, + stmt: byMarker, }); const getCallers_internal: Server.GetRequest<Json.key<'#fct'>,CallSite[]> = { @@ -273,20 +270,20 @@ export const taintStatusTags: Server.GetRequest<null,tag[]>= taintStatusTags_int /** Lvalue taint status */ export interface LvalueTaints { /** tainted lvalue */ - lval: Json.key<'#lval'>; + lval: marker; /** taint status */ taint: taintStatus; } /** Decoder for `LvalueTaints` */ export const jLvalueTaints: Json.Decoder<LvalueTaints> = - Json.jObject({ lval: Json.jKey<'#lval'>('#lval'), taint: jTaintStatus,}); + Json.jObject({ lval: jMarker, taint: jTaintStatus,}); /** Natural order for `LvalueTaints` */ export const byLvalueTaints: Compare.Order<LvalueTaints> = Compare.byFields - <{ lval: Json.key<'#lval'>, taint: taintStatus }>({ - lval: Compare.string, + <{ lval: marker, taint: taintStatus }>({ + lval: byMarker, taint: byTaintStatus, }); @@ -309,7 +306,7 @@ export const taintedLvalues: Server.GetRequest< /** Data for array rows [`properties`](#properties) */ export interface propertiesData { /** Entry identifier. */ - key: Json.key<'#property'>; + key: marker; /** Is the property invalid in some context of the analysis? */ priority: boolean; /** Is the property tainted according to the Eva taint domain? */ @@ -318,17 +315,14 @@ export interface propertiesData { /** Decoder for `propertiesData` */ export const jPropertiesData: Json.Decoder<propertiesData> = - Json.jObject({ - key: Json.jKey<'#property'>('#property'), - priority: Json.jBoolean, - taint: jTaintStatus, + Json.jObject({ key: jMarker, priority: Json.jBoolean, taint: jTaintStatus, }); /** Natural order for `propertiesData` */ export const byPropertiesData: Compare.Order<propertiesData> = Compare.byFields - <{ key: Json.key<'#property'>, priority: boolean, taint: taintStatus }>({ - key: Compare.string, + <{ key: marker, priority: boolean, taint: taintStatus }>({ + key: byMarker, priority: Compare.boolean, taint: byTaintStatus, }); @@ -350,8 +344,8 @@ export const reloadProperties: Server.GetRequest<null,null>= reloadProperties_in const fetchProperties_internal: Server.GetRequest< number, - { pending: number, updated: propertiesData[], - removed: Json.key<'#property'>[], reload: boolean } + { pending: number, updated: propertiesData[], removed: marker[], + reload: boolean } > = { kind: Server.RqKind.GET, name: 'plugins.eva.general.fetchProperties', @@ -359,7 +353,7 @@ const fetchProperties_internal: Server.GetRequest< output: Json.jObject({ pending: Json.jNumber, updated: Json.jArray(jPropertiesData), - removed: Json.jArray(Json.jKey<'#property'>('#property')), + removed: Json.jArray(jMarker), reload: Json.jBoolean, }), signals: [], @@ -367,11 +361,11 @@ const fetchProperties_internal: Server.GetRequest< /** Data fetcher for array [`properties`](#properties) */ export const fetchProperties: Server.GetRequest< number, - { pending: number, updated: propertiesData[], - removed: Json.key<'#property'>[], reload: boolean } + { pending: number, updated: propertiesData[], removed: marker[], + reload: boolean } >= fetchProperties_internal; -const properties_internal: State.Array<Json.key<'#property'>,propertiesData> = { +const properties_internal: State.Array<marker,propertiesData> = { name: 'plugins.eva.general.properties', getkey: ((d:propertiesData) => d.key), signal: signalProperties, @@ -380,7 +374,7 @@ const properties_internal: State.Array<Json.key<'#property'>,propertiesData> = { order: byPropertiesData, }; /** Status of Registered Properties */ -export const properties: State.Array<Json.key<'#property'>,propertiesData> = properties_internal; +export const properties: State.Array<marker,propertiesData> = properties_internal; /** The alarms are counted after being grouped by these categories */ export enum alarmCategory { diff --git a/ivette/src/frama-c/plugins/eva/api/values/index.ts b/ivette/src/frama-c/plugins/eva/api/values/index.ts index 2519fa6327d..fdb7cf5397b 100644 --- a/ivette/src/frama-c/plugins/eva/api/values/index.ts +++ b/ivette/src/frama-c/plugins/eva/api/values/index.ts @@ -70,8 +70,8 @@ export const getCallstacks: Server.GetRequest<marker[],callstack[]>= getCallstac const getCallstackInfo_internal: Server.GetRequest< callstack, - { callee: Json.key<'#fct'>, caller?: Json.key<'#fct'>, - stmt?: Json.key<'#stmt'>, rank?: number }[] + { callee: Json.key<'#fct'>, caller?: Json.key<'#fct'>, stmt?: marker, + rank?: number }[] > = { kind: Server.RqKind.GET, name: 'plugins.eva.values.getCallstackInfo', @@ -80,7 +80,7 @@ const getCallstackInfo_internal: Server.GetRequest< Json.jObject({ callee: Json.jKey<'#fct'>('#fct'), caller: Json.jOption(Json.jKey<'#fct'>('#fct')), - stmt: Json.jOption(Json.jKey<'#stmt'>('#stmt')), + stmt: Json.jOption(jMarker), rank: Json.jOption(Json.jNumber), })), signals: [], @@ -88,31 +88,31 @@ const getCallstackInfo_internal: Server.GetRequest< /** Callstack Description */ export const getCallstackInfo: Server.GetRequest< callstack, - { callee: Json.key<'#fct'>, caller?: Json.key<'#fct'>, - stmt?: Json.key<'#stmt'>, rank?: number }[] + { callee: Json.key<'#fct'>, caller?: Json.key<'#fct'>, stmt?: marker, + rank?: number }[] >= getCallstackInfo_internal; const getStmtInfo_internal: Server.GetRequest< - Json.key<'#stmt'>, + marker, { rank: number, fct: Json.key<'#fct'> } > = { kind: Server.RqKind.GET, name: 'plugins.eva.values.getStmtInfo', - input: Json.jKey<'#stmt'>('#stmt'), + input: jMarker, output: Json.jObject({ rank: Json.jNumber, fct: Json.jKey<'#fct'>('#fct'), }), signals: [], }; /** Stmt Information */ export const getStmtInfo: Server.GetRequest< - Json.key<'#stmt'>, + marker, { rank: number, fct: Json.key<'#fct'> } >= getStmtInfo_internal; const getProbeInfo_internal: Server.GetRequest< marker, - { condition: boolean, effects: boolean, rank: number, - stmt?: Json.key<'#stmt'>, code?: string, evaluable: boolean } + { condition: boolean, effects: boolean, rank: number, stmt?: marker, + code?: string, evaluable: boolean } > = { kind: Server.RqKind.GET, name: 'plugins.eva.values.getProbeInfo', @@ -121,7 +121,7 @@ const getProbeInfo_internal: Server.GetRequest< condition: Json.jBoolean, effects: Json.jBoolean, rank: Json.jNumber, - stmt: Json.jOption(Json.jKey<'#stmt'>('#stmt')), + stmt: Json.jOption(jMarker), code: Json.jOption(Json.jString), evaluable: Json.jBoolean, }), @@ -130,8 +130,8 @@ const getProbeInfo_internal: Server.GetRequest< /** Probe informations */ export const getProbeInfo: Server.GetRequest< marker, - { condition: boolean, effects: boolean, rank: number, - stmt?: Json.key<'#stmt'>, code?: string, evaluable: boolean } + { condition: boolean, effects: boolean, rank: number, stmt?: marker, + code?: string, evaluable: boolean } >= getProbeInfo_internal; /** Evaluation of an expression or lvalue */ diff --git a/ivette/src/frama-c/plugins/eva/valuetable.tsx b/ivette/src/frama-c/plugins/eva/valuetable.tsx index a89b9ee1207..5e103a1f5aa 100644 --- a/ivette/src/frama-c/plugins/eva/valuetable.tsx +++ b/ivette/src/frama-c/plugins/eva/valuetable.tsx @@ -49,7 +49,7 @@ import { ipcRenderer } from 'electron'; type Request<A, B> = (a: A) => Promise<B>; -type Alarm = [ 'True' | 'False' | 'Unknown', string ] +type Alarm = ['True' | 'False' | 'Unknown', string] function getAlarmStatus(alarms: Alarm[] | undefined): string { if (!alarms) return 'none'; if (alarms.length === 0) return 'none'; @@ -57,19 +57,19 @@ function getAlarmStatus(alarms: Alarm[] | undefined): string { else return 'Unknown'; } -type MarkerTracked = [ 'Tracked', boolean ] -type MarkerPinned = [ 'Pinned' , boolean ] -type MarkerStatus = MarkerTracked | MarkerPinned | 'JustFocused' +type MarkerTracked = ['Tracked', boolean] +type MarkerPinned = ['Pinned', boolean] +type MarkerStatus = MarkerTracked | MarkerPinned | 'JustFocused' function MarkerStatusClass(status: MarkerStatus): string { if (status === 'JustFocused') return 'eva-header-just-focused'; - const [ kind, focused ] = status; + const [kind, focused] = status; return 'eva-header-' + kind.toLowerCase() + (focused ? '-focused' : ''); } function isPinnedMarker(status: MarkerStatus): boolean { if (status === 'JustFocused') return false; - const [ kind ] = status; + const [kind] = status; return kind === 'Pinned'; } @@ -84,7 +84,7 @@ function TableCell(props: TableCellProps): JSX.Element { const leftVisible = align === 'center' ? 'block' : 'none'; return ( <div className='eva-cell-container'> - <div className='eva-cell-left' style={{ display: leftVisible }}/> + <div className='eva-cell-left' style={{ display: leftVisible }} /> <div className='eva-cell-content'> {children} </div> @@ -177,12 +177,12 @@ interface Probe extends Location { } /* Builds a cached version of the `getValues` request */ -function useEvaluationCache(): Request<[ Location, callstack ], Evaluation> { - type LocStack = [ Location, callstack ]; - const toString = React.useCallback(([ l, c ] : LocStack): string => { +function useEvaluationCache(): Request<[Location, callstack], Evaluation> { + type LocStack = [Location, callstack]; + const toString = React.useCallback(([l, c]: LocStack): string => { return `${l.fct}:${l.target}:${c}`; }, []); - const get: Request<LocStack, Evaluation> = React.useCallback(([ l, c ]) => { + const get: Request<LocStack, Evaluation> = React.useCallback(([l, c]) => { const callstack = c === 'Summary' ? undefined : c; return Server.send(Values.getValues, { ...l, callstack }); }, []); @@ -195,9 +195,9 @@ function useProbeCache(): Request<Location, Probe> { const cache = useEvaluationCache(); const get = React.useCallback(async (loc: Location): Promise<Probe> => { const infos = await Server.send(Values.getProbeInfo, loc.target); - const evaluate: Request<callstack, Evaluation> = (c) => cache([ loc, c ]); + const evaluate: Request<callstack, Evaluation> = (c) => cache([loc, c]); return { ...loc, ...infos, evaluate }; - }, [ cache ]); + }, [cache]); return Dome.useCache(get, toString); } @@ -215,16 +215,13 @@ interface StmtProps { short?: boolean; } -function Stmt(props: StmtProps): JSX.Element { - const markersInfo = States.useSyncArray(Ast.markerInfo); +function Stmt(props: StmtProps): JSX.Element | null { const { stmt, marker, short } = props; - if (!stmt || !marker) return <></>; - const line = markersInfo.getData(marker)?.sloc?.line; - const filename = markersInfo.getData(marker)?.sloc?.base; - const title = markersInfo.getData(stmt)?.descr; - const text = short ? `@L${line}` : `@${filename}:${line}`; + const { descr, sloc: { base, line } } = States.useMarker(marker); + if (!stmt || !marker) return null; + const label = short ? `@L${line}` : `@${base}:${line}`; const className = 'dome-text-cell eva-stmt'; - return <span className={className} title={title}>{text}</span>; + return <span className={className} title={descr}>{label}</span>; } /* -------------------------------------------------------------------------- */ @@ -345,7 +342,7 @@ function ProbeHeader(props: ProbeHeaderProps): JSX.Element { const isPinned = isPinnedMarker(status); const pinText = isPinned ? 'Unpin' : 'Pin'; - const loc: States.SelectionActions = { location: { fct, marker: target} }; + const loc: States.SelectionActions = { location: { fct, marker: target } }; const onClick = (): void => { setSelection(loc); selectProbe(); }; const onDoubleClick = (): void => pinProbe(!isPinned); const onContextMenu = (): void => { @@ -373,7 +370,7 @@ function ProbeHeader(props: ProbeHeaderProps): JSX.Element { onClick={() => removeProbe()} /> </div>; - + return ( <th ref={ref} @@ -387,7 +384,7 @@ function ProbeHeader(props: ProbeHeaderProps): JSX.Element { <div className='eva-header-text-overflow'> <span className='dome-text-cell' title={code}>{code}</span> </div> - <Stmt stmt={stmt} marker={target} short={true}/> + <Stmt stmt={stmt} marker={target} short={true} /> </TableCell> </th> ); @@ -621,7 +618,7 @@ async function FunctionSection(props: FunctionProps): Promise<JSX.Element> { /* Computes the relevant data for each marker */ interface Data { probe: Probe; summary: Evaluation; status: MarkerStatus } const entries = Array.from(props.markers.entries()); - const data = await Promise.all(entries.map(async ([ target, status ]) => { + const data = await Promise.all(entries.map(async ([target, status]) => { const probe = await props.getProbe({ target, fct }); const summary = await probe.evaluate('Summary'); return { probe, summary, status } as Data; @@ -795,8 +792,8 @@ class FunctionInfos { const { target: tgt, fct } = focusedLoc ?? {}; const inFct = fct !== undefined && fct === this.fct; const ms = new Map<Ast.marker, MarkerStatus>(); - this.pinned.forEach((p) => ms.set(p, [ 'Pinned', inFct && tgt === p ])); - this.tracked.forEach((p) => ms.set(p, [ 'Tracked', inFct && tgt === p ])); + this.pinned.forEach((p) => ms.set(p, ['Pinned', inFct && tgt === p])); + this.tracked.forEach((p) => ms.set(p, ['Tracked', inFct && tgt === p])); if (inFct && tgt && !this.has(tgt)) ms.set(tgt, 'JustFocused'); return new Map(Array.from(ms.entries()).reverse()); } @@ -892,7 +889,7 @@ class FunctionsManager { map<A>(func: (infos: FunctionInfos, fct: string) => A): A[] { const entries = Array.from(this.cache.entries()); - return entries.map(([ fct, infos ]) => func(infos, fct)); + return entries.map(([fct, infos]) => func(infos, fct)); } } @@ -909,7 +906,7 @@ export const evaluateEvent = new Dome.Event('dome.evaluate'); ipcRenderer.on('dome.ipc.evaluate', () => evaluateEvent.emit()); interface EvaluationModeProps { - computationState : Eva.computationStateType | undefined; + computationState: Eva.computationStateType | undefined; selection: States.Selection; setLocPin: (loc: Location, pin: boolean) => void; } @@ -935,8 +932,8 @@ function useEvaluationMode(props: EvaluationModeProps): void { const shortcut = System.platform === 'macos' ? 'Cmd+E' : 'Ctrl+E'; const onEnter = (pattern: string): void => { const marker = selection?.current?.marker; - const data = { atStmt: marker, term: pattern }; - Server.send(Ast.markerFromTerm, data).then(addProbe).catch(handleError); + const data = { stmt: marker, term: pattern }; + Server.send(Ast.parseTerm, data).then(addProbe).catch(handleError); }; const evalMode = { label: 'Evaluation', @@ -973,21 +970,21 @@ const FocusState = new GlobalState<Probe | undefined>(undefined); function EvaTable(): JSX.Element { /* Component state */ - const [ selection, select ] = States.useSelection(); - const [ cs, setCS ] = useGlobalState(CallstackState); - const [ fcts ] = useGlobalState(FunctionsManagerState); - const [ focus, setFocus ] = useGlobalState(FocusState); + const [selection, select] = States.useSelection(); + const [cs, setCS] = useGlobalState(CallstackState); + const [fcts] = useGlobalState(FunctionsManagerState); + const [focus, setFocus] = useGlobalState(FocusState); /* Used to force the component update. We cannot use the `forceUpdate` hook * proposed by Dome as we need to be able to add dependencies on a changing * value (here tac) explicitly. We need to force the update as modifications * of the Function Manager internal data does NOT trigger the component * update. */ - const [ tac, setTic ] = React.useState(0); + const [tac, setTic] = React.useState(0); /* Event use to communicate when a location is selected. Used to scroll * related column into view if needed */ - const [ locEvt ] = React.useState(new Dome.Event<Location>('eva-location')); + const [locEvt] = React.useState(new Dome.Event<Location>('eva-location')); /* Build cached version of needed server's requests */ const getProbe = useProbeCache(); @@ -999,14 +996,14 @@ function EvaTable(): JSX.Element { const selectedCSInfos = await getCallsites(cs); if (selectedCSInfos.length === 0) return undefined; else return selectedCSInfos[0].callee; - }, [ cs, getCallsites ]); + }, [cs, getCallsites]); const { result: csFct } = Dome.usePromise(csFctPromise); /* Reset the selected callstack when the corresponding function is removed */ React.useEffect(() => { if (csFct && fcts.isEmpty(csFct) && focus?.fct !== csFct) setCS('Summary'); - }, [ csFct, setCS, fcts, focus?.fct ] ); + }, [csFct, setCS, fcts, focus?.fct]); /* Updated the focused Probe when the selection changes. Also emit on the * `locEvent` event. */ @@ -1022,7 +1019,7 @@ function EvaTable(): JSX.Element { }; if (loc) getProbe(loc).then(doUpdate); else setFocus(undefined); - }, [ fcts, selection, getProbe, setFocus, locEvt ]); + }, [fcts, selection, getProbe, setFocus, locEvt]); /* Callback used to pin or unpin a location */ const setLocPin = React.useCallback((loc: Location, pin: boolean): void => { @@ -1034,7 +1031,7 @@ function EvaTable(): JSX.Element { /* On meta-selection, pin the selected location. */ React.useEffect(() => { const pin = (loc: States.Location): void => { - const {marker, fct} = loc; + const { marker, fct } = loc; if (marker && fct) setLocPin({ target: marker, fct }, true); }; States.MetaSelection.on(pin); @@ -1052,7 +1049,7 @@ function EvaTable(): JSX.Element { fcts.clean(focus); } setTic(tac + 1); - }, [ fcts, focus, setFocus, tac ]); + }, [fcts, focus, setFocus, tac]); /* Builds the sections for each function. As the component is built * asynchronously, we have to use the `usePromise` hook, which forces us to @@ -1103,13 +1100,13 @@ function EvaTable(): JSX.Element { }); return Promise.all(ps.map(FunctionSection)); }, - [ cs, setCS, fcts, focus, setFocus, tac, getCallsites, setLocPin, csFct, - getCallstacks, getProbe, remove, select, locEvt ]); + [cs, setCS, fcts, focus, setFocus, tac, getCallsites, setLocPin, csFct, + getCallstacks, getProbe, remove, select, locEvt]); const { result: functions } = Dome.usePromise(functionsPromise); /* Builds the alarms component. As for the function sections, it is an * asynchronous process. */ - const alarmsProm = React.useMemo(() => AlarmsInfos(focus)(cs), [ focus, cs ]); + const alarmsProm = React.useMemo(() => AlarmsInfos(focus)(cs), [focus, cs]); const { result: alarmsInfos } = Dome.usePromise(alarmsProm); /* Builds the stacks component. As for the function sections, it is an @@ -1121,7 +1118,7 @@ function EvaTable(): JSX.Element { const isSelected = callsites.find(p) !== undefined; const close = (): void => setCS('Summary'); return StackInfos({ callsites, isSelected, setSelection: select, close }); - }, [ cs, setCS, select, getCallsites, selection ]); + }, [cs, setCS, select, getCallsites, selection]); const { result: stackInfos } = Dome.usePromise(stackInfosPromise); /* Handle Evaluation mode */ @@ -1135,7 +1132,7 @@ function EvaTable(): JSX.Element { <div className='eva-functions-section'> {React.Children.toArray(functions)} </div> - <Vfill/> + <Vfill /> {alarmsInfos} {stackInfos} </> diff --git a/ivette/src/frama-c/plugins/studia/api/studia/index.ts b/ivette/src/frama-c/plugins/studia/api/studia/index.ts index a5a8fb11a1e..4d55bfa8523 100644 --- a/ivette/src/frama-c/plugins/studia/api/studia/index.ts +++ b/ivette/src/frama-c/plugins/studia/api/studia/index.ts @@ -68,24 +68,24 @@ export const byEffects: Compare.Order<effects> = indirect: Compare.array(Compare.pair(Compare.string,byMarker,)), }); -const getReadsLval_internal: Server.GetRequest<Json.key<'#lval'>,effects> = { +const getReadsLval_internal: Server.GetRequest<marker,effects> = { kind: Server.RqKind.GET, name: 'plugins.studia.studia.getReadsLval', - input: Json.jKey<'#lval'>('#lval'), + input: jMarker, output: jEffects, signals: [], }; /** Get the list of statements that read a lval. */ -export const getReadsLval: Server.GetRequest<Json.key<'#lval'>,effects>= getReadsLval_internal; +export const getReadsLval: Server.GetRequest<marker,effects>= getReadsLval_internal; -const getWritesLval_internal: Server.GetRequest<Json.key<'#lval'>,effects> = { +const getWritesLval_internal: Server.GetRequest<marker,effects> = { kind: Server.RqKind.GET, name: 'plugins.studia.studia.getWritesLval', - input: Json.jKey<'#lval'>('#lval'), + input: jMarker, output: jEffects, signals: [], }; /** Get the list of statements that write a lval. */ -export const getWritesLval: Server.GetRequest<Json.key<'#lval'>,effects>= getWritesLval_internal; +export const getWritesLval: Server.GetRequest<marker,effects>= getWritesLval_internal; /* ------------------------------------- */ diff --git a/ivette/src/frama-c/states.ts b/ivette/src/frama-c/states.ts index a4287f3245a..ea4469337c9 100644 --- a/ivette/src/frama-c/states.ts +++ b/ivette/src/frama-c/states.ts @@ -792,6 +792,37 @@ export async function resetSelection(): Promise<void> { } } +// -------------------------------------------------------------------------- +// --- Markers +// -------------------------------------------------------------------------- + +export type attributes = Ast.markerAttributesData; + +export const defaultMarker: attributes = { + key: '', + labelKind: '', + titleKind: '', + name: '', + descr: '', + isLval: false, + isFunDecl: false, + isFun: false, + sloc: { dir: '', base: '', file: '', line: 0 }, +}; + +/** Access the marker attributes from AST. */ +export function useMarker(marker: Ast.marker | undefined): attributes { + const marks = useSyncArray(Ast.markerAttributes); + if (marker === undefined) return defaultMarker; + const attrs = marks.getData(marker); + if (attrs === undefined) return defaultMarker; + return attrs; +} + +// -------------------------------------------------------------------------- +// --- General Synchro +// -------------------------------------------------------------------------- + Server.onReady(() => { if (GlobalSelection.getValue() === emptySelection) resetSelection(); diff --git a/src/kernel_services/ast_data/globals.ml b/src/kernel_services/ast_data/globals.ml index c82ac01c137..42b251943f4 100644 --- a/src/kernel_services/ast_data/globals.ml +++ b/src/kernel_services/ast_data/globals.ml @@ -303,6 +303,11 @@ module Functions = struct in State.memo add vi + let mem vi = + vi.vglob && + try let _ = get vi in true + with Not_found -> false + let get_params kf = match kf.fundec with | Definition(f,_loc) -> f.sformals diff --git a/src/kernel_services/ast_data/globals.mli b/src/kernel_services/ast_data/globals.mli index d2e0cc20be1..51ff3e98930 100644 --- a/src/kernel_services/ast_data/globals.mli +++ b/src/kernel_services/ast_data/globals.mli @@ -96,6 +96,9 @@ module Functions: sig (** {2 Getters} *) + val mem: varinfo -> bool + (** Returns [true] is this variable is associated to some kernel function *) + val get: varinfo -> kernel_function (** @raise Not_found if the given varinfo has no associated kernel function and is not a built-in. diff --git a/src/kernel_services/ast_printing/printer_tag.ml b/src/kernel_services/ast_printing/printer_tag.ml index 9f5fe65f343..7c1e8c4aadc 100644 --- a/src/kernel_services/ast_printing/printer_tag.ml +++ b/src/kernel_services/ast_printing/printer_tag.ml @@ -233,10 +233,13 @@ let ki_of_localizable loc = match loc with | PType _ -> Kglobal let varinfo_of_localizable = function - | PLval (_, _, (Var vi, NoOffset)) -> Some vi - | PVDecl (_, _, vi) -> Some vi - | PGlobal (GVar (vi, _, _) | GVarDecl (vi, _) - | GFunDecl (_, vi, _) | GFun ({svar = vi }, _)) -> Some vi + | PVDecl (_, _, vi) + | PLval (_, _, (Var vi, NoOffset)) + | PTermLval (_, _, _, (TVar { lv_origin = Some vi }, TNoOffset)) + | PGlobal ( + GVar (vi, _, _) | GVarDecl (vi, _) | + GFun ({svar = vi },_) | GFunDecl (_, vi, _) + ) -> Some vi | _ -> None let typ_of_localizable = function @@ -490,16 +493,16 @@ let loc_to_localizable ?(precise_col=false) loc = (* --- Printer API --- *) (* -------------------------------------------------------------------------- *) -module type TAG = +module type INFO = sig - val create : localizable -> string + val tag : localizable -> string val unfold : stmt -> bool end (* We delay the creation of the class to execution time, so that all pretty-printer extensions get properly registered (as we want to inherit from them). The only known solution is to use a functor *) -module BUILD(Tag : TAG)(X: Printer.PrinterClass) : Printer.PrinterClass = +module BUILD(Info : INFO)(X: Printer.PrinterClass) : Printer.PrinterClass = struct class printer : Printer.extensible_printer = object(self) @@ -584,10 +587,10 @@ struct method! next_stmt next fmt current = - if Tag.unfold current + if Info.unfold current then self#preconditions_at_call fmt current; Format.fprintf fmt "@{<%s>%a@}" - (Tag.create (PStmt (Option.get self#current_kf,current))) + (Info.tag (PStmt (Option.get self#current_kf,current))) (super#next_stmt next) current method! lval fmt lv = @@ -596,7 +599,7 @@ struct (* Do not highlight the lvals in initializers. *) | Kstmt _ as ki -> Format.fprintf fmt "@{<%s>" - (Tag.create (PLval (self#current_kf,ki,lv))); + (Info.tag (PLval (self#current_kf,ki,lv))); (match lv with | Var vi, (Field _| Index _ as o) -> (* Small hack to be able to click on the arrays themselves @@ -616,7 +619,7 @@ struct self#lval fmt lv | _ -> Format.fprintf fmt "@{<%s>" - (Tag.create (PExp (self#current_kf,self#current_kinstr,e))); + (Info.tag (PExp (self#current_kf,self#current_kinstr,e))); super#exp fmt e; Format.fprintf fmt "@}" @@ -632,7 +635,7 @@ struct super#term_lval fmt lv | Some ip -> Format.fprintf fmt "@{<%s>" - (Tag.create + (Info.tag (PTermLval (self#current_kf, self#current_kinstr, ip, lv))); (match lv with | TVar vi, (TField _| TIndex _ as o) -> @@ -645,12 +648,12 @@ struct method! vdecl fmt vi = Format.fprintf fmt "@{<%s>%a@}" - (Tag.create (PVDecl (self#current_kf, self#current_kinstr, vi))) + (Info.tag (PVDecl (self#current_kf, self#current_kinstr, vi))) super#vdecl vi method private tag_property p = current_property <- Some p; - Tag.create (PIP p) + Info.tag (PIP p) method! code_annotation fmt ca = match ca.annot_content with @@ -688,7 +691,7 @@ struct | GType _ -> super#global fmt g | GAsm _ | GPragma _ | GText _ | GAnnot _ -> - Format.fprintf fmt "@{<%s>%a@}" (Tag.create (PGlobal g)) super#global g + Format.fprintf fmt "@{<%s>%a@}" (Info.tag (PGlobal g)) super#global g method! extended fmt ext = let loc = @@ -793,14 +796,14 @@ struct self#current_behavior_or_loop from) in Format.fprintf fmt "@{<%s>%a@}" - (Tag.create (PIP ip)) (super#from s) from + (Info.tag (PIP ip)) (super#from s) from method! global_annotation fmt a = match Property.ip_of_global_annotation_single a with | None -> super#global_annotation fmt a | Some ip -> Format.fprintf fmt "@{<%s>%a@}" - (Tag.create (PIP ip)) super#global_annotation a + (Info.tag (PIP ip)) super#global_annotation a method! allocation ~isloop fmt a = match @@ -810,39 +813,39 @@ struct None -> super#allocation ~isloop fmt a | Some ip -> Format.fprintf fmt "@{<%s>%a@}" - (Tag.create (PIP ip)) (super#allocation ~isloop) a; + (Info.tag (PIP ip)) (super#allocation ~isloop) a; method! stmtkind sattr next fmt sk = (* Special tag denoting the start of the statement, WITHOUT any ACSL assertion/statement contract, etc. *) let s = Option.get self#current_stmt in let f = Option.get self#current_kf in - let tag = Tag.create (PStmtStart(f,s)) in + let tag = Info.tag (PStmtStart(f,s)) in Format.fprintf fmt "@{<%s>%a@}" tag (super#stmtkind sattr next) sk method! ikind fmt c = Format.fprintf fmt "@{<%s>%a@}" - (Tag.create (PType(TInt(c,[])))) + (Info.tag (PType(TInt(c,[])))) super#ikind c method! fkind fmt c = Format.fprintf fmt "@{<%s>%a@}" - (Tag.create (PType(TFloat(c,[])))) + (Info.tag (PType(TFloat(c,[])))) super#fkind c method! compname fmt comp = Format.fprintf fmt "@{<%s>%a@}" - (Tag.create (PGlobal(Globals.Types.global Struct comp.cname))) + (Info.tag (PGlobal(Globals.Types.global Struct comp.cname))) super#compname comp method! enuminfo fmt enum = Format.fprintf fmt "@{<%s>%a@}" - (Tag.create (PGlobal(Globals.Types.global Enum enum.ename))) + (Info.tag (PGlobal(Globals.Types.global Enum enum.ename))) super#enuminfo enum method! typeinfo fmt tinfo = Format.fprintf fmt "@{<%s>%a@}" - (Tag.create (PGlobal(Globals.Types.global Typedef tinfo.tname))) + (Info.tag (PGlobal(Globals.Types.global Typedef tinfo.tname))) super#typeinfo tinfo initializer force_brace <- true @@ -852,7 +855,7 @@ end module type Tag = sig - val create : localizable -> string + val tag : localizable -> string end module type S_pp = @@ -871,11 +874,11 @@ struct let printer () = let pp = Printer.current_printer () in let module PP = (val pp: Printer.PrinterClass) in - let module TAG = struct - let create = T.create + let module INFO = struct + let tag = T.tag let unfold s = !unfold s end in - let module TagPrinterClass = BUILD(TAG)(PP) in + let module TagPrinterClass = BUILD(INFO)(PP) in new TagPrinterClass.printer let with_unfold_precond unfolder f fmt x = diff --git a/src/kernel_services/ast_printing/printer_tag.mli b/src/kernel_services/ast_printing/printer_tag.mli index 57de33aaba6..9da67df17c2 100644 --- a/src/kernel_services/ast_printing/printer_tag.mli +++ b/src/kernel_services/ast_printing/printer_tag.mli @@ -74,7 +74,7 @@ val loc_to_localizable: ?precise_col:bool -> Filepath.position -> localizable op module type Tag = sig - val create : localizable -> string + val tag : localizable -> string end module type S_pp = diff --git a/src/plugins/eva/api/general_requests.ml b/src/plugins/eva/api/general_requests.ml index 37a7c0daaec..fdb4c9cb1f3 100644 --- a/src/plugins/eva/api/general_requests.ml +++ b/src/plugins/eva/api/general_requests.ml @@ -457,8 +457,8 @@ module PropertiesData = struct ~package ~name:"properties" ~descr:(Markdown.plain "Status of Registered Properties") - ~key:(fun ip -> Kernel_ast.Marker.create (PIP ip)) - ~keyType:Kernel_ast.Marker.jproperty + ~key:(fun ip -> Kernel_ast.Marker.tag (PIP ip)) + ~keyType:Kernel_ast.Marker.jtype ~iter:Property_status.iter model end diff --git a/src/plugins/gui/pretty_source.ml b/src/plugins/gui/pretty_source.ml index e68c355f644..b2d1ddff1c5 100644 --- a/src/plugins/gui/pretty_source.ml +++ b/src/plugins/gui/pretty_source.ml @@ -203,12 +203,12 @@ struct | PIP _ -> 'i' | PType _ -> 'y' - let create loc = + let tag loc = incr current ; let tag = Printf.sprintf "guitag:%c%x" (charcode loc) !current in Hashtbl.replace hashtbl tag loc ; tag - let get = Hashtbl.find hashtbl + let find = Hashtbl.find hashtbl end @@ -261,7 +261,7 @@ let buffer_formatter state source = let s = Extlib.format_string_of_stag s in (* Ignore tags that are not ours *) if Extlib.string_prefix "guitag:" s then - Stack.push (source#end_iter#offset, Tag.get s) starts ; + Stack.push (source#end_iter#offset, Tag.find s) starts ; "" in let emit_close_tag s = diff --git a/src/plugins/server/kernel_ast.ml b/src/plugins/server/kernel_ast.ml index 6864cbb2b69..f21cc6e95b8 100644 --- a/src/plugins/server/kernel_ast.ml +++ b/src/plugins/server/kernel_ast.ml @@ -96,90 +96,6 @@ end (* --- Printers --- *) (* -------------------------------------------------------------------------- *) -(* The kind of a marker. *) -module MarkerKind = struct - - let kinds = Enum.dictionary () - - let kind name = - Enum.tag - ~name - ~descr:(Md.plain (String.capitalize_ascii name)) - kinds - - let expr = kind "expression" - let lval = kind "lvalue" - let decl = kind "declaration" - let stmt = kind "statement" - let glob = kind "global" - let term = kind "term" - let prop = kind "property" - let typ = kind "type" - - let () = - Enum.set_lookup - kinds - (fun localizable -> - let open Printer_tag in - match localizable with - | PStmt _ -> stmt - | PStmtStart _ -> stmt - | PVDecl _ -> decl - | PLval _ -> lval - | PExp _ -> expr - | PTermLval _ -> term - | PGlobal _ -> glob - | PIP _ -> prop - | PType _ -> typ) - - let data = - Request.dictionary - ~package - ~name:"markerKind" - ~descr:(Md.plain "Marker kind") - kinds - - include (val data : S with type t = Printer_tag.localizable) -end - -module MarkerVar = struct - - let vars = Enum.dictionary () - - let kind name = - Enum.tag - ~name - ~descr:(Md.plain (String.capitalize_ascii name)) - vars - - let none = kind "none" - let var = kind "variable" - let fct = kind "function" - - let () = - Enum.set_lookup - vars - (fun localizable -> - let open Printer_tag in - match localizable with - | PLval (_, _, (Var vi, NoOffset)) - | PVDecl (_, _, vi) - | PGlobal (GVar (vi, _, _) | GVarDecl (vi, _)) -> - if Cil.isFunctionType vi.vtype then fct else var - | PGlobal (GFun _ | GFunDecl _) -> fct - | PLval _ | PStmt _ | PStmtStart _ - | PExp _ | PTermLval _ | PGlobal _ | PIP _ | PType _ -> none) - - let data = - Request.dictionary - ~package - ~name:"markerVar" - ~descr:(Md.plain "Marker variable") - vars - - include (val data : S with type t = Printer_tag.localizable) -end - module Marker = struct @@ -217,66 +133,6 @@ struct let iter f = Localizable.Hashtbl.iter (fun key str -> f (key, str)) (STATE.get ()).tags - let array = - let model = States.model () in - let () = - States.column - ~name:"kind" - ~descr:(Md.plain "Marker kind") - ~data:(module MarkerKind) - ~get:fst - model - in - let () = - States.column - ~name:"var" - ~descr:(Md.plain "Marker variable") - ~data:(module MarkerVar) - ~get:fst - model - in - let () = - States.column - ~name:"name" - ~descr:(Md.plain "Marker short name") - ~data:(module Jalpha) - ~get:(fun (tag, _) -> Printer_tag.label tag) - model - in - let () = - States.column - ~name:"descr" - ~descr:(Md.plain "Marker declaration or description") - ~data:(module Jstring) - ~get:(fun (tag, _) -> Rich_text.to_string Printer_tag.pretty tag) - model - in - let () = - States.option - ~name:"scope" - ~descr:(Md.plain "Function scope of the marker, if applicable") - ~data:(module Jstring) - ~get:(fun (tag, _) -> - Option.map Kernel_function.get_name @@ - Printer_tag.kf_of_localizable tag) - model - in - let () = - States.column - ~name:"sloc" - ~descr:(Md.plain "Source location") - ~data:(module Position) - ~get:(fun (tag, _) -> fst (Printer_tag.loc_of_localizable tag)) - model - in - States.register_array - ~package - ~name:"markerInfo" - ~descr:(Md.plain "Marker information") - ~key:snd ~keyType:Jstring - ~iter ~add_reload_hook:ast_update_hook - model - let create_tag = function | PStmt(_,s) -> Printf.sprintf "#s%d" s.sid | PStmtStart(_,s) -> Printf.sprintf "#k%d" s.sid @@ -288,40 +144,29 @@ struct | PIP _ -> Printf.sprintf "#p%d" (incr kid ; !kid) | PType _ -> Printf.sprintf "#y%d" (incr kid ; !kid) - let create loc = + let hooks = ref [] + let hook f = hooks := !hooks @ [f] + + let tag loc = let { tags ; locs } = STATE.get () in try Localizable.Hashtbl.find tags loc with Not_found -> let tag = create_tag loc in Localizable.Hashtbl.add tags loc tag ; Hashtbl.add locs tag loc ; - States.update array (loc, tag); - tag + List.iter (fun fn -> fn (loc,tag)) !hooks ; tag - let lookup tag = Hashtbl.find (STATE.get()).locs tag + let find tag = Hashtbl.find (STATE.get()).locs tag type t = localizable - let markers = ref [] - let jmarker kd = - let jt = Pkg.Jkey kd in markers := jt :: !markers ; jt - - let jstmt = jmarker "stmt" - let jdecl = jmarker "decl" - let jlval = jmarker "lval" - let jexpr = jmarker "expr" - let jterm = jmarker "term" - let jglobal = jmarker "global" - let jproperty = jmarker "property" - let jtyp = jmarker "type" - let jtype = Data.declare ~package ~name:"marker" ~descr:(Md.plain "Localizable AST markers") - Pkg.(Junion (List.rev !markers)) + (Pkg.Jkey "marker") - let to_json loc = `String (create loc) + let to_json loc = `String (tag loc) let of_json js = - try lookup (Js.to_string js) + try find (Js.to_string js) with Not_found -> Data.failure "not a localizable marker" end @@ -334,27 +179,36 @@ module Printer = Printer_tag.Make(Marker) module Lval = struct + open Printer_tag + type t = kinstr * lval - let jtype = Marker.jlval + let jtype = Marker.jtype + let to_json (kinstr, lval) = let kf = match kinstr with | Kglobal -> None | Kstmt stmt -> Some (Kernel_function.find_englobing_kf stmt) in Marker.to_json (PLval (kf, kinstr, lval)) - let of_json js = - let open Printer_tag in - match Marker.of_json js with + + let find = function | PLval (_, kinstr, lval) -> kinstr, lval | PVDecl (_, kinstr, vi) -> kinstr, Cil.var vi | PGlobal (GVar (vi, _, _) | GVarDecl (vi, _)) -> Kglobal, Cil.var vi - | _ -> Data.failure "not a lval marker" + | _ -> raise Not_found + + let mem tag = try let _ = find tag in true with Not_found -> false + + let of_json js = + try find (Marker.of_json js) + with Not_found -> Data.failure "not a lval marker" + end module Stmt = struct type t = stmt - let jtype = Marker.jstmt + let jtype = Marker.jtype let to_json st = let kf = Kernel_function.find_englobing_kf st in Marker.to_json (PStmt(kf,st)) @@ -368,7 +222,7 @@ end module Ki = struct type t = kinstr - let jtype = Pkg.Joption Marker.jstmt + let jtype = Pkg.Joption Marker.jtype let to_json = function | Kglobal -> `Null | Kstmt st -> Stmt.to_json st @@ -430,6 +284,138 @@ module KfMarker = struct R.get fct r, R.get marker r end +(* -------------------------------------------------------------------------- *) +(* --- Marker Attributes --- *) +(* -------------------------------------------------------------------------- *) + +module Attributes = +struct + open Printer_tag + + let descr ~short m = + match varinfo_of_localizable m with + | Some vi -> + if Globals.Functions.mem vi then "Function" else + if vi.vglob then + if short then "Global" else "Global Variable" + else if vi.vformal then + if short then "Formal" else "Formal Parameter" + else if vi.vtemp then + if short then "Temp" else "Temporary Variable (generated)" + else + if short then "Local" else "Local Variable" + | None -> + match m with + | PStmt _ | PStmtStart _ -> if short then "Stmt" else "Statement" + | PLval _ -> if short then "Lval" else "L-value" + | PTermLval _ -> if short then "Lval" else "ACSL L-value" + | PVDecl _ -> assert false + | PExp _ -> if short then "Expr" else "Expression" + | PIP _ -> if short then "Prop" else "Property" + | PGlobal _ -> if short then "Decl" else "Declaration" + | PType _ -> "Type" + + let kf tag = + match varinfo_of_localizable tag with + | Some vi -> Globals.Functions.get vi + | None -> raise Not_found + + let fundecl = function + | PGlobal (GVar (vi, _, _) | GVarDecl (vi, _)) -> Globals.Functions.get vi + | _ -> raise Not_found + + let is_kf tag = try let _ = kf tag in true with Not_found -> false + let is_fundecl tag = try let _ = fundecl tag in true with Not_found -> false + + let scope tag = + Option.map Kernel_function.get_name @@ Printer_tag.kf_of_localizable tag + + let model = States.model () + + let () = + States.column + ~name:"labelKind" + ~descr:(Md.plain "Marker kind (short)") + ~data:(module Jalpha) + ~get:(fun (tag,_) -> descr ~short:true tag) + model + + let () = + States.column + ~name:"titleKind" + ~descr:(Md.plain "Marker kind (long)") + ~data:(module Jalpha) + ~get:(fun (tag,_) -> descr ~short:false tag) + model + + let () = + States.column + ~name:"name" + ~descr:(Md.plain "Marker short name") + ~data:(module Jalpha) + ~get:(fun (tag, _) -> Printer_tag.label tag) + model + + let () = + States.column + ~name:"descr" + ~descr:(Md.plain "Marker declaration or description") + ~data:(module Jstring) + ~get:(fun (tag, _) -> Rich_text.to_string Printer_tag.pretty tag) + model + + let () = + States.column + ~name:"isLval" + ~descr:(Md.plain "Whether it is an l-value") + ~data:(module Jbool) + ~get:(fun (tag, _) -> Lval.mem tag) + model + let () = + States.column + ~name:"isFunDecl" + ~descr:(Md.plain "Whether it is a function declaration or definition") + ~data:(module Jbool) + ~get:(fun (tag, _) -> is_fundecl tag) + model + + let () = + States.column + ~name:"isFun" + ~descr:(Md.plain "Whether it is a function symbol") + ~data:(module Jbool) + ~get:(fun (tag, _) -> is_kf tag) + model + + let () = + States.option + ~name:"scope" + ~descr:(Md.plain "Function scope of the marker, if applicable") + ~data:(module Jstring) + ~get:(fun (tag, _) -> scope tag) + model + + let () = + States.column + ~name:"sloc" + ~descr:(Md.plain "Source location") + ~data:(module Position) + ~get:(fun (tag, _) -> fst (Printer_tag.loc_of_localizable tag)) + model + + let array = States.register_array + ~package + ~name:"markerAttributes" + ~descr:(Md.plain "Marker attributes") + ~key:snd ~keyType:Jstring + ~iter:Marker.iter + ~add_reload_hook:ast_update_hook + model + + let () = Marker.hook (States.update array) + +end + (* -------------------------------------------------------------------------- *) (* --- Functions --- *) (* -------------------------------------------------------------------------- *) @@ -805,68 +791,74 @@ let () = set_files (* -------------------------------------------------------------------------- *) -(* ----- Build a marker from an ACSL term ----------------------------------- *) +(* --- Build a marker from an ACSL term --- *) (* -------------------------------------------------------------------------- *) -type marker_term_input = { atStmt : stmt ; term : string } - -module MarkerTermInput = struct - type record - let record : record Record.signature = Record.signature () - - let atStmt = - let descr = "The statement at which we will build the marker." in - Record.field record ~name:"atStmt" ~descr:(Markdown.plain descr) - (module Marker) - - let term = - let descr = "The ACSL term." in - Record.field record ~name:"term" ~descr:(Markdown.plain descr) - (module Data.Jstring) - - let data = - Record.publish record ~package ~name:"markerFromTermInput" - ~descr:(Markdown.plain "<markerFromTerm> input") - - module R : Record.S with type r = record = (val data) - type t = marker_term_input option - let jtype = R.jtype - - let of_json js = - let record = R.of_json js in - match R.get atStmt record with - | PStmt (_, s) | PStmtStart (_, s) - | PLval (_, Kstmt s, _) | PExp (_, Kstmt s, _) - | PTermLval (_, Kstmt s, _, _) - | PVDecl (_, Kstmt s, _) -> - let term = R.get term record in - Some { atStmt = s ; term } - | _ -> None - -end - -module MarkerTermOutput = Data.Joption (Marker) - -let build_marker input = - let env = - let open Logic_typing in - Lenv.empty () |> append_pre_label |> append_init_label |> append_here_label - in - try - let kf = Kernel_function.find_englobing_kf input.atStmt in - let term = Logic_parse_string.term ~env kf input.term in - let exp = Logic_to_c.term_to_exp term in - Some (Printer_tag.PExp (Some kf, Kstmt input.atStmt, exp)) - with Not_found - | Logic_parse_string.Error _ - | Logic_parse_string.Unbound _ - | Logic_to_c.No_conversion -> None (* TODO: return an error message. *) - -let descr = "Build a marker from an ACSL term." +let logic_environment () = + let open Logic_typing in + Lenv.empty () |> append_pre_label |> append_init_label |> append_here_label + +module StmtTerm = Datatype.Pair(Cil_datatype.Stmt)(Cil_datatype.Term) +module Cache = Hashtbl.Make(StmtTerm) + +let cache : Printer_tag.localizable Cache.t = Cache.create 0 + +let reparse_term kf stmt text = + let env = logic_environment () in + try Some (stmt,Logic_parse_string.term ~env kf text) + with Logic_parse_string.Error _ | Parsing.Parse_error -> None + +let parseable_marker = + let open Printer_tag in + function + | PStmt _ | PStmtStart _ | PTermLval _ | PVDecl _ | PGlobal _ | PIP _ + | PLval (None, _, _) | PExp (None, _, _) + | PLval (_, Kglobal, _) | PExp (_, Kglobal, _) + | PType _ + -> None + | PLval (Some kf, Kstmt stmt, lval) -> + reparse_term kf stmt @@ Format.asprintf "%a" Cil_datatype.Lval.pretty lval + | PExp (Some kf, Kstmt stmt, exp) -> + reparse_term kf stmt @@ Format.asprintf "%a" Cil_datatype.Exp.pretty exp + +(* pre-fill the cache with known markers *) +let () = + Marker.hook + begin fun (loc,_) -> + match parseable_marker loc with + | None -> () + | Some entry -> Cache.add cache entry loc + end -let () = Request.register ~package - ~kind:`GET ~name:"markerFromTerm" ~descr:(Markdown.plain descr) - ~input:(module MarkerTermInput) ~output:(module MarkerTermOutput) - (fun input -> Option.bind input build_marker) +(* request to parse a new marker from ACSL term *) +let () = + let module Md = Markdown in + let s = Request.signature ~output:(module Data.Joption(Marker)) () in + let get_marker = Request.param s ~name:"stmt" + ~descr:(Md.plain "Marker position from where to localize the term") + (module Marker) in + let get_term = Request.param s ~name:"term" + ~descr:(Md.plain "ACSL term to parse") + (module Data.Jstring) in + Request.register_sig ~package s + ~kind:`GET ~name:"parseTerm" + ~descr:(Md.plain "Parse an ACSL Term and returns the associated marker") + begin fun rq () -> + match Printer_tag.ki_of_localizable @@ get_marker rq with + | Kglobal -> None + | Kstmt stmt -> + let kf = Kernel_function.find_englobing_kf stmt in + let env = logic_environment () in + let term = Logic_parse_string.term ~env kf @@ get_term rq in + let entry = (stmt, term) in + try Some (Cache.find cache entry) with Not_found -> + let tag = + match Logic_to_c.term_to_exp term with + | { enode = Lval lv } -> + Printer_tag.PLval(Some kf, Kstmt stmt, lv) + | exp -> + Printer_tag.PExp(Some kf, Kstmt stmt, exp) + in Cache.add cache entry tag ; Some tag + end -(**************************************************************************) +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/server/kernel_ast.mli b/src/plugins/server/kernel_ast.mli index 938b968119c..50702ec1922 100644 --- a/src/plugins/server/kernel_ast.mli +++ b/src/plugins/server/kernel_ast.mli @@ -24,37 +24,28 @@ (** Ast Data *) (* -------------------------------------------------------------------------- *) -open Package open Cil_types -module Position : Data.S with type t = Filepath.position - -module Kf : Data.S with type t = kernel_function -module Fundec : Data.S with type t = fundec -module Ki : Data.S with type t = kinstr -module Stmt : Data.S with type t = stmt -module Lval : Data.S with type t = kinstr * lval module Marker : sig include Data.S with type t = Printer_tag.localizable - val jstmt : jtype - val jdecl : jtype - val jlval : jtype - val jexpr : jtype - val jterm : jtype - val jglobal : jtype - val jproperty : jtype - val jtyp : jtype - - val create : t -> string + val tag : t -> string (** Memoized unique identifier. *) - val lookup : string -> t - (** Get back the localizable, if any. *) + val find : string -> t + (** Get back the localizable, if any. + @raises Not_found if marker is not defined yet *) + end +module Position : Data.S with type t = Filepath.position +module Kf : Data.S with type t = kernel_function +module Fundec : Data.S with type t = fundec +module Ki : Data.S with type t = kinstr +module Stmt : Data.S with type t = stmt +module Lval : Data.S with type t = kinstr * lval module KfMarker : Data.S with type t = kernel_function * Printer_tag.localizable (* -------------------------------------------------------------------------- *) diff --git a/src/plugins/server/kernel_properties.ml b/src/plugins/server/kernel_properties.ml index a130f3d5634..7d54d4144eb 100644 --- a/src/plugins/server/kernel_properties.ml +++ b/src/plugins/server/kernel_properties.ml @@ -338,8 +338,8 @@ let array = ~package ~name:"status" ~descr:(Md.plain "Status of Registered Properties") - ~key:(fun ip -> Kernel_ast.Marker.create (PIP ip)) - ~keyType:Kernel_ast.Marker.jproperty + ~key:(fun ip -> Kernel_ast.Marker.tag (PIP ip)) + ~keyType:Kernel_ast.Marker.jtype ~iter ~add_update_hook ~add_remove_hook -- GitLab