import { useMemo } from 'react';
import { useDebugPointer } from '../../api/queries/debug';
import { ApiError } from '../../api/client';
import { EmptyState } from '../../components/ui/EmptyState';
import { ErrorState } from '../../components/ui/ErrorState';
import { LoadingState } from '../../components/ui/LoadingState';
import type {
PointerLocationView,
PointerValueView,
PointerFieldEntryView,
} from '../../api/types';
interface PointerAnalysisPanelProps {
file: string;
functionName: string;
}
export function PointerAnalysisPanel({
file,
functionName,
}: PointerAnalysisPanelProps) {
const { data, isLoading, error } = useDebugPointer(file, functionName);
if (isLoading) {
return ;
}
if (error) {
if (error instanceof ApiError && error.status === 404) {
return (
);
}
return ;
}
if (
!data ||
(data.values.length === 0 &&
data.field_reads.length === 0 &&
data.field_writes.length === 0)
) {
return (
);
}
return (
Per-Value Points-To
{data.values.length} value
{data.values.length === 1 ? '' : 's'} · {data.location_count}{' '}
location
{data.location_count === 1 ? '' : 's'}
{data.values.length === 0 ? (
All SSA values point to nothing tracked.
) : (
)}
{data.field_reads.length > 0 && (
)}
{data.field_writes.length > 0 && (
)}
);
}
function PointerValueTable({
values,
locations,
}: {
values: PointerValueView[];
locations: PointerLocationView[];
}) {
const locById = useMemo(() => {
const map = new Map();
for (const loc of locations) map.set(loc.id, loc);
return map;
}, [locations]);
return (
| Value |
Name |
Points-To |
{values.map((v) => (
| v{v.ssa_value} |
{v.var_name ?? '-'} |
{v.is_top ? (
⊤ (top)
) : (
v.points_to.map((id) => (
))
)}
|
))}
);
}
function LocationChip({ loc }: { loc?: PointerLocationView }) {
if (!loc) {
return (
?
);
}
const className =
loc.kind === 'Top'
? 'cap-badge cap-badge-sink'
: loc.kind === 'Field'
? 'cap-badge cap-badge-sanitizer'
: 'cap-badge cap-badge-source';
return (
{loc.display}
);
}
function FieldEntriesBlock({
title,
entries,
emptyHint,
}: {
title: string;
entries: PointerFieldEntryView[];
emptyHint: string;
}) {
return (
{title}
{entries.length} entr{entries.length === 1 ? 'y' : 'ies'}
{entries.length === 0 ? (
{emptyHint}
) : (
| Target |
Field |
{entries.map((e, i) => (
|
{e.param_index === null ? 'self' : `param[${e.param_index}]`}
|
{e.field} |
))}
)}
);
}