Commit 26634fc5 authored by Stefan Behnel's avatar Stefan Behnel

merged in changes from Cython 0.9.6.7

parents df831940 997ac5b6
...@@ -85,7 +85,8 @@ class CCodeWriter: ...@@ -85,7 +85,8 @@ class CCodeWriter:
try: try:
return self.input_file_contents[file] return self.input_file_contents[file]
except KeyError: except KeyError:
F = open(file).readlines() F = [line.replace('*/', '*[inserted by cython to avoid comment closer]/')
for line in open(file).readlines()]
self.input_file_contents[file] = F self.input_file_contents[file] = F
return F return F
......
...@@ -596,6 +596,22 @@ class CharNode(ConstNode): ...@@ -596,6 +596,22 @@ class CharNode(ConstNode):
class IntNode(ConstNode): class IntNode(ConstNode):
type = PyrexTypes.c_long_type type = PyrexTypes.c_long_type
def coerce_to(self, dst_type, env):
# Arrange for a Python version of the string to be pre-allocated
# when coercing to a Python type.
if dst_type.is_pyobject:
self.entry = env.get_py_num(self.value)
self.type = PyrexTypes.py_object_type
# We still need to perform normal coerce_to processing on the
# result, because we might be coercing to an extension type,
# in which case a type test node will be needed.
return ConstNode.coerce_to(self, dst_type, env)
def calculate_result_code(self):
if self.type.is_pyobject:
return self.entry.cname
else:
return str(self.value)
class FloatNode(ConstNode): class FloatNode(ConstNode):
type = PyrexTypes.c_double_type type = PyrexTypes.c_double_type
...@@ -814,8 +830,28 @@ class NameNode(AtomicExprNode): ...@@ -814,8 +830,28 @@ class NameNode(AtomicExprNode):
entry = self.entry entry = self.entry
if entry is None: if entry is None:
return # There was an error earlier return # There was an error earlier
# is_pyglobal seems to be True for module level-globals only.
# We use this to access class->tp_dict if necessary.
if entry.is_pyglobal: if entry.is_pyglobal:
namespace = self.entry.namespace_cname namespace = self.entry.namespace_cname
if entry.is_member:
# if we entry is a member we have to cheat: SetAttr does not work
# on types, so we create a descriptor which is then added to tp_dict
if Options.intern_names:
code.put_error_if_neg(self.pos,
'PyDict_SetItem(%s->tp_dict, %s, %s)' % (
namespace,
entry.interned_cname,
rhs.py_result()))
else:
code.put_error_if_neg(self.pos,
'PyDict_SetItemString(%s->tp_dict, %s, %s)' % (
namespace,
entry.name,
rhs.py_result()))
else:
if Options.intern_names: if Options.intern_names:
code.put_error_if_neg(self.pos, code.put_error_if_neg(self.pos,
'PyObject_SetAttr(%s, %s, %s)' % ( 'PyObject_SetAttr(%s, %s, %s)' % (
...@@ -832,6 +868,7 @@ class NameNode(AtomicExprNode): ...@@ -832,6 +868,7 @@ class NameNode(AtomicExprNode):
print "NameNode.generate_assignment_code:" print "NameNode.generate_assignment_code:"
print "...generating disposal code for", rhs print "...generating disposal code for", rhs
rhs.generate_disposal_code(code) rhs.generate_disposal_code(code)
else: else:
if self.type.is_pyobject: if self.type.is_pyobject:
#print "NameNode.generate_assignment_code: to", self.name ### #print "NameNode.generate_assignment_code: to", self.name ###
...@@ -1013,6 +1050,9 @@ class TempNode(AtomicExprNode): ...@@ -1013,6 +1050,9 @@ class TempNode(AtomicExprNode):
self.result_ctype = py_object_type self.result_ctype = py_object_type
self.is_temp = 1 self.is_temp = 1
def analyse_types(self, env):
return self.type
def generate_result_code(self, code): def generate_result_code(self, code):
pass pass
...@@ -1654,6 +1694,8 @@ class AttributeNode(ExprNode): ...@@ -1654,6 +1694,8 @@ class AttributeNode(ExprNode):
entry = None entry = None
if obj_type.attributes_known(): if obj_type.attributes_known():
entry = obj_type.scope.lookup_here(self.attribute) entry = obj_type.scope.lookup_here(self.attribute)
if entry and entry.is_member:
entry = None
else: else:
error(self.pos, error(self.pos,
"Cannot select attribute of incomplete type '%s'" "Cannot select attribute of incomplete type '%s'"
...@@ -1663,6 +1705,8 @@ class AttributeNode(ExprNode): ...@@ -1663,6 +1705,8 @@ class AttributeNode(ExprNode):
if entry: if entry:
if obj_type.is_extension_type and entry.name == "__weakref__": if obj_type.is_extension_type and entry.name == "__weakref__":
error(self.pos, "Illegal use of special attribute __weakref__") error(self.pos, "Illegal use of special attribute __weakref__")
# methods need the normal attribute lookup
# because they do not have struct entries
if entry.is_variable or entry.is_cmethod: if entry.is_variable or entry.is_cmethod:
self.type = entry.type self.type = entry.type
self.member = entry.cname self.member = entry.cname
...@@ -2295,6 +2339,8 @@ unop_node_classes = { ...@@ -2295,6 +2339,8 @@ unop_node_classes = {
def unop_node(pos, operator, operand): def unop_node(pos, operator, operand):
# Construct unnop node of appropriate class for # Construct unnop node of appropriate class for
# given operator. # given operator.
if isinstance(operand, IntNode) and operator == '-':
return IntNode(pos = operand.pos, value = -int(operand.value))
return unop_node_classes[operator](pos, return unop_node_classes[operator](pos,
operator = operator, operator = operator,
operand = operand) operand = operand)
...@@ -2716,7 +2762,7 @@ class BoolBinopNode(ExprNode): ...@@ -2716,7 +2762,7 @@ class BoolBinopNode(ExprNode):
if self.type.is_pyobject: if self.type.is_pyobject:
test_result = self.temp_bool.result_code test_result = self.temp_bool.result_code
code.putln( code.putln(
"%s = PyObject_IsTrue(%s); %s" % ( "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
test_result, test_result,
self.operand1.py_result(), self.operand1.py_result(),
code.error_goto_if_neg(test_result, self.pos))) code.error_goto_if_neg(test_result, self.pos)))
...@@ -2742,7 +2788,6 @@ class CondExprNode(ExprNode): ...@@ -2742,7 +2788,6 @@ class CondExprNode(ExprNode):
self.true_val.analyse_types(env) self.true_val.analyse_types(env)
self.false_val.analyse_types(env) self.false_val.analyse_types(env)
self.type = self.compute_result_type(self.true_val.type, self.false_val.type) self.type = self.compute_result_type(self.true_val.type, self.false_val.type)
if self.type:
if self.true_val.type.is_pyobject or self.false_val.type.is_pyobject: if self.true_val.type.is_pyobject or self.false_val.type.is_pyobject:
self.true_val = self.true_val.coerce_to(self.type, env) self.true_val = self.true_val.coerce_to(self.type, env)
self.false_val = self.false_val.coerce_to(self.type, env) self.false_val = self.false_val.coerce_to(self.type, env)
...@@ -2750,7 +2795,7 @@ class CondExprNode(ExprNode): ...@@ -2750,7 +2795,7 @@ class CondExprNode(ExprNode):
self.true_val = self.true_val.coerce_to_temp(env) self.true_val = self.true_val.coerce_to_temp(env)
self.false_val = self.false_val.coerce_to_temp(env) self.false_val = self.false_val.coerce_to_temp(env)
self.is_temp = 1 self.is_temp = 1
else: if self.type == PyrexTypes.error_type:
self.type_error() self.type_error()
def allocate_temps(self, env, result_code = None): def allocate_temps(self, env, result_code = None):
...@@ -2784,8 +2829,12 @@ class CondExprNode(ExprNode): ...@@ -2784,8 +2829,12 @@ class CondExprNode(ExprNode):
return type1 return type1
elif type1.is_pyobject or type2.is_pyobject: elif type1.is_pyobject or type2.is_pyobject:
return py_object_type return py_object_type
elif type1.assignable_from(type2):
return type1
elif type2.assignable_from(type1):
return type2
else: else:
return None return PyrexTypes.error_type
def type_error(self): def type_error(self):
if not (self.true_val.type.is_error or self.false_val.type.is_error): if not (self.true_val.type.is_error or self.false_val.type.is_error):
...@@ -2807,6 +2856,16 @@ class CondExprNode(ExprNode): ...@@ -2807,6 +2856,16 @@ class CondExprNode(ExprNode):
code.putln("}") code.putln("}")
self.test.generate_disposal_code(code) self.test.generate_disposal_code(code)
richcmp_constants = {
"<" : "Py_LT",
"<=": "Py_LE",
"==": "Py_EQ",
"!=": "Py_NE",
"<>": "Py_NE",
">" : "Py_GT",
">=": "Py_GE",
}
class CmpNode: class CmpNode:
# Mixin class containing code common to PrimaryCmpNodes # Mixin class containing code common to PrimaryCmpNodes
# and CascadedCmpNodes. # and CascadedCmpNodes.
...@@ -2816,6 +2875,10 @@ class CmpNode: ...@@ -2816,6 +2875,10 @@ class CmpNode:
or (self.cascade and self.cascade.is_python_comparison()) or (self.cascade and self.cascade.is_python_comparison())
or self.operator in ('in', 'not_in')) or self.operator in ('in', 'not_in'))
def is_python_result(self):
return ((self.has_python_operands() and self.operator not in ('is', 'is_not', 'in', 'not_in'))
or (self.cascade and self.cascade.is_python_result()))
def check_types(self, env, operand1, op, operand2): def check_types(self, env, operand1, op, operand2):
if not self.types_okay(operand1, op, operand2): if not self.types_okay(operand1, op, operand2):
error(self.pos, "Invalid types for '%s' (%s, %s)" % error(self.pos, "Invalid types for '%s' (%s, %s)" %
...@@ -2842,33 +2905,40 @@ class CmpNode: ...@@ -2842,33 +2905,40 @@ class CmpNode:
def generate_operation_code(self, code, result_code, def generate_operation_code(self, code, result_code,
operand1, op , operand2): operand1, op , operand2):
if self.type is PyrexTypes.py_object_type:
coerce_result = "__Pyx_PyBool_FromLong"
else:
coerce_result = ""
if 'not' in op: negation = "!"
else: negation = ""
if op == 'in' or op == 'not_in': if op == 'in' or op == 'not_in':
code.putln( code.putln(
"%s = PySequence_Contains(%s, %s); %s" % ( "%s = %s(%sPySequence_Contains(%s, %s)); %s" % (
result_code, result_code,
coerce_result,
negation,
operand2.py_result(), operand2.py_result(),
operand1.py_result(), operand1.py_result(),
code.error_goto_if_neg(result_code, self.pos))) code.error_goto_if_neg(result_code, self.pos)))
if op == 'not_in':
code.putln(
"%s = !%s;" % (
result_code, result_code))
elif (operand1.type.is_pyobject elif (operand1.type.is_pyobject
and op not in ('is', 'is_not')): and op not in ('is', 'is_not')):
code.put_error_if_neg(self.pos, code.putln("%s = PyObject_RichCompare(%s, %s, %s); %s" % (
"PyObject_Cmp(%s, %s, &%s)" % ( result_code,
operand1.py_result(), operand1.py_result(),
operand2.py_result(), operand2.py_result(),
result_code)) richcmp_constants[op],
code.putln( code.error_goto_if_null(result_code, self.pos)))
"%s = %s %s 0;" % ( else:
result_code, result_code, op)) if operand1.type.is_pyobject:
res1, res2 = operand1.py_result(), operand2.py_result()
else: else:
code.putln("%s = %s %s %s;" % ( res1, res2 = operand1.result_code, operand2.result_code
code.putln("%s = %s(%s %s %s);" % (
result_code, result_code,
operand1.result_code, coerce_result,
res1,
self.c_operator(op), self.c_operator(op),
operand2.result_code)) res2))
def c_operator(self, op): def c_operator(self, op):
if op == 'is': if op == 'is':
...@@ -2908,7 +2978,14 @@ class PrimaryCmpNode(ExprNode, CmpNode): ...@@ -2908,7 +2978,14 @@ class PrimaryCmpNode(ExprNode, CmpNode):
self.operand2 = self.operand2.coerce_to_simple(env) self.operand2 = self.operand2.coerce_to_simple(env)
self.cascade.coerce_cascaded_operands_to_temp(env) self.cascade.coerce_cascaded_operands_to_temp(env)
self.check_operand_types(env) self.check_operand_types(env)
if self.is_python_result():
self.type = PyrexTypes.py_object_type
else:
self.type = PyrexTypes.c_bint_type self.type = PyrexTypes.c_bint_type
cdr = self.cascade
while cdr:
cdr.type = self.type
cdr = cdr.cascade
if self.is_pycmp or self.cascade: if self.is_pycmp or self.cascade:
self.is_temp = 1 self.is_temp = 1
...@@ -3019,6 +3096,9 @@ class CascadedCmpNode(Node, CmpNode): ...@@ -3019,6 +3096,9 @@ class CascadedCmpNode(Node, CmpNode):
self.cascade.release_subexpr_temps(env) self.cascade.release_subexpr_temps(env)
def generate_evaluation_code(self, code, result, operand1): def generate_evaluation_code(self, code, result, operand1):
if self.type.is_pyobject:
code.putln("if (__Pyx_PyObject_IsTrue(%s)) {" % result)
else:
code.putln("if (%s) {" % result) code.putln("if (%s) {" % result)
self.operand2.generate_evaluation_code(code) self.operand2.generate_evaluation_code(code)
self.generate_operation_code(code, result, self.generate_operation_code(code, result,
...@@ -3213,7 +3293,7 @@ class CoerceToBooleanNode(CoercionNode): ...@@ -3213,7 +3293,7 @@ class CoerceToBooleanNode(CoercionNode):
def generate_result_code(self, code): def generate_result_code(self, code):
if self.arg.type.is_pyobject: if self.arg.type.is_pyobject:
code.putln( code.putln(
"%s = PyObject_IsTrue(%s); %s" % ( "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
self.result_code, self.result_code,
self.arg.py_result(), self.arg.py_result(),
code.error_goto_if_neg(self.result_code, self.pos))) code.error_goto_if_neg(self.result_code, self.pos)))
......
...@@ -125,6 +125,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -125,6 +125,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("") code.putln("")
code.putln("/* Implementation of %s */" % env.qualified_name) code.putln("/* Implementation of %s */" % env.qualified_name)
self.generate_const_definitions(env, code) self.generate_const_definitions(env, code)
self.generate_interned_num_decls(env, code)
self.generate_interned_name_decls(env, code) self.generate_interned_name_decls(env, code)
self.generate_py_string_decls(env, code) self.generate_py_string_decls(env, code)
self.generate_cached_builtins_decls(env, code) self.generate_cached_builtins_decls(env, code)
...@@ -1238,6 +1239,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1238,6 +1239,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.error_goto(self.pos))); code.error_goto(self.pos)));
def generate_intern_code(self, env, code): def generate_intern_code(self, env, code):
for entry in env.pynum_entries:
code.putln("%s = PyInt_FromLong(%s); %s;" % (
entry.cname,
entry.init,
code.error_goto_if_null(entry.cname, self.pos)))
if env.intern_map: if env.intern_map:
env.use_utility_code(Nodes.init_intern_tab_utility_code); env.use_utility_code(Nodes.init_intern_tab_utility_code);
code.putln( code.putln(
......
...@@ -21,6 +21,7 @@ pymethdef_prefix = pyrex_prefix + "mdef_" ...@@ -21,6 +21,7 @@ pymethdef_prefix = pyrex_prefix + "mdef_"
methtab_prefix = pyrex_prefix + "methods_" methtab_prefix = pyrex_prefix + "methods_"
memtab_prefix = pyrex_prefix + "members_" memtab_prefix = pyrex_prefix + "members_"
interned_prefix = pyrex_prefix + "n_" interned_prefix = pyrex_prefix + "n_"
interned_num_prefix = pyrex_prefix + "num_"
objstruct_prefix = pyrex_prefix + "obj_" objstruct_prefix = pyrex_prefix + "obj_"
typeptr_prefix = pyrex_prefix + "ptype_" typeptr_prefix = pyrex_prefix + "ptype_"
prop_set_prefix = pyrex_prefix + "setprop_" prop_set_prefix = pyrex_prefix + "setprop_"
...@@ -56,3 +57,7 @@ c_api_tab_cname = pyrex_prefix + "c_api_tab" ...@@ -56,3 +57,7 @@ c_api_tab_cname = pyrex_prefix + "c_api_tab"
gilstate_cname = pyrex_prefix + "state" gilstate_cname = pyrex_prefix + "state"
extern_c_macro = pyrex_prefix.upper() + "EXTERN_C" extern_c_macro = pyrex_prefix.upper() + "EXTERN_C"
def py_version_hex(major, minor=0, micro=0, release_level=0, release_serial=0):
return (major << 24) | (minor << 16) | (micro << 8) | (release_level << 4) | (release_serial)
...@@ -116,6 +116,18 @@ class BlockNode: ...@@ -116,6 +116,18 @@ class BlockNode:
code.putln( code.putln(
"static PyObject *%s;" % entry.pystring_cname) "static PyObject *%s;" % entry.pystring_cname)
def generate_interned_num_decls(self, env, code):
# Flush accumulated interned nums from the global scope
# and generate declarations for them.
genv = env.global_scope()
entries = genv.interned_nums
if entries:
code.putln("")
for entry in entries:
code.putln(
"static PyObject *%s;" % entry.cname)
del entries[:]
def generate_cached_builtins_decls(self, env, code): def generate_cached_builtins_decls(self, env, code):
entries = env.builtin_scope().undeclared_cached_entries entries = env.builtin_scope().undeclared_cached_entries
if len(entries) > 0: if len(entries) > 0:
...@@ -429,6 +441,9 @@ class CVarDefNode(StatNode): ...@@ -429,6 +441,9 @@ class CVarDefNode(StatNode):
"Python object cannot be declared extern") "Python object cannot be declared extern")
name = name_declarator.name name = name_declarator.name
cname = name_declarator.cname cname = name_declarator.cname
if name == '':
error(declarator.pos, "Missing name in declaration.")
return
if type.is_cfunction: if type.is_cfunction:
dest_scope.declare_cfunction(name, type, declarator.pos, dest_scope.declare_cfunction(name, type, declarator.pos,
cname = cname, visibility = self.visibility) cname = cname, visibility = self.visibility)
...@@ -534,6 +549,8 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -534,6 +549,8 @@ class FuncDefNode(StatNode, BlockNode):
# #filename string C name of filename string const # #filename string C name of filename string const
# entry Symtab.Entry # entry Symtab.Entry
py_func = None
def analyse_expressions(self, env): def analyse_expressions(self, env):
pass pass
...@@ -551,6 +568,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -551,6 +568,7 @@ class FuncDefNode(StatNode, BlockNode):
# Code for nested function definitions would go here # Code for nested function definitions would go here
# if we supported them, which we probably won't. # if we supported them, which we probably won't.
# ----- Top-level constants used by this function # ----- Top-level constants used by this function
self.generate_interned_num_decls(lenv, code)
self.generate_interned_name_decls(lenv, code) self.generate_interned_name_decls(lenv, code)
self.generate_py_string_decls(lenv, code) self.generate_py_string_decls(lenv, code)
self.generate_cached_builtins_decls(lenv, code) self.generate_cached_builtins_decls(lenv, code)
...@@ -559,6 +577,10 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -559,6 +577,10 @@ class FuncDefNode(StatNode, BlockNode):
self.generate_const_definitions(lenv, code) self.generate_const_definitions(lenv, code)
# ----- Function header # ----- Function header
code.putln("") 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, self.generate_function_header(code,
with_pymethdef = env.is_py_class_scope) with_pymethdef = env.is_py_class_scope)
# ----- Local variable declarations # ----- Local variable declarations
...@@ -639,6 +661,10 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -639,6 +661,10 @@ class FuncDefNode(StatNode, BlockNode):
# retval_code) # retval_code)
code.putln("return %s;" % retval_code) code.putln("return %s;" % retval_code)
code.putln("}") code.putln("}")
# ----- Python version
if self.py_func:
self.py_func.generate_function_definitions(env, code)
def put_stararg_decrefs(self, code): def put_stararg_decrefs(self, code):
pass pass
...@@ -671,7 +697,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -671,7 +697,7 @@ class FuncDefNode(StatNode, BlockNode):
class CFuncDefNode(FuncDefNode): class CFuncDefNode(FuncDefNode):
# C function definition. # C function definition.
# #
# modifiers 'inline ' or '' # modifiers ['inline']
# visibility 'private' or 'public' or 'extern' # visibility 'private' or 'public' or 'extern'
# base_type CBaseTypeNode # base_type CBaseTypeNode
# declarator CDeclaratorNode # declarator CDeclaratorNode
...@@ -701,6 +727,29 @@ class CFuncDefNode(FuncDefNode): ...@@ -701,6 +727,29 @@ class CFuncDefNode(FuncDefNode):
defining = self.body is not None) defining = self.body is not None)
self.return_type = type.return_type self.return_type = type.return_type
if self.overridable:
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)
self.override = OverrideCheckNode(self.pos, py_func = self.py_func)
self.body.stats.insert(0, self.override)
def declare_arguments(self, env): def declare_arguments(self, env):
for arg in self.type.args: for arg in self.type.args:
if not arg.name: if not arg.name:
...@@ -730,9 +779,9 @@ class CFuncDefNode(FuncDefNode): ...@@ -730,9 +779,9 @@ class CFuncDefNode(FuncDefNode):
storage_class = "%s " % Naming.extern_c_macro storage_class = "%s " % Naming.extern_c_macro
else: else:
storage_class = "" storage_class = ""
code.putln("%s%s%s {" % ( code.putln("%s%s %s {" % (
storage_class, storage_class,
self.modifiers, ' '.join(self.modifiers).upper(), # macro forms
header)) header))
def generate_argument_declarations(self, env, code): def generate_argument_declarations(self, env, code):
...@@ -925,7 +974,7 @@ class DefNode(FuncDefNode): ...@@ -925,7 +974,7 @@ class DefNode(FuncDefNode):
else: else:
self.entry.doc = self.doc self.entry.doc = self.doc
self.entry.func_cname = \ 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 = \ self.entry.doc_cname = \
Naming.funcdoc_prefix + env.scope_prefix + self.name Naming.funcdoc_prefix + env.scope_prefix + self.name
self.entry.pymethdef_cname = \ self.entry.pymethdef_cname = \
...@@ -967,13 +1016,14 @@ class DefNode(FuncDefNode): ...@@ -967,13 +1016,14 @@ class DefNode(FuncDefNode):
self.synthesize_assignment_node(env) self.synthesize_assignment_node(env)
def analyse_default_values(self, env): def analyse_default_values(self, env):
genv = env.global_scope()
for arg in self.args: for arg in self.args:
if arg.default: if arg.default:
if arg.is_generic: if arg.is_generic:
arg.default.analyse_types(env) arg.default.analyse_types(genv)
arg.default = arg.default.coerce_to(arg.type, env) arg.default = arg.default.coerce_to(arg.type, genv)
arg.default.allocate_temps(env) arg.default.allocate_temps(genv)
arg.default_entry = env.add_default_value(arg.type) arg.default_entry = genv.add_default_value(arg.type)
arg.default_entry.used = 1 arg.default_entry.used = 1
else: else:
error(arg.pos, error(arg.pos,
...@@ -991,7 +1041,7 @@ class DefNode(FuncDefNode): ...@@ -991,7 +1041,7 @@ class DefNode(FuncDefNode):
self.assmt.analyse_declarations(env) self.assmt.analyse_declarations(env)
self.assmt.analyse_expressions(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 = [] arg_code_list = []
sig = self.entry.signature sig = self.entry.signature
if sig.has_dummy_arg: if sig.has_dummy_arg:
...@@ -1014,6 +1064,8 @@ class DefNode(FuncDefNode): ...@@ -1014,6 +1064,8 @@ class DefNode(FuncDefNode):
dc = self.return_type.declaration_code(self.entry.func_cname) dc = self.return_type.declaration_code(self.entry.func_cname)
header = "static %s(%s)" % (dc, arg_code) header = "static %s(%s)" % (dc, arg_code)
code.putln("%s; /*proto*/" % header) code.putln("%s; /*proto*/" % header)
if proto_only:
return
if self.entry.doc: if self.entry.doc:
code.putln( code.putln(
'static char %s[] = "%s";' % ( 'static char %s[] = "%s";' % (
...@@ -1184,7 +1236,10 @@ class DefNode(FuncDefNode): ...@@ -1184,7 +1236,10 @@ class DefNode(FuncDefNode):
old_type = arg.hdr_type old_type = arg.hdr_type
new_type = arg.type new_type = arg.type
if old_type.is_pyobject: if old_type.is_pyobject:
code.putln("if (likely(%s)) {" % arg.hdr_cname) if arg.default:
code.putln("if (%s) {" % arg.hdr_cname)
else:
code.putln("assert(%s); {" % arg.hdr_cname)
self.generate_arg_conversion_from_pyobject(arg, code) self.generate_arg_conversion_from_pyobject(arg, code)
code.putln("}") code.putln("}")
elif new_type.is_pyobject: elif new_type.is_pyobject:
...@@ -1275,6 +1330,50 @@ class DefNode(FuncDefNode): ...@@ -1275,6 +1330,50 @@ class DefNode(FuncDefNode):
def caller_will_check_exceptions(self): def caller_will_check_exceptions(self):
return 1 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): class PyClassDefNode(StatNode, BlockNode):
# A Python class definition. # A Python class definition.
...@@ -1414,7 +1513,8 @@ class CClassDefNode(StatNode): ...@@ -1414,7 +1513,8 @@ class CClassDefNode(StatNode):
def analyse_expressions(self, env): def analyse_expressions(self, env):
if self.body: if self.body:
self.body.analyse_expressions(env) scope = self.entry.type.scope
self.body.analyse_expressions(scope)
def generate_function_definitions(self, env, code): def generate_function_definitions(self, env, code):
if self.body: if self.body:
......
...@@ -1209,13 +1209,21 @@ def p_statement(s, level, cdef_flag = 0, visibility = 'private'): ...@@ -1209,13 +1209,21 @@ def p_statement(s, level, cdef_flag = 0, visibility = 'private'):
if level not in ('module', 'module_pxd'): if level not in ('module', 'module_pxd'):
s.error("ctypedef statement not allowed here") s.error("ctypedef statement not allowed here")
return p_ctypedef_statement(s, level, visibility) return p_ctypedef_statement(s, level, visibility)
overridable = 0
if s.sy == 'cdef': if s.sy == 'cdef':
cdef_flag = 1 cdef_flag = 1
s.next() s.next()
if s.sy == 'rdef':
cdef_flag = 1
overridable = 1
s.next()
if cdef_flag: if cdef_flag:
if level not in ('module', 'module_pxd', 'function', 'c_class', 'c_class_pxd'): if level not in ('module', 'module_pxd', 'function', 'c_class', 'c_class_pxd'):
s.error('cdef statement not allowed here') s.error('cdef statement not allowed here')
return p_cdef_statement(s, level, visibility) return p_cdef_statement(s, level, visibility, overridable = overridable)
# elif s.sy == 'rdef':
# s.next()
# return p_c_func_or_var_declaration(s, level, s.position(), visibility = visibility, overridable = True)
elif s.sy == 'def': elif s.sy == 'def':
if level not in ('module', 'class', 'c_class', 'property'): if level not in ('module', 'class', 'c_class', 'property'):
s.error('def statement not allowed here') s.error('def statement not allowed here')
...@@ -1234,8 +1242,6 @@ def p_statement(s, level, cdef_flag = 0, visibility = 'private'): ...@@ -1234,8 +1242,6 @@ def p_statement(s, level, cdef_flag = 0, visibility = 'private'):
if level in ('c_class', 'c_class_pxd'): if level in ('c_class', 'c_class_pxd'):
if s.sy == 'pass': if s.sy == 'pass':
return p_pass_statement(s, with_newline = 1) return p_pass_statement(s, with_newline = 1)
else:
s.error("Executable statement not allowed here")
if s.sy == 'if': if s.sy == 'if':
return p_if_statement(s) return p_if_statement(s)
elif s.sy == 'while': elif s.sy == 'while':
...@@ -1572,8 +1578,10 @@ def p_c_arg_decl(s, in_pyfunc, cmethod_flag = 0, kw_only = 0): ...@@ -1572,8 +1578,10 @@ def p_c_arg_decl(s, in_pyfunc, cmethod_flag = 0, kw_only = 0):
default = default, default = default,
kw_only = kw_only) kw_only = kw_only)
def p_cdef_statement(s, level, visibility = 'private'): def p_cdef_statement(s, level, visibility = 'private', overridable = False):
pos = s.position() pos = s.position()
if overridable and level not in ('c_class', 'c_class_pxd'):
error(pos, "Overridable cdef function not allowed here")
visibility = p_visibility(s, visibility) visibility = p_visibility(s, visibility)
if visibility == 'extern' and s.sy in ('from' ,':'): if visibility == 'extern' and s.sy in ('from' ,':'):
return p_cdef_extern_block(s, level, pos) return p_cdef_extern_block(s, level, pos)
...@@ -1595,7 +1603,7 @@ def p_cdef_statement(s, level, visibility = 'private'): ...@@ -1595,7 +1603,7 @@ def p_cdef_statement(s, level, visibility = 'private'):
s.expect_newline('Expected a newline') s.expect_newline('Expected a newline')
return node return node
else: else:
return p_c_func_or_var_declaration(s, level, pos, visibility) return p_c_func_or_var_declaration(s, level, pos, visibility, overridable)
def p_cdef_extern_block(s, level, pos): def p_cdef_extern_block(s, level, pos):
include_file = None include_file = None
...@@ -1700,13 +1708,13 @@ def p_visibility(s, prev_visibility): ...@@ -1700,13 +1708,13 @@ def p_visibility(s, prev_visibility):
return visibility return visibility
def p_c_modifiers(s): def p_c_modifiers(s):
if s.systring in ('inline', ): if s.sy == 'IDENT' and s.systring in ('inline',):
modifier = s.systring.upper() # uppercase is macro defined for various compilers modifier = s.systring
s.next() s.next()
return modifier + ' ' + p_c_modifiers(s) return [modifier] + p_c_modifiers(s)
return "" return []
def p_c_func_or_var_declaration(s, level, pos, visibility = 'private'): def p_c_func_or_var_declaration(s, level, pos, visibility = 'private', overridable = False):
cmethod_flag = level in ('c_class', 'c_class_pxd') cmethod_flag = level in ('c_class', 'c_class_pxd')
modifiers = p_c_modifiers(s) modifiers = p_c_modifiers(s)
base_type = p_c_base_type(s) base_type = p_c_base_type(s)
...@@ -1720,7 +1728,8 @@ def p_c_func_or_var_declaration(s, level, pos, visibility = 'private'): ...@@ -1720,7 +1728,8 @@ def p_c_func_or_var_declaration(s, level, pos, visibility = 'private'):
base_type = base_type, base_type = base_type,
declarator = declarator, declarator = declarator,
body = suite, body = suite,
modifiers = modifiers) modifiers = modifiers,
overridable = overridable)
else: else:
if level == 'module_pxd' and visibility <> 'extern': if level == 'module_pxd' and visibility <> 'extern':
error(pos, error(pos,
......
...@@ -346,8 +346,6 @@ class CIntType(CNumericType): ...@@ -346,8 +346,6 @@ class CIntType(CNumericType):
class CBIntType(CIntType): class CBIntType(CIntType):
# TODO: this should be a macro "(__ ? Py_True : Py_False)"
# and no error checking should be needed (just an incref).
to_py_function = "__Pyx_PyBool_FromLong" to_py_function = "__Pyx_PyBool_FromLong"
from_py_function = "__Pyx_PyObject_IsTrue" from_py_function = "__Pyx_PyObject_IsTrue"
exception_check = 0 exception_check = 0
......
...@@ -138,7 +138,7 @@ reserved_words = [ ...@@ -138,7 +138,7 @@ reserved_words = [
"raise", "import", "exec", "try", "except", "finally", "raise", "import", "exec", "try", "except", "finally",
"while", "if", "elif", "else", "for", "in", "assert", "while", "if", "elif", "else", "for", "in", "assert",
"and", "or", "not", "is", "in", "lambda", "from", "and", "or", "not", "is", "in", "lambda", "from",
"NULL", "cimport", "by", "with" "NULL", "cimport", "by", "with", "rdef"
] ]
function_contexts = [ # allowed arguments to the "with" option function_contexts = [ # allowed arguments to the "with" option
......
...@@ -30,6 +30,7 @@ class Entry: ...@@ -30,6 +30,7 @@ class Entry:
# or class attribute during # or class attribute during
# class construction # class construction
# is_special boolean Is a special class method # is_special boolean Is a special class method
# is_member boolean Is an assigned class member
# is_variable boolean Is a variable # is_variable boolean Is a variable
# is_cfunction boolean Is a C function # is_cfunction boolean Is a C function
# is_cmethod boolean Is a C method of an extension type # is_cmethod boolean Is a C method of an extension type
...@@ -72,6 +73,7 @@ class Entry: ...@@ -72,6 +73,7 @@ class Entry:
is_cglobal = 0 is_cglobal = 0
is_pyglobal = 0 is_pyglobal = 0
is_special = 0 is_special = 0
is_member = 0
is_variable = 0 is_variable = 0
is_cfunction = 0 is_cfunction = 0
is_cmethod = 0 is_cmethod = 0
...@@ -121,6 +123,7 @@ class Scope: ...@@ -121,6 +123,7 @@ class Scope:
# free_temp_entries [Entry] Temp variables currently unused # free_temp_entries [Entry] Temp variables currently unused
# temp_counter integer Counter for naming temp vars # temp_counter integer Counter for naming temp vars
# cname_to_entry {string : Entry} Temp cname to entry mapping # cname_to_entry {string : Entry} Temp cname to entry mapping
# int_to_entry {int : Entry} Temp cname to entry mapping
# pow_function_used boolean The C pow() function is used # pow_function_used boolean The C pow() function is used
# return_type PyrexType or None Return type of function owning scope # return_type PyrexType or None Return type of function owning scope
# is_py_class_scope boolean Is a Python class scope # is_py_class_scope boolean Is a Python class scope
...@@ -167,6 +170,7 @@ class Scope: ...@@ -167,6 +170,7 @@ class Scope:
self.cname_to_entry = {} self.cname_to_entry = {}
self.pow_function_used = 0 self.pow_function_used = 0
self.string_to_entry = {} self.string_to_entry = {}
self.num_to_entry = {}
self.pystring_entries = [] self.pystring_entries = []
def __str__(self): def __str__(self):
...@@ -393,6 +397,28 @@ class Scope: ...@@ -393,6 +397,28 @@ class Scope:
self.pystring_entries.append(entry) self.pystring_entries.append(entry)
self.global_scope().all_pystring_entries.append(entry) self.global_scope().all_pystring_entries.append(entry)
def add_py_num(self, value):
# Add an entry for an int constant.
cname = "%s%s" % (Naming.interned_num_prefix, value)
cname = cname.replace('-', 'neg_').replace('.','_')
entry = Entry("", cname, c_long_type, init = value)
entry.used = 1
entry.is_interned = 1
self.const_entries.append(entry)
self.interned_nums.append(entry)
return entry
def get_py_num(self, value):
# Get entry for int constant. Returns an existing
# one if possible, otherwise creates a new one.
genv = self.global_scope()
entry = genv.num_to_entry.get(value)
if not entry:
entry = genv.add_py_num(value)
genv.num_to_entry[value] = entry
genv.pynum_entries.append(entry)
return entry
def new_const_cname(self): def new_const_cname(self):
# Create a new globally-unique name for a constant. # Create a new globally-unique name for a constant.
return self.global_scope().new_const_cname() return self.global_scope().new_const_cname()
...@@ -606,8 +632,10 @@ class ModuleScope(Scope): ...@@ -606,8 +632,10 @@ class ModuleScope(Scope):
self.cimported_modules = [] self.cimported_modules = []
self.intern_map = {} self.intern_map = {}
self.interned_names = [] self.interned_names = []
self.interned_nums = []
self.all_pystring_entries = [] self.all_pystring_entries = []
self.types_imported = {} self.types_imported = {}
self.pynum_entries = []
def qualifying_scope(self): def qualifying_scope(self):
return self.parent_module return self.parent_module
...@@ -1052,6 +1080,7 @@ class CClassScope(ClassScope): ...@@ -1052,6 +1080,7 @@ class CClassScope(ClassScope):
def declare_var(self, name, type, pos, def declare_var(self, name, type, pos,
cname = None, visibility = 'private', is_cdef = 0): cname = None, visibility = 'private', is_cdef = 0):
if is_cdef:
# Add an entry for an attribute. # Add an entry for an attribute.
if self.defined: if self.defined:
error(pos, error(pos,
...@@ -1084,13 +1113,25 @@ class CClassScope(ClassScope): ...@@ -1084,13 +1113,25 @@ class CClassScope(ClassScope):
error(pos, error(pos,
"Non-generic Python attribute cannot be exposed for writing from Python") "Non-generic Python attribute cannot be exposed for writing from Python")
return entry return entry
else:
# Add an entry for a class attribute.
entry = Scope.declare_var(self, name, type, pos,
cname, visibility, is_cdef)
entry.is_member = 1
entry.is_pyglobal = 1 # xxx: is_pyglobal changes behaviour in so many places that
# I keep it in for now. is_member should be enough
# later on
entry.namespace_cname = "(PyObject *)%s" % self.parent_type.typeptr_cname
if Options.intern_names:
entry.interned_cname = self.intern(name)
return entry
def declare_pyfunction(self, name, pos): def declare_pyfunction(self, name, pos):
# Add an entry for a method. # Add an entry for a method.
if name in ('__eq__', '__ne__', '__lt__', '__gt__', '__le__', '__ge__'): if name in ('__eq__', '__ne__', '__lt__', '__gt__', '__le__', '__ge__'):
error(pos, "Special method %s must be implemented via __richcmp__" error(pos, "Special method %s must be implemented via __richcmp__" % name)
% name) entry = self.declare_var(name, py_object_type, pos)
entry = self.declare(name, name, py_object_type, pos)
special_sig = get_special_method_signature(name) special_sig = get_special_method_signature(name)
if special_sig: if special_sig:
# Special methods get put in the method table with a particular # Special methods get put in the method table with a particular
...@@ -1169,6 +1210,27 @@ class CClassScope(ClassScope): ...@@ -1169,6 +1210,27 @@ class CClassScope(ClassScope):
adapt(base_entry.cname), base_entry.visibility) adapt(base_entry.cname), base_entry.visibility)
entry.is_inherited = 1 entry.is_inherited = 1
def allocate_temp(self, type):
return Scope.allocate_temp(self.global_scope(), type)
def release_temp(self, cname):
return Scope.release_temp(self.global_scope(), cname)
def lookup(self, name):
if name == "classmethod":
# We don't want to use the builtin classmethod here 'cause it won't do the
# right thing in this scope (as the class memebers aren't still functions).
# Don't want to add a cfunction to this scope 'cause that would mess with
# the type definition, so we just return the right entry.
self.use_utility_code(classmethod_utility_code)
entry = Entry("classmethod",
"__Pyx_Method_ClassMethod",
CFuncType(py_object_type, [CFuncTypeArg("", py_object_type, None)], 0, 0))
entry.is_cfunction = 1
return entry
else:
return Scope.lookup(self, name)
class PropertyScope(Scope): class PropertyScope(Scope):
# Scope holding the __get__, __set__ and __del__ methods for # Scope holding the __get__, __set__ and __del__ methods for
...@@ -1188,3 +1250,25 @@ class PropertyScope(Scope): ...@@ -1188,3 +1250,25 @@ class PropertyScope(Scope):
error(pos, "Only __get__, __set__ and __del__ methods allowed " error(pos, "Only __get__, __set__ and __del__ methods allowed "
"in a property declaration") "in a property declaration")
return None return None
# Should this go elsewhere (and then get imported)?
#------------------------------------------------------------------------------------
classmethod_utility_code = [
"""
#include "descrobject.h"
static PyObject* __Pyx_Method_ClassMethod(PyObject *method); /*proto*/
""","""
static PyObject* __Pyx_Method_ClassMethod(PyObject *method) {
/* It appears that PyMethodDescr_Type is not anywhere exposed in the Python/C API */
/* if (!PyObject_TypeCheck(method, &PyMethodDescr_Type)) { */
if (strcmp(method->ob_type->tp_name, "method_descriptor") != 0) {
PyErr_Format(PyExc_TypeError, "Extension type classmethod() can only be called on a method_descriptor.");
return NULL;
}
PyMethodDescrObject *descr = (PyMethodDescrObject *)method;
return PyDescr_NewClassMethod(descr->d_type, descr->d_method);
}
"""
]
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import Naming import Naming
import PyrexTypes import PyrexTypes
import sys
class Signature: class Signature:
# Method slot signature descriptor. # Method slot signature descriptor.
...@@ -163,7 +164,7 @@ class GCDependentSlot(SlotDescriptor): ...@@ -163,7 +164,7 @@ class GCDependentSlot(SlotDescriptor):
# the type participates in GC. # the type participates in GC.
def __init__(self, slot_name, no_gc_value, gc_value, dynamic = 0): def __init__(self, slot_name, no_gc_value, gc_value, dynamic = 0):
SlotDescriptor.__init__(self, slot_name, dynamic) SlotDescriptor.__init__(self, slot_name, dynamic = dynamic)
self.no_gc_value = no_gc_value self.no_gc_value = no_gc_value
self.gc_value = gc_value self.gc_value = gc_value
......
version = '0.9.6.6' version = '0.9.6.7'
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