cython-devel

changeset 2203:4495287d2fdb

NoneCheckNode to enforce runtime None checks for object references
author Stefan Behnel <scoder@users.berlios.de>
date Sun Mar 29 20:55:51 2009 +0200 (2 years ago)
parents 0afd7738bd5e
children 91be9458f343
files Cython/Compiler/ExprNodes.py Cython/Compiler/Optimize.py
line diff
1.1 --- a/Cython/Compiler/ExprNodes.py Sun Mar 29 20:44:11 2009 +0200 1.2 +++ b/Cython/Compiler/ExprNodes.py Sun Mar 29 20:55:51 2009 +0200 1.3 @@ -5091,7 +5091,44 @@ 1.4 1.5 def free_temps(self, code): 1.6 self.arg.free_temps(code) 1.7 - 1.8 + 1.9 + 1.10 +class NoneCheckNode(CoercionNode): 1.11 + # This node is used to check that a Python object is not None and 1.12 + # raises an appropriate exception (as specified by the creating 1.13 + # transform). 1.14 + 1.15 + def __init__(self, arg, exception_type_cname, exception_message): 1.16 + CoercionNode.__init__(self, arg) 1.17 + self.type = arg.type 1.18 + self.result_ctype = arg.ctype() 1.19 + self.exception_type_cname = exception_type_cname 1.20 + self.exception_message = exception_message 1.21 + 1.22 + def analyse_types(self, env): 1.23 + pass 1.24 + 1.25 + def result_in_temp(self): 1.26 + return self.arg.result_in_temp() 1.27 + 1.28 + def calculate_result_code(self): 1.29 + return self.arg.result() 1.30 + 1.31 + def generate_result_code(self, code): 1.32 + code.putln( 1.33 + "if (unlikely(%s == Py_None)) {" % self.arg.result()) 1.34 + code.putln('PyErr_SetString(%s, "%s"); %s ' % ( 1.35 + self.exception_type_cname, 1.36 + StringEncoding.escape_byte_string(self.exception_message), 1.37 + code.error_goto(self.pos))) 1.38 + code.putln("}") 1.39 + 1.40 + def generate_post_assignment_code(self, code): 1.41 + self.arg.generate_post_assignment_code(code) 1.42 + 1.43 + def free_temps(self, code): 1.44 + self.arg.free_temps(code) 1.45 + 1.46 1.47 class CoerceToPyTypeNode(CoercionNode): 1.48 # This node is used to convert a C data type
2.1 --- a/Cython/Compiler/Optimize.py Sun Mar 29 20:44:11 2009 +0200 2.2 +++ b/Cython/Compiler/Optimize.py Sun Mar 29 20:55:51 2009 +0200 2.3 @@ -476,6 +476,7 @@ 2.4 arg_list = arg_tuple.args 2.5 self_arg = function.obj 2.6 obj_type = self_arg.type 2.7 + is_unbound_method = False 2.8 if obj_type.is_builtin_type: 2.9 if obj_type is Builtin.type_type and arg_list and \ 2.10 arg_list[0].type.is_pyobject: 2.11 @@ -483,6 +484,7 @@ 2.12 # (ignoring 'type.mro()' here ...) 2.13 type_name = function.obj.name 2.14 self_arg = None 2.15 + is_unbound_method = True 2.16 else: 2.17 type_name = obj_type.name 2.18 else: 2.19 @@ -494,9 +496,9 @@ 2.20 if self_arg is not None: 2.21 arg_list = [self_arg] + list(arg_list) 2.22 if kwargs: 2.23 - return method_handler(node, arg_list, kwargs) 2.24 + return method_handler(node, arg_list, kwargs, is_unbound_method) 2.25 else: 2.26 - return method_handler(node, arg_list) 2.27 + return method_handler(node, arg_list, is_unbound_method) 2.28 else: 2.29 return node 2.30 2.31 @@ -625,7 +627,7 @@ 2.32 PyrexTypes.CFuncTypeArg("item", PyrexTypes.py_object_type, None), 2.33 ]) 2.34 2.35 - def _handle_simple_method_object_append(self, node, args): 2.36 + def _handle_simple_method_object_append(self, node, args, is_unbound_method): 2.37 # X.append() is almost always referring to a list 2.38 if len(args) != 2: 2.39 return node 2.40 @@ -644,13 +646,14 @@ 2.41 ], 2.42 exception_value = "-1") 2.43 2.44 - def _handle_simple_method_list_append(self, node, args): 2.45 + def _handle_simple_method_list_append(self, node, args, is_unbound_method): 2.46 if len(args) != 2: 2.47 error(node.pos, "list.append(x) called with wrong number of args, found %d" % 2.48 len(args)) 2.49 return node 2.50 return self._substitute_method_call( 2.51 - node, "PyList_Append", self.PyList_Append_func_type, args) 2.52 + node, "PyList_Append", self.PyList_Append_func_type, 2.53 + 'append', is_unbound_method, args) 2.54 2.55 single_param_func_type = PyrexTypes.CFuncType( 2.56 PyrexTypes.c_int_type, [ 2.57 @@ -658,21 +661,37 @@ 2.58 ], 2.59 exception_value = "-1") 2.60 2.61 - def _handle_simple_method_list_sort(self, node, args): 2.62 + def _handle_simple_method_list_sort(self, node, args, is_unbound_method): 2.63 if len(args) != 1: 2.64 return node 2.65 return self._substitute_method_call( 2.66 - node, "PyList_Sort", self.single_param_func_type, args) 2.67 + node, "PyList_Sort", self.single_param_func_type, 2.68 + 'sort', is_unbound_method, args) 2.69 2.70 - def _handle_simple_method_list_reverse(self, node, args): 2.71 + def _handle_simple_method_list_reverse(self, node, args, is_unbound_method): 2.72 if len(args) != 1: 2.73 error(node.pos, "list.reverse(x) called with wrong number of args, found %d" % 2.74 len(args)) 2.75 + return node 2.76 return self._substitute_method_call( 2.77 - node, "PyList_Reverse", self.single_param_func_type, args) 2.78 + node, "PyList_Reverse", self.single_param_func_type, 2.79 + 'reverse', is_unbound_method, args) 2.80 2.81 - def _substitute_method_call(self, node, name, func_type, args=()): 2.82 + def _substitute_method_call(self, node, name, func_type, 2.83 + attr_name, is_unbound_method, args=()): 2.84 args = list(args) 2.85 + if args: 2.86 + self_arg = args[0] 2.87 + if is_unbound_method: 2.88 + self_arg = ExprNodes.NoneCheckNode( 2.89 + self_arg, "PyExc_TypeError", 2.90 + "descriptor '%s' requires a '%s' object but received a 'NoneType'" % ( 2.91 + attr_name, node.function.obj.name)) 2.92 + else: 2.93 + self_arg = ExprNodes.NoneCheckNode( 2.94 + self_arg, "PyExc_AttributeError", 2.95 + "'NoneType' object has no attribute '%s'" % attr_name) 2.96 + args[0] = self_arg 2.97 # FIXME: args[0] may need a runtime None check (ticket #166) 2.98 return ExprNodes.PythonCapiCallNode( 2.99 node.pos, name, func_type,