diff --git a/pybind/AE.cpp b/pybind/AE.cpp index 68bc809..d79d181 100644 --- a/pybind/AE.cpp +++ b/pybind/AE.cpp @@ -31,6 +31,11 @@ using namespace SVF; void bind_abstract_state(py::module& m) { + // Virtual-memory address constants from AddressValue.h, exposed so Python + // code can build the null / black-hole address without hard-coding literals. + m.attr("NullMemAddr") = (uint32_t)(NullMemAddr); + m.attr("BlackHoleObjAddr") = (uint32_t)(BlackHoleObjAddr); + py::class_(m, "BoundedInt") .def(py::init([](int64_t val) { return new BoundedInt(val); diff --git a/pybind/Graphs.cpp b/pybind/Graphs.cpp index 03aa54f..1834f69 100644 --- a/pybind/Graphs.cpp +++ b/pybind/Graphs.cpp @@ -103,6 +103,7 @@ void bind_icfg_node(py::module& m) { .def("addActualParms", &CallICFGNode::addActualParms, "Add an actual parameter to the call") .def("isVarArg", &CallICFGNode::isVarArg, "Check if the call is a vararg call") .def("isVirtualCall", &CallICFGNode::isVirtualCall, "Check if the call is a virtual call") + .def("arg_size", &CallICFGNode::arg_size, "Get the number of actual arguments at the call site") .def("getRetICFGNode", &CallICFGNode::getRetICFGNode, py::return_value_policy::reference, "Get the return node"); // === RetICFGNode === @@ -220,6 +221,12 @@ void bind_icfg_graph(py::module& m) { return nodes; }, py::return_value_policy::reference) + // getICFGEdge(src, dst, kind): 0=IntraCF, 1=CallCF, 2=RetCF. Returns None if absent. + .def("getICFGEdge", [](ICFG& icfg, const ICFGNode* src, const ICFGNode* dst, int kind) -> ICFGEdge* { + return icfg.getICFGEdge(src, dst, static_cast(kind)); + }, py::arg("src"), py::arg("dst"), py::arg("kind") = 0, py::return_value_policy::reference, + "Get the ICFG edge between src and dst of the given kind (0=IntraCF,1=CallCF,2=RetCF); None if absent") + // getGNode(id) .def("getGNode", [](ICFG& icfg, NodeID id) -> ICFGNode* { ICFGNode* node = icfg.getGNode(id); @@ -404,7 +411,13 @@ void bind_callgraph(py::module& m) { }, py::arg("id"), py::return_value_policy::reference, "Check if a node is in a cycle") .def("repNode", [](Andersen::CallGraphSCC& cg, NodeID id) { return cg.repNode(id); - }, py::arg("id"), "Get the representative node ID of the SCC containing the given node"); + }, py::arg("id"), "Get the representative node ID of the SCC containing the given node") + .def("subNodes", [](Andersen::CallGraphSCC& cg, NodeID rep) { + std::vector out; + for (NodeID n : cg.subNodes(rep)) + out.push_back(n); + return out; + }, py::arg("rep"), "Get the call-graph node IDs in the SCC represented by 'rep'"); } void bind_thread_call_graph(py::module &m) { diff --git a/pysvf/__init__.py b/pysvf/__init__.py index c9515e7..1d093d0 100644 --- a/pysvf/__init__.py +++ b/pysvf/__init__.py @@ -233,6 +233,8 @@ def main(): AbstractValue, BoundedInt, Options, + NullMemAddr, + BlackHoleObjAddr, MTA, MHP, LockAnalysis, diff --git a/pysvf/pysvf.pyi b/pysvf/pysvf.pyi index f53a804..f05d238 100644 --- a/pysvf/pysvf.pyi +++ b/pysvf/pysvf.pyi @@ -10,6 +10,10 @@ LLVM_DIR: str EXTAPI_BC_PATH: str Z3_DIR: str +# Virtual-memory address constants (from AddressValue.h). +NullMemAddr: int +BlackHoleObjAddr: int + def run_tool(tool_name: str, args: List[str]) -> None: ... SVFFunction = Any @@ -197,6 +201,9 @@ class CallGraphSCC: def repNode(self, id: int) -> int: ... """Get the representative node ID of the SCC containing the given node""" + def subNodes(self, rep: int) -> List[int]: ... + """Get the call-graph node IDs in the SCC represented by 'rep'""" + class FunEntryICFGNode(ICFGNode): def __init__(self, *args, **kwargs) -> None: ... """Not intended for direct instantiation.""" @@ -236,6 +243,9 @@ class CallICFGNode(ICFGNode): def getArgument(self, idx: int) -> "SVFVar": ... """Get the argument at the given index""" + def arg_size(self) -> int: ... + """Get the number of actual arguments at the call site""" + def isVarArg(self) -> bool: ... """Check if the call is a vararg call""" @@ -645,6 +655,9 @@ class ICFG: def getNodes(self) -> List[ICFGNode]: ... """Get the nodes of the ICFG""" + def getICFGEdge(self, src: ICFGNode, dst: ICFGNode, kind: int = ...) -> Optional["ICFGEdge"]: ... + """Get the ICFG edge between src and dst of the given kind (0=IntraCF,1=CallCF,2=RetCF); None if absent""" + def getGNode(self, id: int) -> ICFGNode: ... """Get the ICFG node with the given ID"""