Skip to content

Commit 5361418

Browse files
committed
feat(dataflow): identity map — IR node ids ↔ ordinal can:// ids
1 parent 207e6a6 commit 5361418

2 files changed

Lines changed: 54 additions & 0 deletions

File tree

codeanalyzer/dataflow/identity.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""Bijection between internal IR node ids (ints, per function) and canonical
2+
ordinal ids `<callable can:// id>@<tag>` — `@entry`/`@exit` for the synthetic
3+
CFG bookends, `@line:col` for real statements. Both emitters consume this so
4+
JSON body-node ids and Neo4j PyCFGNode keys are identical."""
5+
from __future__ import annotations
6+
from typing import Dict, Iterable
7+
8+
9+
class IdentityMap:
10+
def __init__(self, callable_id: str, id_to_ordinal: Dict[int, str]):
11+
self._callable_id = callable_id
12+
self._map = id_to_ordinal
13+
14+
@classmethod
15+
def for_function(cls, callable_id: str, pdg) -> "IdentityMap":
16+
cfg = pdg.cfg
17+
m: Dict[int, str] = {}
18+
for n in cfg.nodes:
19+
if n.id == cfg.entry_id:
20+
m[n.id] = f"{callable_id}@entry"
21+
elif n.id == cfg.exit_id:
22+
m[n.id] = f"{callable_id}@exit"
23+
else:
24+
m[n.id] = f"{callable_id}@{n.start_line}:{n.start_column}"
25+
return cls(callable_id, m)
26+
27+
def ordinal(self, node_id: int) -> str:
28+
return self._map[node_id]
29+
30+
def node_ids(self) -> Iterable[int]:
31+
return self._map.keys()

test/test_v2_l3.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from codeanalyzer.dataflow.identity import IdentityMap
2+
3+
class _Node:
4+
def __init__(self, id, start_line, start_column, kind):
5+
self.id, self.start_line, self.start_column, self.kind = id, start_line, start_column, kind
6+
7+
class _CFG:
8+
def __init__(self, nodes, entry_id, exit_id):
9+
self._n = {n.id: n for n in nodes}; self.nodes = nodes
10+
self.entry_id, self.exit_id = entry_id, exit_id
11+
def node_by_id(self, i): return self._n[i]
12+
13+
class _PDG:
14+
def __init__(self, cfg): self.cfg = cfg
15+
16+
def test_ordinal_ids_for_entry_exit_and_statements():
17+
nodes = [_Node(0, 1, 0, "entry"), _Node(1, 2, 4, "statement"), _Node(2, 3, 4, "exit")]
18+
pdg = _PDG(_CFG(nodes, entry_id=0, exit_id=2))
19+
im = IdentityMap.for_function("can://python/app/m.py/f()", pdg)
20+
assert im.ordinal(0) == "can://python/app/m.py/f()@entry"
21+
assert im.ordinal(1) == "can://python/app/m.py/f()@2:4"
22+
assert im.ordinal(2) == "can://python/app/m.py/f()@exit"
23+
assert set(im.node_ids()) == {0, 1, 2}

0 commit comments

Comments
 (0)