From 6d66c50bd16a8788a50e26d114938e9a3a6ef8e6 Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Sun, 31 Jan 2010 01:42:42 -0800 Subject: [PATCH] Binding PyCFunction type. This is a bit hackish, but creating a wrapper type would be as well, but would also have a performance penalty. --- Cython/Compiler/ExprNodes.py | 73 ++++++++++++++++++++++++++++++++++- Cython/Compiler/ModuleNode.py | 4 ++ Cython/Compiler/Naming.py | 1 + Cython/Compiler/Nodes.py | 5 +++ 4 files changed, 81 insertions(+), 2 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index ec820511..cf3edfa7 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -4053,15 +4053,18 @@ class PyCFunctionNode(ExprNode): # # pymethdef_cname string PyMethodDef structure # self_object ExprNode or None + # binding bool subexprs = [] self_object = None + binding = False type = py_object_type is_temp = 1 def analyse_types(self, env): - pass + if self.binding: + env.use_utility_code(binding_cfunc_utility_code) gil_message = "Constructing Python function" @@ -4073,9 +4076,14 @@ class PyCFunctionNode(ExprNode): return self_result def generate_result_code(self, code): + if self.binding: + constructor = "%s_New" % Naming.binding_cfunc + else: + constructor = "PyCFunction_New" code.putln( - "%s = PyCFunction_New(&%s, %s); %s" % ( + "%s = %s(&%s, %s); %s" % ( self.result(), + constructor, self.pymethdef_cname, self.self_result_code(), code.error_goto_if_null(self.result(), self.pos))) @@ -4084,6 +4092,8 @@ class PyCFunctionNode(ExprNode): class InnerFunctionNode(PyCFunctionNode): # Special PyCFunctionNode that depends on a closure class # + binding = True + def self_result_code(self): return "((PyObject*)%s)" % Naming.cur_scope_cname @@ -6652,3 +6662,62 @@ proto=""" #define UNARY_NEG_WOULD_OVERFLOW(x) \ (((x) < 0) & ((unsigned long)(x) == 0-(unsigned long)(x))) """) + + +binding_cfunc_utility_code = UtilityCode( +proto=""" +#define %(binding_cfunc)s_USED 1 + +typedef struct { + PyCFunctionObject func; +} %(binding_cfunc)s_object; + +PyTypeObject %(binding_cfunc)s_type; +PyTypeObject *%(binding_cfunc)s = NULL; + +PyObject *%(binding_cfunc)s_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module); /* proto */ +#define %(binding_cfunc)s_New(ml, self) %(binding_cfunc)s_NewEx(ml, self, NULL) + +int %(binding_cfunc)s_init(void); /* proto */ +""" % Naming.__dict__, +impl=""" + +PyObject *%(binding_cfunc)s_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) { + %(binding_cfunc)s_object *op = PyObject_GC_New(%(binding_cfunc)s_object, %(binding_cfunc)s); + if (op == NULL) + return NULL; + op->func.m_ml = ml; + Py_XINCREF(self); + op->func.m_self = self; + Py_XINCREF(module); + op->func.m_module = module; + _PyObject_GC_TRACK(op); + return (PyObject *)op; +} + +static void %(binding_cfunc)s_dealloc(%(binding_cfunc)s_object *m) { + _PyObject_GC_UNTRACK(m); + Py_XDECREF(m->func.m_self); + Py_XDECREF(m->func.m_module); + PyObject_GC_Del(m); +} + +static PyObject *%(binding_cfunc)s_descr_get(PyObject *func, PyObject *obj, PyObject *type) { + if (obj == Py_None) + obj = NULL; + return PyMethod_New(func, obj, type); +} + +int %(binding_cfunc)s_init(void) { + %(binding_cfunc)s_type = PyCFunction_Type; + %(binding_cfunc)s_type.tp_name = "cython_binding_builtin_function_or_method"; + %(binding_cfunc)s_type.tp_dealloc = (destructor)%(binding_cfunc)s_dealloc; + %(binding_cfunc)s_type.tp_descr_get = %(binding_cfunc)s_descr_get; + if (PyType_Ready(&%(binding_cfunc)s_type) < 0) { + return -1; + } + %(binding_cfunc)s = &%(binding_cfunc)s_type; + return 0; + +} +""" % Naming.__dict__) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index a716d431..fd00e831 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -1674,6 +1674,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): code.putln("#else"); code.putln("%s = PyBytes_FromStringAndSize(\"\", 0); %s" % (Naming.empty_bytes, code.error_goto_if_null(Naming.empty_bytes, self.pos))); code.putln("#endif"); + + code.putln("#ifdef %s_USED" % Naming.binding_cfunc) + code.putln("if (%s_init() < 0) %s" % (Naming.binding_cfunc, code.error_goto(self.pos))) + code.putln("#endif") code.putln("/*--- Library function declarations ---*/") env.generate_library_function_declarations(code) diff --git a/Cython/Compiler/Naming.py b/Cython/Compiler/Naming.py index f9f710f1..2967ceba 100644 --- a/Cython/Compiler/Naming.py +++ b/Cython/Compiler/Naming.py @@ -89,6 +89,7 @@ cur_scope_cname = pyrex_prefix + "cur_scope" enc_scope_cname = pyrex_prefix + "enc_scope" frame_cname = pyrex_prefix + "frame" frame_code_cname = pyrex_prefix + "frame_code" +binding_cfunc = pyrex_prefix + "binding_PyCFunctionType" line_c_macro = "__LINE__" diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index a87d1af0..3c6b1644 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1635,6 +1635,7 @@ class DefNode(FuncDefNode): # name string the Python name of the function # lambda_name string the internal name of a lambda 'function' # decorators [DecoratorNode] list of decorators + # binding bool bind like a Python function # args [CArgDeclNode] formal arguments # star_arg PyArgDeclNode or None * argument # starstar_arg PyArgDeclNode or None ** argument @@ -1661,6 +1662,7 @@ class DefNode(FuncDefNode): entry = None acquire_gil = 0 self_in_stararg = 0 + binding = False def __init__(self, pos, **kwds): FuncDefNode.__init__(self, pos, **kwds) @@ -1973,6 +1975,9 @@ class DefNode(FuncDefNode): elif env.is_closure_scope: rhs = ExprNodes.InnerFunctionNode( self.pos, pymethdef_cname = self.entry.pymethdef_cname) + else: + rhs = ExprNodes.PyCFunctionNode( + self.pos, pymethdef_cname = self.entry.pymethdef_cname, binding = self.binding) self.assmt = SingleAssignmentNode(self.pos, lhs = ExprNodes.NameNode(self.pos, name = self.name), rhs = rhs) -- 2.26.2