Author: Antonio Cuni <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit