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