diff --git a/pybind/AE.cpp b/pybind/AE.cpp index 5b23f33..68bc809 100644 --- a/pybind/AE.cpp +++ b/pybind/AE.cpp @@ -11,6 +11,20 @@ #include "AE/Svfexe/AbstractInterpretation.h" #include +// Tell pybind11 that AbstractInterpretation is non-copyable and non-movable. +// The class owns a std::vector>, so its implicit +// copy/move ctors are ill-formed — but std::is_(copy|move)_constructible +// reports true until the body is instantiated, which it isn't unless we +// expose getAEInstance. Without these specialisations pybind11 instantiates +// make_(copy|move)_constructor in type_caster_base whenever a binding returns +// AbstractInterpretation* / AbstractInterpretation&, and the static_assert +// inside fires. +namespace pybind11 { namespace detail { +template <> +struct is_copy_constructible : std::false_type {}; +template <> +struct is_move_constructible : std::false_type {}; +}} namespace py = pybind11; using namespace SVF; @@ -684,10 +698,20 @@ void bind_abstract_state(py::module& m) { // (now-removed) AbstractStateManager: loadValue, storeValue, GEP // helpers, def/use-site queries, and direct trace access. // --------------------------------------------------------------- - py::class_(m, "AbstractInterpretation") - // No constructor / static factory bound here: the class has a - // protected ctor and is non-copyable. Users receive instances via - // other bound entry points. + // Module-level shim for AbstractInterpretation::getAEInstance. Exposing + // it via .def_static directly didn't work even with the trait + // specialisations above (pybind11's def_static path takes a different + // SFINAE route), so we bind a free function here and attach it as a + // staticmethod on the class from Python (pysvf/__init__.py). + m.def("_AbstractInterpretation_getAEInstance", []() -> AbstractInterpretation* { + return &AbstractInterpretation::getAEInstance(); + }, py::return_value_policy::reference); + + py::class_>(m, "AbstractInterpretation") + // getAEInstance is attached as a staticmethod from pysvf/__init__.py + // via the module-level shim above. The py::nodelete holder keeps + // pybind from trying to free the SVF-owned singleton. // State access (replaces old AbstractStateManager::getAbstractState etc.). .def("getAbsState", diff --git a/pysvf/__init__.py b/pysvf/__init__.py index 902b035..c9515e7 100644 --- a/pysvf/__init__.py +++ b/pysvf/__init__.py @@ -243,6 +243,12 @@ def main(): from .pysvf import AndersenWaveDiff_WPA as AndersenWaveDiff_WPA from .pysvf import Steensgaard_WPA as Steensgaard_WPA +# AbstractInterpretation.getAEInstance is bound at module level (see comment +# in pybind/AE.cpp); attach it as a staticmethod here so users can call +# pysvf.AbstractInterpretation.getAEInstance() the same way the C++ class does. +from .pysvf import _AbstractInterpretation_getAEInstance as _ae_instance_shim +AbstractInterpretation.getAEInstance = staticmethod(_ae_instance_shim) + # argument can be a string or a list of strings def buildSVFModule(args) -> None: if isinstance(args, str): diff --git a/pysvf/pysvf.pyi b/pysvf/pysvf.pyi index fb40d22..9b50ed9 100644 --- a/pysvf/pysvf.pyi +++ b/pysvf/pysvf.pyi @@ -2003,6 +2003,9 @@ class AbstractInterpretation: AbstractStateManager (folded back into AbstractInterpretation upstream when the AbstractStateManager.h header was removed).""" + @staticmethod + def getAEInstance() -> 'AbstractInterpretation': ... + # State access (replaces old getAbstractState / updateAbstractState). def getAbsState(self, node: 'ICFGNode') -> AbstractState: ... def updateAbsState(self, node: 'ICFGNode', state: AbstractState) -> None: ...