Commit 395cc677 authored by Stefan Behnel's avatar Stefan Behnel

Generate short wrappers for special methods like "__next__()" to adapt their...

Generate short wrappers for special methods like "__next__()" to adapt their signature for the PyCFunction entry in PyMethodDef.
Previously, their cast to a two-argument PyCFunction was incorrect.
See #2363.
parent 259da7c9
...@@ -2179,7 +2179,7 @@ class CCodeWriter(object): ...@@ -2179,7 +2179,7 @@ class CCodeWriter(object):
if entry.in_closure: if entry.in_closure:
self.put_giveref('Py_None') self.put_giveref('Py_None')
def put_pymethoddef(self, entry, term, allow_skip=True): def put_pymethoddef(self, entry, term, allow_skip=True, wrapper_code_writer=None):
if entry.is_special or entry.name == '__getattribute__': if entry.is_special or entry.name == '__getattribute__':
if entry.name not in special_py_methods: if entry.name not in special_py_methods:
if entry.name == '__getattr__' and not self.globalstate.directives['fast_getattr']: if entry.name == '__getattr__' and not self.globalstate.directives['fast_getattr']:
...@@ -2189,29 +2189,38 @@ class CCodeWriter(object): ...@@ -2189,29 +2189,38 @@ class CCodeWriter(object):
# that's better than ours. # that's better than ours.
elif allow_skip: elif allow_skip:
return return
from .TypeSlots import method_coexist
if entry.doc:
doc_code = entry.doc_cname
else:
doc_code = 0
method_flags = entry.signature.method_flags() method_flags = entry.signature.method_flags()
if method_flags: if not method_flags:
if entry.is_special: return
method_flags += [method_coexist] if entry.is_special:
func_ptr = entry.func_cname from . import TypeSlots
# Add required casts, but try not to shadow real warnings. method_flags += [TypeSlots.method_coexist]
cast = '__Pyx_PyCFunctionFast' if 'METH_FASTCALL' in method_flags else 'PyCFunction' func_ptr = wrapper_code_writer.put_pymethoddef_wrapper(entry) if wrapper_code_writer else entry.func_cname
if 'METH_KEYWORDS' in method_flags: # Add required casts, but try not to shadow real warnings.
cast += 'WithKeywords' cast = '__Pyx_PyCFunctionFast' if 'METH_FASTCALL' in method_flags else 'PyCFunction'
if cast != 'PyCFunction': if 'METH_KEYWORDS' in method_flags:
func_ptr = '(void*)(%s)%s' % (cast, func_ptr) cast += 'WithKeywords'
self.putln( if cast != 'PyCFunction':
'{"%s", (PyCFunction)%s, %s, %s}%s' % ( func_ptr = '(void*)(%s)%s' % (cast, func_ptr)
entry.name, self.putln(
func_ptr, '{"%s", (PyCFunction)%s, %s, %s}%s' % (
"|".join(method_flags), entry.name,
doc_code, func_ptr,
term)) "|".join(method_flags),
entry.doc_cname if entry.doc else '0',
term))
def put_pymethoddef_wrapper(self, entry):
func_cname = entry.func_cname
if entry.is_special:
method_flags = entry.signature.method_flags()
if method_flags and 'METH_NOARGS' in method_flags:
# Special NOARGS methods really take no arguments besides 'self', but PyCFunction expects one.
func_cname = Naming.method_wrapper_prefix + func_cname
self.putln("static PyObject *%s(PyObject *self, CYTHON_UNUSED PyObject *arg) {return %s(self);}" % (
func_cname, entry.func_cname))
return func_cname
# GIL methods # GIL methods
......
...@@ -2144,18 +2144,24 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -2144,18 +2144,24 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if env.is_c_class_scope and not env.pyfunc_entries: if env.is_c_class_scope and not env.pyfunc_entries:
return return
binding = env.directives['binding'] binding = env.directives['binding']
code.putln("") code.putln("")
wrapper_code_writer = code.insertion_point()
code.putln( code.putln(
"static PyMethodDef %s[] = {" % ( "static PyMethodDef %s[] = {" % (
env.method_table_cname)) env.method_table_cname))
for entry in env.pyfunc_entries: for entry in env.pyfunc_entries:
if not entry.fused_cfunction and not (binding and entry.is_overridable): if not entry.fused_cfunction and not (binding and entry.is_overridable):
code.put_pymethoddef(entry, ",") code.put_pymethoddef(entry, ",", wrapper_code_writer=wrapper_code_writer)
code.putln( code.putln(
"{0, 0, 0, 0}") "{0, 0, 0, 0}")
code.putln( code.putln(
"};") "};")
if wrapper_code_writer.getvalue():
wrapper_code_writer.putln("")
def generate_dict_getter_function(self, scope, code): def generate_dict_getter_function(self, scope, code):
dict_attr = scope.lookup_here("__dict__") dict_attr = scope.lookup_here("__dict__")
if not dict_attr or not dict_attr.is_variable: if not dict_attr or not dict_attr.is_variable:
......
...@@ -28,6 +28,7 @@ const_prefix = pyrex_prefix + "k_" ...@@ -28,6 +28,7 @@ const_prefix = pyrex_prefix + "k_"
py_const_prefix = pyrex_prefix + "kp_" py_const_prefix = pyrex_prefix + "kp_"
label_prefix = pyrex_prefix + "L" label_prefix = pyrex_prefix + "L"
pymethdef_prefix = pyrex_prefix + "mdef_" pymethdef_prefix = pyrex_prefix + "mdef_"
method_wrapper_prefix = pyrex_prefix + "specialmethod_"
methtab_prefix = pyrex_prefix + "methods_" methtab_prefix = pyrex_prefix + "methods_"
memtab_prefix = pyrex_prefix + "members_" memtab_prefix = pyrex_prefix + "members_"
objstruct_prefix = pyrex_prefix + "obj_" objstruct_prefix = pyrex_prefix + "obj_"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment