(Python) override modifier for cdef methods
authorRobert Bradshaw <robertwb@math.washington.edu>
Tue, 2 Oct 2007 07:39:41 +0000 (00:39 -0700)
committerRobert Bradshaw <robertwb@math.washington.edu>
Tue, 2 Oct 2007 07:39:41 +0000 (00:39 -0700)
Cython/Compiler/ExprNodes.py
Cython/Compiler/Nodes.py
Cython/Compiler/Parsing.py

index 2050ca6ae000b11cef6b0705cb73be2587094310..7d9d2d1a2956ff8e08faca7424f91c98e655d89c 100644 (file)
@@ -1033,6 +1033,9 @@ class TempNode(AtomicExprNode):
         if type.is_pyobject:
             self.result_ctype = py_object_type
         self.is_temp = 1
+        
+    def analyse_types(self, env):
+        return self.type
     
     def generate_result_code(self, code):
         pass
@@ -1686,11 +1689,10 @@ class AttributeNode(ExprNode):
                     error(self.pos, "Illegal use of special attribute __weakref__")
                 # methods need the normal attribute lookup
                 # because they do not have struct entries
-                if not entry.is_method:
-                    if entry.is_variable or entry.is_cmethod:
-                        self.type = entry.type
-                        self.member = entry.cname
-                        return
+                if entry.is_variable or entry.is_cmethod:
+                    self.type = entry.type
+                    self.member = entry.cname
+                    return
                 else:
                     # If it's not a variable or C method, it must be a Python
                     # method of an extension type, so we treat it like a Python
index 51c53e5eeba583632edc1f73a912f8a0041789f7..cffd1673243404831431d9cfecce484de671e532 100644 (file)
@@ -534,6 +534,8 @@ class FuncDefNode(StatNode, BlockNode):
     #  #filename        string        C name of filename string const
     #  entry           Symtab.Entry
     
+    py_func = None
+    
     def analyse_expressions(self, env):
         pass
                 
@@ -559,6 +561,10 @@ class FuncDefNode(StatNode, BlockNode):
         self.generate_const_definitions(lenv, code)
         # ----- Function header
         code.putln("")
+        if self.py_func:
+            self.py_func.generate_function_header(code, 
+                with_pymethdef = env.is_py_class_scope,
+                proto_only=True)
         self.generate_function_header(code,
             with_pymethdef = env.is_py_class_scope)
         # ----- Local variable declarations
@@ -639,7 +645,11 @@ class FuncDefNode(StatNode, BlockNode):
             #          retval_code)
             code.putln("return %s;" % retval_code)
         code.putln("}")
-    
+        # ----- Python version
+        if self.py_func:
+            self.py_func.generate_function_definitions(env, code)
+
+        
     def put_stararg_decrefs(self, code):
         pass
 
@@ -671,7 +681,7 @@ class FuncDefNode(StatNode, BlockNode):
 class CFuncDefNode(FuncDefNode):
     #  C function definition.
     #
-    #  modifiers     'inline ' or ''
+    #  modifiers     'inline ' or 'visible' or 'overrideable'
     #  visibility    'private' or 'public' or 'extern'
     #  base_type     CBaseTypeNode
     #  declarator    CDeclaratorNode
@@ -700,7 +710,34 @@ class CFuncDefNode(FuncDefNode):
             cname = cname, visibility = self.visibility,
             defining = self.body is not None)
         self.return_type = type.return_type
-    
+
+        if 'overrideable' in self.modifiers or 'visible' in self.modifiers:
+            if 'visible' in self.modifiers:
+                self.modifiers.remove('visible')
+            import ExprNodes
+            arg_names = [arg.name for arg in self.type.args]
+            self_arg = ExprNodes.NameNode(self.pos, name=arg_names[0])
+            cfunc = ExprNodes.AttributeNode(self.pos, obj=self_arg, attribute=self.declarator.base.name)
+            c_call = ExprNodes.SimpleCallNode(self.pos, function=cfunc, args=[ExprNodes.NameNode(self.pos, name=n) for n in arg_names[1:]])
+            py_func_body = ReturnStatNode(pos=self.pos, return_type=PyrexTypes.py_object_type, value=c_call)
+            self.py_func = DefNode(pos = self.pos, 
+                                   name = self.declarator.base.name,
+                                   args = self.declarator.args,
+                                   star_arg = None,
+                                   starstar_arg = None,
+                                   doc = None, # self.doc,
+                                   body = py_func_body)
+            self.py_func.analyse_declarations(env)
+            # Reset scope entry the above cfunction
+            env.entries[name] = self.entry
+            if Options.intern_names:
+                self.py_func.interned_attr_cname = env.intern(self.py_func.entry.name)
+            if 'overrideable' in self.modifiers:
+                self.modifiers.remove('overrideable')
+                self.override = OverrideCheckNode(self.pos, py_func = self.py_func)
+                self.body.stats.insert(0, self.override)
+            
+                    
     def declare_arguments(self, env):
         for arg in self.type.args:
             if not arg.name:
@@ -732,7 +769,7 @@ class CFuncDefNode(FuncDefNode):
             storage_class = ""
         code.putln("%s%s%s {" % (
             storage_class,
-            self.modifiers, 
+            ' '.join(self.modifiers).upper(), # macro forms 
             header))
 
     def generate_argument_declarations(self, env, code):
@@ -792,7 +829,7 @@ class CFuncDefNode(FuncDefNode):
             
     def caller_will_check_exceptions(self):
         return self.entry.type.exception_check
-
+                    
 
 class PyArgDeclNode(Node):
     # Argument which must be a Python object (used
@@ -923,7 +960,7 @@ class DefNode(FuncDefNode):
         else:
             self.entry.doc = self.doc
         self.entry.func_cname = \
-            Naming.func_prefix + env.scope_prefix + self.name
+            Naming.func_prefix + "py_" + env.scope_prefix + self.name
         self.entry.doc_cname = \
             Naming.funcdoc_prefix + env.scope_prefix + self.name
         self.entry.pymethdef_cname = \
@@ -989,7 +1026,7 @@ class DefNode(FuncDefNode):
         self.assmt.analyse_declarations(env)
         self.assmt.analyse_expressions(env)
             
-    def generate_function_header(self, code, with_pymethdef):
+    def generate_function_header(self, code, with_pymethdef, proto_only=0):
         arg_code_list = []
         sig = self.entry.signature
         if sig.has_dummy_arg:
@@ -1012,6 +1049,8 @@ class DefNode(FuncDefNode):
         dc = self.return_type.declaration_code(self.entry.func_cname)
         header = "static %s(%s)" % (dc, arg_code)
         code.putln("%s; /*proto*/" % header)
+        if proto_only:
+            return
         if self.entry.doc:
             code.putln(
                 'static char %s[] = "%s";' % (
@@ -1272,6 +1311,50 @@ class DefNode(FuncDefNode):
     def caller_will_check_exceptions(self):
         return 1
             
+class OverrideCheckNode(StatNode):
+    # A Node for dispatching to the def method if it
+    # is overriden. 
+    #
+    #  py_func
+    #
+    #  args
+    #  func_temp
+    #  body
+    
+    def analyse_expressions(self, env):
+        self.args = env.arg_entries
+        import ExprNodes
+        self.func_node = ExprNodes.PyTempNode(self.pos, env)
+        call_tuple = ExprNodes.TupleNode(self.pos, args=[ExprNodes.NameNode(self.pos, name=arg.name) for arg in self.args[1:]])
+        call_node = ExprNodes.SimpleCallNode(self.pos,
+                                             function=self.func_node, 
+                                             args=[ExprNodes.NameNode(self.pos, name=arg.name) for arg in self.args[1:]])
+        self.body = ReturnStatNode(self.pos, value=call_node)
+#        self.func_temp = env.allocate_temp_pyobject()
+        self.body.analyse_expressions(env)
+#        env.release_temp(self.func_temp)
+        
+    def generate_execution_code(self, code):
+        # Check to see if we are an extension type
+        self_arg = "((PyObject *)%s)" % self.args[0].cname
+        code.putln("/* Check if overriden in Python */")
+        code.putln("if (unlikely(%s->ob_type->tp_dictoffset != 0)) {" % self_arg)
+        err = code.error_goto_if_null(self_arg, self.pos)
+        # need to get attribute manually--scope would return cdef method
+        if Options.intern_names:
+            code.putln("%s = PyObject_GetAttr(%s, %s); %s" % (self.func_node.result_code, self_arg, self.py_func.interned_attr_cname, err))
+        else:
+            code.putln('%s = PyObject_GetAttrString(%s, "%s"); %s' % (self.func_node.result_code, self_arg, self.py_func.entry.name, err))
+        # It appears that this type is not anywhere exposed in the Python/C API
+        is_builtin_function_or_method = '(strcmp(%s->ob_type->tp_name, "builtin_function_or_method") == 0)' % self.func_node.result_code
+        is_overridden = '(PyCFunction_GET_FUNCTION(%s) != &%s)' % (self.func_node.result_code, self.py_func.entry.func_cname)
+        code.putln('if (!%s || %s) {' % (is_builtin_function_or_method, is_overridden))
+        self.body.generate_execution_code(code)
+        code.putln('}')
+#        code.put_decref(self.func_temp, PyrexTypes.py_object_type)
+        code.putln("}")
+
+
 
 class PyClassDefNode(StatNode, BlockNode):
     #  A Python class definition.
index ba91760bdeefd8b1193d19071da5f6575cc87d9b..111f7a91b99c339a63e4afdd97460b0945ac9ecd 100644 (file)
@@ -1691,11 +1691,11 @@ def p_visibility(s, prev_visibility):
     return visibility
     
 def p_c_modifiers(s):
-    if s.systring in ('inline', ):
-        modifier = s.systring.upper() # uppercase is macro defined for various compilers
+    if s.sy == 'IDENT' and s.systring in ('inline', 'visible', 'overrideable'):
+        modifier = s.systring
         s.next()
-        return modifier + ' ' + p_c_modifiers(s)
-    return ""
+        return [modifier] + p_c_modifiers(s)
+    return []
 
 def p_c_func_or_var_declaration(s, level, pos, visibility = 'private'):
     cmethod_flag = level in ('c_class', 'c_class_pxd')