diff --git a/ivette/Makefile b/ivette/Makefile index 29010215b7bddf4c90c1036b10c4c090a1818e0f..a30dcad4c5f3265efb03fb9bdf283e93d0243a8f 100644 --- a/ivette/Makefile +++ b/ivette/Makefile @@ -35,7 +35,7 @@ tsc: dome-pkg dome-templ api: @echo "[Ivette] Generating TypeScript API" - @find api -name "*.ts" -exec rm -f {} \; + @rm -fr api/generated ../bin/frama-c.byte -load-module api/server_tsc.ml -server-tsc @find api -name "*.ts" -exec chmod a-w {} \; diff --git a/ivette/api/generated/kernel/ast/index.ts b/ivette/api/generated/kernel/ast/index.ts index 2a910fb94ae5a99b0f515c71fca9aff3bfa3da03..2bd3772caab604fb8d9559f6cd3b77ded324dc11 100644 --- a/ivette/api/generated/kernel/ast/index.ts +++ b/ivette/api/generated/kernel/ast/index.ts @@ -120,7 +120,7 @@ export const markerVarTags: Server.GetRequest<null,tag[]>= markerVarTags_interna /** Data for array rows [`markerInfo`](#markerinfo) */ export interface markerInfoData { /** Entry identifier. */ - key: Json.key<'#markerInfo'>; + key: string; /** Marker kind */ kind: markerKind; /** Marker variable */ @@ -136,8 +136,7 @@ export interface markerInfoData { /** Loose decoder for `markerInfoData` */ export const jMarkerInfoData: Json.Loose<markerInfoData> = Json.jObject({ - key: Json.jFail(Json.jKey<'#markerInfo'>('#markerInfo'), - '#markerInfo expected'), + key: Json.jFail(Json.jString,'String expected'), kind: jMarkerKindSafe, var: jMarkerVarSafe, name: Json.jFail(Json.jString,'String expected'), @@ -152,8 +151,8 @@ export const jMarkerInfoDataSafe: Json.Safe<markerInfoData> = /** Natural order for `markerInfoData` */ export const byMarkerInfoData: Compare.Order<markerInfoData> = Compare.byFields - <{ key: Json.key<'#markerInfo'>, kind: markerKind, var: markerVar, - name: string, descr: string, sloc: source }>({ + <{ key: string, kind: markerKind, var: markerVar, name: string, + descr: string, sloc: source }>({ key: Compare.string, kind: byMarkerKind, var: byMarkerVar, @@ -178,8 +177,8 @@ export const reloadMarkerInfo: Server.GetRequest<null,null>= reloadMarkerInfo_in const fetchMarkerInfo_internal: Server.GetRequest< number, - { pending: number, updated: markerInfoData[], - removed: Json.key<'#markerInfo'>[], reload: boolean } + { pending: number, updated: markerInfoData[], removed: string[], + reload: boolean } > = { kind: Server.RqKind.GET, name: 'kernel.ast.fetchMarkerInfo', @@ -187,21 +186,18 @@ const fetchMarkerInfo_internal: Server.GetRequest< output: Json.jObject({ pending: Json.jFail(Json.jNumber,'Number expected'), updated: Json.jList(jMarkerInfoData), - removed: Json.jList(Json.jKey<'#markerInfo'>('#markerInfo')), + removed: Json.jList(Json.jString), reload: Json.jFail(Json.jBoolean,'Boolean expected'), }), }; /** Data fetcher for array [`markerInfo`](#markerinfo) */ export const fetchMarkerInfo: Server.GetRequest< number, - { pending: number, updated: markerInfoData[], - removed: Json.key<'#markerInfo'>[], reload: boolean } + { pending: number, updated: markerInfoData[], removed: string[], + reload: boolean } >= fetchMarkerInfo_internal; -const markerInfo_internal: State.Array< - Json.key<'#markerInfo'>, - markerInfoData - > = { +const markerInfo_internal: State.Array<string,markerInfoData> = { name: 'kernel.ast.markerInfo', getkey: ((d:markerInfoData) => d.key), signal: signalMarkerInfo, @@ -210,7 +206,7 @@ const markerInfo_internal: State.Array< order: byMarkerInfoData, }; /** Marker informations */ -export const markerInfo: State.Array<Json.key<'#markerInfo'>,markerInfoData> = markerInfo_internal; +export const markerInfo: State.Array<string,markerInfoData> = markerInfo_internal; /** Localizable AST markers */ export type marker = diff --git a/ivette/api/generated/kernel/properties/index.ts b/ivette/api/generated/kernel/properties/index.ts index 7ca5ce68b252ecb5308fe822caec7a05ef680773..dcba1484d490140e593cacd56ab49052929aed4e 100644 --- a/ivette/api/generated/kernel/properties/index.ts +++ b/ivette/api/generated/kernel/properties/index.ts @@ -225,7 +225,7 @@ export const alarmsTags: Server.GetRequest<null,tag[]>= alarmsTags_internal; /** Data for array rows [`status`](#status) */ export interface statusData { /** Entry identifier. */ - key: Json.key<'#status'>; + key: Json.key<'#property'>; /** Full description */ descr: string; /** Kind */ @@ -235,7 +235,7 @@ export interface statusData { /** Status */ status: propStatus; /** Function */ - function?: Json.key<'#fct'>; + fct?: Json.key<'#fct'>; /** Instruction */ kinstr?: Json.key<'#stmt'>; /** Position */ @@ -251,12 +251,12 @@ export interface statusData { /** Loose decoder for `statusData` */ export const jStatusData: Json.Loose<statusData> = Json.jObject({ - key: Json.jFail(Json.jKey<'#status'>('#status'),'#status expected'), + key: Json.jFail(Json.jKey<'#property'>('#property'),'#property expected'), descr: Json.jFail(Json.jString,'String expected'), kind: jPropKindSafe, names: Json.jList(Json.jString), status: jPropStatusSafe, - function: Json.jKey<'#fct'>('#fct'), + fct: Json.jKey<'#fct'>('#fct'), kinstr: Json.jKey<'#stmt'>('#stmt'), source: jSourceSafe, alarm: Json.jString, @@ -271,8 +271,8 @@ export const jStatusDataSafe: Json.Safe<statusData> = /** Natural order for `statusData` */ export const byStatusData: Compare.Order<statusData> = Compare.byFields - <{ key: Json.key<'#status'>, descr: string, kind: propKind, - names: string[], status: propStatus, function?: Json.key<'#fct'>, + <{ 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, @@ -280,7 +280,7 @@ export const byStatusData: Compare.Order<statusData> = kind: byPropKind, names: Compare.array(Compare.string), status: byPropStatus, - function: Compare.defined(Compare.string), + fct: Compare.defined(Compare.string), kinstr: Compare.defined(Compare.string), source: bySource, alarm: Compare.defined(Compare.string), @@ -304,7 +304,7 @@ export const reloadStatus: Server.GetRequest<null,null>= reloadStatus_internal; const fetchStatus_internal: Server.GetRequest< number, - { pending: number, updated: statusData[], removed: Json.key<'#status'>[], + { pending: number, updated: statusData[], removed: Json.key<'#property'>[], reload: boolean } > = { kind: Server.RqKind.GET, @@ -313,18 +313,18 @@ const fetchStatus_internal: Server.GetRequest< output: Json.jObject({ pending: Json.jFail(Json.jNumber,'Number expected'), updated: Json.jList(jStatusData), - removed: Json.jList(Json.jKey<'#status'>('#status')), + removed: Json.jList(Json.jKey<'#property'>('#property')), reload: Json.jFail(Json.jBoolean,'Boolean expected'), }), }; /** Data fetcher for array [`status`](#status) */ export const fetchStatus: Server.GetRequest< number, - { pending: number, updated: statusData[], removed: Json.key<'#status'>[], + { pending: number, updated: statusData[], removed: Json.key<'#property'>[], reload: boolean } >= fetchStatus_internal; -const status_internal: State.Array<Json.key<'#status'>,statusData> = { +const status_internal: State.Array<Json.key<'#property'>,statusData> = { name: 'kernel.properties.status', getkey: ((d:statusData) => d.key), signal: signalStatus, @@ -333,6 +333,6 @@ const status_internal: State.Array<Json.key<'#status'>,statusData> = { order: byStatusData, }; /** Status of Registered Properties */ -export const status: State.Array<Json.key<'#status'>,statusData> = status_internal; +export const status: State.Array<Json.key<'#property'>,statusData> = status_internal; /* ------------------------------------- */ diff --git a/ivette/api/server_tsc.ml b/ivette/api/server_tsc.ml index d190a3b3e19a78816c2a97fe4adef1be20f3a98b..669d83168c2cd58d3536c88a00742bbf9864f187 100644 --- a/ivette/api/server_tsc.ml +++ b/ivette/api/server_tsc.ml @@ -375,10 +375,9 @@ let makeDeclaration fmt names d = self.name jtype js self.name; - | D_array { arr_key ; arr_kind } -> + | D_array { arr_key ; arr_kind = jkey } -> let data = Pkg.Derived.data self in - let jkey = (Pkg.Jkey arr_kind) in - let jrow = (Pkg.Jdata data) in + let jrow = Pkg.Jdata data in Format.fprintf fmt "@[<hv 2>const %s_internal: State.Array<@,%a,@,%a@,>@] = {@\n" self.name jtype jkey jtype jrow ; diff --git a/ivette/src/frama-c/eva/Values.tsx b/ivette/src/frama-c/eva/Values.tsx index 12de5e02e76db03216962de1d270a7f92761e6b9..798b64bd19ccc4ff763122d15402684ba8c0d508 100644 --- a/ivette/src/frama-c/eva/Values.tsx +++ b/ivette/src/frama-c/eva/Values.tsx @@ -218,7 +218,7 @@ function StackInfo() { focused === stmt && 'eva-focused', ); const onClick = () => { - const location = { function: caller, marker: stmt }; + const location = { fct: caller, marker: stmt }; setSelection({ location }); }; return ( @@ -361,7 +361,7 @@ function TableCell(props: TableCellProps) { ); const onClick = () => { if (probe) { - const location = { function: probe.fct, marker: probe.marker }; + const location = { fct: probe.fct, marker: probe.marker }; setSelection({ location }); model.setSelectedRow(row); } @@ -497,7 +497,7 @@ function ValuesPanel(props: ValuesPanelProps) { const [selection] = States.useSelection(); React.useEffect(() => { const curr = selection?.current; - const fct = curr?.function; + const fct = curr?.fct; const marker = Ast.jMarker(curr?.marker); model.setLayout({ zoom, margin, fct, marker }); }); diff --git a/ivette/src/frama-c/eva/stacks.ts b/ivette/src/frama-c/eva/stacks.ts index 3583661f6b2dedd9764732f0d249fa667eb380b9..aef6e64d511431dd886116f03a89996a1dfb1f30 100644 --- a/ivette/src/frama-c/eva/stacks.ts +++ b/ivette/src/frama-c/eva/stacks.ts @@ -16,7 +16,7 @@ export type callstacks = Values.callstack[]; export interface Callsite { callee: string; caller?: string; - stmt?: string; + stmt?: Ast.marker; rank?: number; } diff --git a/ivette/src/frama-c/states.ts b/ivette/src/frama-c/states.ts index 3895ee4978f75af68bed38a3677e579510941174..377ab636464fc688519e4c6ef394a26f3bda3df9 100644 --- a/ivette/src/frama-c/states.ts +++ b/ivette/src/frama-c/states.ts @@ -15,6 +15,7 @@ import { Order } from 'dome/data/compare'; import { GlobalState, useGlobalState } from 'dome/data/states'; import { useModel } from 'dome/table/models'; import { CompactModel } from 'dome/table/arrays'; +import * as Ast from 'frama-c/api/kernel/ast'; import * as Server from './server'; const PROJECT = new Dome.Event('frama-c.project'); @@ -445,22 +446,15 @@ export function useSyncArray<K, A>( // --- Selection // -------------------------------------------------------------------------- -type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = - Partial<T> & U[keyof U]; - -export interface FullLocation { - /** Function name. */ - readonly function: string; - /** Marker identifier. */ - readonly marker: string; -} - /** An AST location. * * Properties [[function]] and [[marker]] are optional, * but at least one of the two must be set. */ -export type Location = AtLeastOne<FullLocation>; +export type Location = { + fct?: string; + marker?: Ast.marker; +}; export interface HistorySelection { /** Previous locations with respect to the [[current]] one. */ @@ -554,7 +548,7 @@ function isNthSelect(a: SelectionActions): a is NthSelect { /** Update selection to the given location. */ function selectLocation(s: Selection, location: Location): Selection { const [prevSelections, nextSelections] = - s.current && s.current.function !== location.function ? + s.current && s.current.fct !== location.fct ? [[s.current, ...s.history.prevSelections], []] : [s.history.prevSelections, s.history.nextSelections]; return { diff --git a/ivette/src/renderer/ASTinfo.tsx b/ivette/src/renderer/ASTinfo.tsx index be2aa17a99d70cb07f0ab23ac1a61174ac880922..4ad40c6cfb0f6f7a11ea12881388d5a2e20da908 100644 --- a/ivette/src/renderer/ASTinfo.tsx +++ b/ivette/src/renderer/ASTinfo.tsx @@ -33,7 +33,7 @@ const ASTinfo = () => { // Callbacks function onTextSelection(id: string) { // For now, the only markers are functions. - const location = { function: id }; + const location = { fct: id }; updateSelection({ location }); } diff --git a/ivette/src/renderer/ASTview.tsx b/ivette/src/renderer/ASTview.tsx index 8583af381634c5a90bebcf4e82ac79ff56515e73..c5f941f18664276e55883500a9ef0933affe02b0 100644 --- a/ivette/src/renderer/ASTview.tsx +++ b/ivette/src/renderer/ASTview.tsx @@ -9,11 +9,10 @@ import * as States from 'frama-c/states'; import * as Utils from 'frama-c/utils'; import * as Dome from 'dome'; -import * as Json from 'dome/data/json'; import { RichTextBuffer } from 'dome/text/buffers'; import { Text } from 'dome/text/editors'; import { Component, TitleBar } from 'frama-c/LabViews'; -import * as AST from 'frama-c/api/kernel/ast'; +import * as Ast from 'frama-c/api/kernel/ast'; import * as Properties from 'frama-c/api/kernel/properties'; import { getCallers, getDeadCode } from 'frama-c/api/plugins/eva/general'; import { getWritesLval, getReadsLval } from 'frama-c/api/plugins/studia/studia'; @@ -38,7 +37,7 @@ async function loadAST( buffer.log('// Loading', theFunction, '…'); (async () => { try { - const data = await Server.send(AST.printFunction, theFunction); + const data = await Server.send(Ast.printFunction, theFunction); buffer.clear(); if (!data) { buffer.log('// No code for function', theFunction); @@ -63,7 +62,7 @@ async function loadAST( async function functionCallers(functionName: string) { try { const data = await Server.send(getCallers, functionName); - const locations = data.map(([fct, marker]) => ({ function: fct, marker })); + const locations = data.map(([fct, marker]) => ({ fct, marker })); return locations; } catch (err) { D.error(`Fail to retrieve callers of function '${functionName}':`, err); @@ -79,12 +78,12 @@ type access = 'Reads' | 'Writes'; async function studia( marker: string, - info: AST.markerInfoData, + info: Ast.markerInfoData, kind: access, ) { const request = kind === 'Reads' ? getReadsLval : getWritesLval; const data = await Server.send(request, marker); - const locations = data.direct.map(([f, m]) => ({ function: f, marker: m })); + const locations = data.direct.map(([f, m]) => ({ fct: f, marker: m })); const lval = info.name; if (locations.length > 0) { const name = `${kind} of ${lval}`; @@ -139,7 +138,7 @@ const ASTview = () => { const printed = React.useRef<string | undefined>(); const [selection, updateSelection] = States.useSelection(); const multipleSelections = selection?.multiple.allSelections; - const theFunction = selection?.current?.function; + const theFunction = selection?.current?.fct; const theMarker = selection?.current?.marker; const { buttons: themeButtons, theme, fontSize, wrapText } = Preferences.useThemeButtons({ @@ -150,7 +149,7 @@ const ASTview = () => { disabled: !theFunction, }); - const markersInfo = States.useSyncArray(AST.markerInfo); + const markersInfo = States.useSyncArray(Ast.markerInfo); const deadCode = States.useRequest(getDeadCode, theFunction); const propertyStatus = States.useSyncArray(Properties.status).getArray(); const statusDict = States.useTags(Properties.propStatusTags); @@ -158,7 +157,7 @@ const ASTview = () => { const setBullets = React.useCallback(() => { if (theFunction) { propertyStatus.forEach((prop) => { - if (prop.function === theFunction) { + if (prop.fct === theFunction) { const status = statusDict.get(prop.status); if (status) { const bullet = makeBullet(status); @@ -206,26 +205,26 @@ const ASTview = () => { if (theMarker) buffer.scroll(theMarker); }, [buffer, theMarker]); - function onTextSelection(id: string) { + function onSelection(markerId: string) { if (selection.current) { - const location = { ...selection.current, marker: id }; + const { fct } = selection.current; + const location: States.Location = { fct, marker: Ast.jMarker(markerId) }; updateSelection({ location }); } } - async function onContextMenu(id: string) { + async function onContextMenu(markerId: string) { const items = []; - const markerId = (id as Json.key<'#markerInfo'>); const selectedMarkerInfo = markersInfo.getData(markerId); if (selectedMarkerInfo?.var === 'function') { if (selectedMarkerInfo.kind === 'declaration') { const name = selectedMarkerInfo?.name; if (name) { const locations = await functionCallers(name); - const locationsByFunction = _.groupBy(locations, (e) => e.function); + const locationsByFunction = _.groupBy(locations, (e) => e.fct); _.forEach(locationsByFunction, (e) => { - const callerName = e[0].function; + const callerName = e[0].fct; items.push({ label: `Go to caller ${callerName} ` + @@ -233,7 +232,7 @@ const ASTview = () => { onClick: () => updateSelection({ name: `Call sites of function ${name}`, locations, - index: locations.findIndex((l) => l.function === callerName), + index: locations.findIndex((l) => l.fct === callerName), }), }); }); @@ -242,7 +241,7 @@ const ASTview = () => { items.push({ label: `Go to definition of ${selectedMarkerInfo.name}`, onClick: () => { - const location = { function: selectedMarkerInfo.name }; + const location = { fct: selectedMarkerInfo.name }; updateSelection({ location }); }, }); @@ -252,7 +251,11 @@ const ASTview = () => { || selectedMarkerInfo?.var === 'variable'; function onClick(kind: access) { if (selectedMarkerInfo) - studia(markerId, selectedMarkerInfo, kind).then(updateSelection); + studia( + markerId, + selectedMarkerInfo, + kind, + ).then(updateSelection); } items.push({ label: 'Studia: select writes', @@ -281,7 +284,7 @@ const ASTview = () => { fontSize={fontSize} lineWrapping={wrapText} selection={theMarker} - onSelection={onTextSelection} + onSelection={onSelection} onContextMenu={onContextMenu} gutters={['bullet']} readOnly diff --git a/ivette/src/renderer/Globals.tsx b/ivette/src/renderer/Globals.tsx index 27d21bc47181ee8f530e98abf2c209324240992b..0a4d754b4fdcfb12cd00740af485edd0ae92275e 100644 --- a/ivette/src/renderer/Globals.tsx +++ b/ivette/src/renderer/Globals.tsx @@ -23,7 +23,7 @@ const makeHint = (fct: functionsData): GlobalHint => ({ id: fct.key, label: fct.name, title: fct.signature, - value: { function: fct.name }, + value: { fct: fct.name }, }); export function useHints(): [GlobalHint[], (pattern: string) => void] { @@ -101,12 +101,12 @@ export default () => { function isSelected(fct: functionsData) { return multipleSelection?.allSelections.some( - (l) => fct.name === l?.function + (l) => fct.name === l?.fct ); } // Currently selected function. - const current: undefined | string = selection?.current?.function; + const current: undefined | string = selection?.current?.fct; function showFunction(fct: functionsData) { const visible = @@ -119,7 +119,7 @@ export default () => { } function onSelection(name: string) { - updateSelection({ location: { function: name } }); + updateSelection({ location: { fct: name } }); } async function onContextMenu() { diff --git a/ivette/src/renderer/Properties.tsx b/ivette/src/renderer/Properties.tsx index 89737980d9c4855cf01eed52d767aef811f418df..9189a9263631ba5fac55ce5b5ff1b47227e6d4f1 100644 --- a/ivette/src/renderer/Properties.tsx +++ b/ivette/src/renderer/Properties.tsx @@ -235,7 +235,7 @@ const byStatus = const byProperty: Compare.ByFields<Property> = { status: byStatus, - function: Compare.defined(Compare.alpha), + fct: Compare.defined(Compare.alpha), source: bySource, kind: Compare.structural, alarm: Compare.defined(Compare.alpha), @@ -270,7 +270,7 @@ class PropertyModel extends Arrays.CompactModel<Json.key<'#status'>, Property> { } filterItem(prop: Property) { - const kf = prop.function; + const kf = prop.fct; const cf = this.filterFun; const filteringFun = cf && filter('currentFunction'); const filterFunction = filteringFun ? kf === cf : true; @@ -477,15 +477,14 @@ const RenderTable = () => { model.reload(); }, [model, data]); - const [selection, updateSelection] = - States.useSelection(); + const [selection, updateSelection] = States.useSelection(); const [showFilter, flipFilter] = Dome.useFlipSettings('ivette.properties.showFilter'); // Updating the filter Dome.useEvent(Reload, model.reload); - const selectedFunction = selection?.current?.function; + const selectedFunction = selection?.current?.fct; React.useEffect(() => { model.setFilterFunction(selectedFunction); }, [model, selectedFunction]); @@ -493,8 +492,8 @@ const RenderTable = () => { // Callbacks const onPropertySelection = React.useCallback( - ({ key: propKey, function: fct }: Property) => { - const location = { function: fct, marker: propKey }; + ({ key: marker, fct }: Property) => { + const location = { fct, marker }; updateSelection({ location }); }, [updateSelection], ); diff --git a/ivette/src/renderer/SourceCode.tsx b/ivette/src/renderer/SourceCode.tsx index 0c9f8a7dfa42d7a626938d1fe10f5d24016c76a8..aa1ca31d7984a27de13b2a886f9231e5afe1158b 100644 --- a/ivette/src/renderer/SourceCode.tsx +++ b/ivette/src/renderer/SourceCode.tsx @@ -7,7 +7,6 @@ import * as States from 'frama-c/states'; import * as Dome from 'dome'; import { readFile } from 'dome/system'; -import * as Json from 'dome/data/json'; import { RichTextBuffer } from 'dome/text/buffers'; import { Text } from 'dome/text/editors'; import { Component, TitleBar } from 'frama-c/LabViews'; @@ -37,7 +36,7 @@ const SourceCode = () => { // Hooks const buffer = React.useMemo(() => new RichTextBuffer(), []); const [selection] = States.useSelection(); - const theFunction = selection?.current?.function; + const theFunction = selection?.current?.fct; const theMarker = selection?.current?.marker; const { buttons: themeButtons, theme, fontSize, wrapText } = Preferences.useThemeButtons({ @@ -72,10 +71,8 @@ const SourceCode = () => { } // Actual source code loading upon function or marker update. const sloc = - /* Non-empty [selection] has defined either marker or function: we give - precedence to marker as it provides more precise source location. */ - (theMarker && - markersInfo.getData(theMarker as Json.key<'#markerInfo'>)?.sloc) + /* markers have more precise source location */ + (theMarker && markersInfo.getData(theMarker)?.sloc) ?? (theFunction && functionsData.find((e) => e.name === theFunction)?.sloc); if (sloc) { diff --git a/src/plugins/server/kernel_ast.ml b/src/plugins/server/kernel_ast.ml index a8e380889208883e6e45eff039c4180a26dd898c..b707805c9980c3ac1b8d6e653b9158c3933282ab 100644 --- a/src/plugins/server/kernel_ast.ml +++ b/src/plugins/server/kernel_ast.ml @@ -206,7 +206,7 @@ struct ~package ~name:"markerInfo" ~descr:(Md.plain "Marker informations") - ~key:snd + ~key:snd ~keyType:Jstring ~iter model let create_tag = function diff --git a/src/plugins/server/kernel_properties.ml b/src/plugins/server/kernel_properties.ml index 511c5299a0e354b86bef51f466c5d02b060e0eef..4f09c05dab339a6a5f2f94916f04dd81cdedea4f 100644 --- a/src/plugins/server/kernel_properties.ml +++ b/src/plugins/server/kernel_properties.ml @@ -281,7 +281,7 @@ let () = States.column model ~name:"status" ~data:(module PropStatus) ~get:(Property_status.Feedback.get) -let () = States.column model ~name:"function" +let () = States.column model ~name:"fct" ~descr:(Md.plain "Function") ~data:(module Joption(Kf)) ~get:Property.get_kf @@ -330,6 +330,7 @@ let array = ~name:"status" ~descr:(Md.plain "Status of Registered Properties") ~key:(fun ip -> Kernel_ast.Marker.create (PIP ip)) + ~keyType:Kernel_ast.Marker.jproperty ~iter ~add_update_hook ~add_remove_hook diff --git a/src/plugins/server/package.ml b/src/plugins/server/package.ml index 309cae632565703288b5cc95ca1a5793215cfba5..bb87b1da3d58d915060b0455fa705ae855e78eba 100644 --- a/src/plugins/server/package.ml +++ b/src/plugins/server/package.ml @@ -208,7 +208,7 @@ type requestInfo = { type arrayInfo = { arr_key: string; - arr_kind: string; + arr_kind: jtype; } type declKindInfo = diff --git a/src/plugins/server/package.mli b/src/plugins/server/package.mli index 952f0dd05659e32932bce624cb57c19fcc2e8741..30075f6243c419aca7d1e4e98ba70462c1704d1d 100644 --- a/src/plugins/server/package.mli +++ b/src/plugins/server/package.mli @@ -72,7 +72,7 @@ type requestInfo = { type arrayInfo = { arr_key: string; - arr_kind: string; + arr_kind: jtype; } type declKindInfo = diff --git a/src/plugins/server/states.ml b/src/plugins/server/states.ml index f9ae51a5b8acb166d074e7b2dc88b1624e90c1b1..52a682f2bc7b854e86c8435201fa231ebcc3c4e6 100644 --- a/src/plugins/server/states.ml +++ b/src/plugins/server/states.ml @@ -293,9 +293,14 @@ let fetch array n = (* --- Signature Registry --- *) (* -------------------------------------------------------------------------- *) +let rec is_keyType = function + | Package.Junion js -> List.for_all is_keyType js + | Jstring | Jalpha | Jkey _ | Jtag _ -> true + | _ -> false + let register_array ~package ~name ~descr ~key ?(keyName="key") - ?(keyKind=name) + ?(keyType=Package.Jkey name) ~(iter : 'a callback) ?(add_update_hook : 'a callback option) ?(add_remove_hook : 'a callback option) @@ -304,15 +309,24 @@ let register_array ~package ~name ~descr ~key let open Markdown in let href = link ~name () in let columns = List.rev !model in - if List.exists (fun (fd,_) -> fd.Package.fd_name = keyName) columns then - raise (Invalid_argument "States.array: key name overrides column name") ; + begin + if List.exists (fun (fd,_) -> fd.Package.fd_name = keyName) columns then + raise (Invalid_argument ( + Printf.sprintf "States.array(%S) : invalid key %S" + name keyName + )); + if not (is_keyType keyType) then + raise (Invalid_argument ( + Printf.sprintf "States.array(%S): invalid key type" name + )); + end ; let fields = Package.{ fd_name = keyName ; - fd_type = Jkey keyKind ; + fd_type = keyType ; fd_descr = plain "Entry identifier." ; } :: List.map fst columns in let id = Package.declare_id ~package:package ~name:name ~descr - (D_array { arr_key = keyName ; arr_kind = keyKind }) in + (D_array { arr_key = keyName ; arr_kind = keyType }) in let signal = Request.signal ~package ~name:(Package.Derived.signal id).name ~descr:(plain "Signal for array" @ href) in @@ -331,7 +345,7 @@ let register_array ~package ~name ~descr ~key let signature = Request.signature ~input:(module Jint) () in let module Jkeys = Jlist(struct include Jstring - let jtype = Package.Jkey keyKind + let jtype = keyType end) in let module Jrows = Jlist (struct include Jany diff --git a/src/plugins/server/states.mli b/src/plugins/server/states.mli index a75c6c76c8776b067e39b4544c66198d3424fe89..1f38038f83ee1d2b4ea80a1fb1704119e9bf9021 100644 --- a/src/plugins/server/states.mli +++ b/src/plugins/server/states.mli @@ -136,7 +136,7 @@ val register_array : descr:Markdown.text -> key:('a -> string) -> ?keyName:string -> - ?keyKind:string -> + ?keyType:jtype -> iter:('a callback) -> ?add_update_hook:('a callback) -> ?add_remove_hook:('a callback) ->