Author: Antonio Cuni <anto.c...@gmail.com>
Branch: hpy
Changeset: r98536:ea75324675cd
Date: 2020-01-14 19:01 +0100
http://bitbucket.org/pypy/pypy/changeset/ea75324675cd/

Log:    introduce W_CPyStaticData to manage the lifetime of pymethods.
        Previously, the array was freed as soon as we exited the
        scoped_alloc(), so we ended up with W_PyCFunction referencing dead
        memory :(

diff --git a/pypy/module/hpy_universal/interp_cpy_compat.py 
b/pypy/module/hpy_universal/interp_cpy_compat.py
--- a/pypy/module/hpy_universal/interp_cpy_compat.py
+++ b/pypy/module/hpy_universal/interp_cpy_compat.py
@@ -1,6 +1,10 @@
 from rpython.rtyper.lltypesystem import lltype, rffi
 from rpython.rlib.rarithmetic import widen
+from rpython.rlib import rgc
 from pypy.interpreter.error import OperationError, oefmt
+from pypy.interpreter.baseobjspace import W_Root
+from pypy.interpreter.gateway import interp2app
+from pypy.interpreter.typedef import TypeDef
 from pypy.module.cpyext import pyobject
 from pypy.module.cpyext.methodobject import PyMethodDef, PyCFunction
 from pypy.module.cpyext.modsupport import convert_method_defs
@@ -30,21 +34,52 @@
     # convert hpymethods into a C array of PyMethodDef
     dict_w = {}
     n = len(hpymethods)
-    with lltype.scoped_alloc(PyMethodDefP.TO, n+1) as pymethods:
-        for i in range(n):
-            src = hpymethods[i] # HPyMethodDef
-            dst = pymethods[i]  # PyMethodDef
-            dst.c_ml_name = src.c_ml_name
-            dst.c_ml_doc = src.c_ml_doc
-            # for legacy methods, ml_meth contains a PyCFunction which can be
-            # called using the old C-API/cpyext calling convention
-            dst.c_ml_meth = rffi.cast(PyCFunction, src.c_ml_meth)
-            rffi.setintfield(dst, 'c_ml_flags', widen(src.c_ml_flags) & 
~llapi._HPy_METH)
-        pymethods[n].c_ml_name = lltype.nullptr(rffi.CONST_CCHARP.TO)
-        #
-        # convert_method_defs expects a PyMethodDef*, not a PyMethodDef[]
-        p_pymethods = rffi.cast(lltype.Ptr(PyMethodDef), pymethods)
-        convert_method_defs(space, dict_w, p_pymethods, None, w_mod, modname)
+    pymethods = lltype.malloc(PyMethodDefP.TO, n+1, zero=True, flavor='raw')
+    for i in range(n):
+        src = hpymethods[i] # HPyMethodDef
+        dst = pymethods[i]  # PyMethodDef
+        dst.c_ml_name = src.c_ml_name
+        dst.c_ml_doc = src.c_ml_doc
+        # for legacy methods, ml_meth contains a PyCFunction which can be
+        # called using the old C-API/cpyext calling convention
+        dst.c_ml_meth = rffi.cast(PyCFunction, src.c_ml_meth)
+        rffi.setintfield(dst, 'c_ml_flags', widen(src.c_ml_flags) & 
~llapi._HPy_METH)
+    pymethods[n].c_ml_name = lltype.nullptr(rffi.CONST_CCHARP.TO)
+    #
+    # convert_method_defs expects a PyMethodDef*, not a PyMethodDef[]
+    p_pymethods = rffi.cast(lltype.Ptr(PyMethodDef), pymethods)
+    convert_method_defs(space, dict_w, p_pymethods, None, w_mod, modname)
 
     for key, w_func in dict_w.items():
         space.setattr(w_mod, space.newtext(key), w_func)
+
+    # transfer the ownership of pymethods to W_CPyStaticData
+    w_static_data = W_CPyStaticData(space, pymethods)
+    space.setattr(w_mod, space.newtext("__cpy_static_data__"), w_static_data)
+
+
+class W_CPyStaticData(W_Root):
+    """
+    An object which owns the various C data structures which we need to create
+    for compatibility with cpyext.
+    """
+
+    def __init__(self, space, pymethods):
+        self.space = space
+        self.pymethods = pymethods # PyMethodDef[]
+
+    @rgc.must_be_light_finalizer
+    def __del__(self):
+        lltype.free(self.pymethods, flavor='raw')
+        self.pymethods = None
+
+    def repr(self):
+        return self.space.newtext("<CpyStaticData>")
+
+W_CPyStaticData.typedef = TypeDef(
+    'hpy_universal.CPyStaticData',
+    __module__ = 'hpy_universal',
+    __name__ = '<CPyStaticData>',
+    __repr__ = interp2app(W_CPyStaticData.repr),
+    )
+W_CPyStaticData.typedef.acceptable_as_base_class = False
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to