Commit 057375c9 authored by Stefan Behnel's avatar Stefan Behnel

merge of Robert's changes for 0.9.6.11

parents 96fb7ea3 c93ab4d4
# Note: Work in progress
import os
import re
import time
from StringIO import StringIO
import Version
from Code import CCodeWriter
# need one-characters subsitutions (for now) so offsets aren't off
special_chars = [('<', '\xF0', '&lt;'),
('>', '\xF1', '&gt;'),
('&', '\xF2', '&amp;')]
class AnnotationCCodeWriter(CCodeWriter):
......@@ -29,14 +34,13 @@ class AnnotationCCodeWriter(CCodeWriter):
# if pos is not None:
# CCodeWriter.mark_pos(self, pos)
# return
print "marking", pos
if self.last_pos:
try:
code = self.code[self.last_pos[1]]
except KeyError:
code = ""
self.code[self.last_pos[1]] = code + self.buffer.getvalue()
self.buffer = StringIO()
self.buffer = StringIO()
self.last_pos = pos
def annotate(self, pos, item):
......@@ -47,12 +51,10 @@ class AnnotationCCodeWriter(CCodeWriter):
f = open(filename)
lines = f.readlines()
for k in range(len(lines)):
# there has to be a better way to do this
lines[k] = lines[k].replace(' ','\t ')
lines[k] = lines[k].replace(' ','\t\t')
# TODO: this is incorrect
lines[k] = lines[k].replace('<', '~')
lines[k] = lines[k].replace('>', '~')
line = lines[k]
for c, cc, html in special_chars:
line = line.replace(c, cc)
lines[k] = line
f.close()
all = []
for pos, item in self.annotations:
......@@ -86,6 +88,7 @@ body { font-family: courier; font-size: 12; }
.py_api { color: red; }
.pyx_api { color: #FF3000; }
.py_macro_api { color: #FF8000; }
.error_goto { color: #FF8000; }
.tag { }
......@@ -97,6 +100,8 @@ body { font-family: courier; font-size: 12; }
.py_call { color: #FF0000; font-weight: bold; }
.c_call { color: #0000FF; }
.line { margin: 0em }
</style>
<script>
function toggleDiv(id) {
......@@ -108,11 +113,15 @@ function toggleDiv(id) {
</head>
""")
f.write('<body>\n')
f.write('<p>Generated by Cython %s on %s\n' % (Version.version, time.asctime()))
c_file = os.path.basename(filename)[:-3] + 'c'
f.write('<p>Raw output: <a href="%s">%s</a>\n' % (c_file, c_file))
k = 0
py_c_api = re.compile('(Py[A-Z][a-z]+_[A-Z][a-z][A-Za-z_]+)')
pyx_api = re.compile('(__Pyx[A-Za-z_]+)\(')
py_marco_api = re.compile('(Py[A-Za-z]*_[A-Z][A-Z_]+)')
error_goto = re.compile(r'((; *if .*)? \{__pyx_filename = .*goto __pyx_L\w+;\})')
for line in lines:
......@@ -125,16 +134,20 @@ function toggleDiv(id) {
code, c_api_calls = py_c_api.subn(r"<span class='py_api'>\1</span>", code)
code, pyx_api_calls = pyx_api.subn(r"<span class='pyx_api'>\1</span>(", code)
code, macro_api_calls = py_marco_api.subn(r"<span class='py_macro_api'>\1</span>", code)
code, error_goto_calls = error_goto.subn(r"<span class='error_goto'>\1</span>", code)
code = code.replace("<span class='error_goto'>;", ";<span class='error_goto'>")
color = "FFFF%02x" % int(255/(1+(5*c_api_calls+2*pyx_api_calls+macro_api_calls)/10.0))
f.write("<div class='line' style='background-color: #%s' onclick='toggleDiv(\"line%s\")'>" % (color, k))
f.write("<pre class='line' style='background-color: #%s' onclick='toggleDiv(\"line%s\")'>" % (color, k))
f.write(" %d: " % k)
line = line.replace('\t', '&nbsp;')
f.write(line)
for c, cc, html in special_chars:
line = line.replace(cc, html)
f.write(line.rstrip())
f.write('</div>\n')
f.write("<div id='line%s' class='code' style='background-color: #%s'>%s</div>" % (k, color, code.replace('\n', '\n<br>')))
f.write('</pre>\n')
f.write("<pre id='line%s' class='code' style='background-color: #%s'>%s</pre>" % (k, color, code))
f.write('</body></html>\n')
f.close()
......
......@@ -98,6 +98,8 @@ class CCodeWriter:
return "0x%02X%02X%02X%02X" % (tuple(pyversion) + (0,0,0,0))[:4]
def mark_pos(self, pos):
if pos is None:
return
file, line, col = pos
contents = self.file_contents(file)
......
......@@ -1931,7 +1931,7 @@ class AttributeNode(ExprNode):
code.putln(
'%s = PyObject_GetAttrString(%s, "%s"); %s' % (
self.result_code,
self.objpy_result(),
self.obj.py_result(),
self.attribute,
code.error_goto_if_null(self.result_code, self.pos)))
......@@ -2118,6 +2118,19 @@ class SequenceNode(ExprNode):
class TupleNode(SequenceNode):
# Tuple constructor.
def analyse_types(self, env):
if len(self.args) == 0:
self.type = py_object_type
self.is_temp = 0
else:
SequenceNode.analyse_types(self, env)
def calculate_result_code(self):
if len(self.args) > 0:
error(pos, "Positive length tuples must be constructed.")
else:
return Naming.empty_tuple
def compile_time_value(self, denv):
values = self.compile_time_value_list(denv)
......@@ -2127,6 +2140,9 @@ class TupleNode(SequenceNode):
self.compile_time_value_error(e)
def generate_operation_code(self, code):
if len(self.args) == 0:
# result_code is Naming.empty_tuple
return
code.putln(
"%s = PyTuple_New(%s); %s" % (
self.result_code,
......@@ -2565,6 +2581,8 @@ def unop_node(pos, operator, operand):
# given operator.
if isinstance(operand, IntNode) and operator == '-':
return IntNode(pos = operand.pos, value = -int(operand.value))
elif isinstance(operand, UnopNode) and operand.operator == operator:
warning(pos, "Python has no increment/decrement operator: %s%sx = %s(%sx) = x" % ((operator,)*4), 5)
return unop_node_classes[operator](pos,
operator = operator,
operand = operand)
......@@ -3582,20 +3600,7 @@ class CoerceFromPyTypeNode(CoercionNode):
code.putln('%s = %s; %s' % (
self.result_code,
rhs,
code.error_goto_if(self.error_cond(), self.pos)))
def error_cond(self):
conds = []
if self.type.is_string:
conds.append("(!%s)" % self.result_code)
elif self.type.exception_value is not None:
conds.append("(%s == %s)" % (self.result_code, self.type.exception_value))
if self.type.exception_check:
conds.append("PyErr_Occurred()")
if len(conds) > 0:
return " && ".join(conds)
else:
return 0
code.error_goto_if(self.type.error_condition(self.result_code), self.pos)))
class CoerceToBooleanNode(CoercionNode):
......
......@@ -17,7 +17,6 @@ import Parsing
from Symtab import BuiltinScope, ModuleScope
import Code
from Cython.Utils import replace_suffix
import Builtin
from Cython import Utils
verbose = 0
......@@ -34,6 +33,7 @@ class Context:
def __init__(self, include_directories):
#self.modules = {"__builtin__" : BuiltinScope()}
import Builtin
self.modules = {"__builtin__" : Builtin.builtin_scope}
self.include_directories = include_directories
......
......@@ -217,12 +217,14 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_py_string_decls(env, code)
self.generate_cached_builtins_decls(env, code)
self.body.generate_function_definitions(env, code)
code.mark_pos(None)
self.generate_interned_name_table(env, code)
self.generate_py_string_table(env, code)
self.generate_typeobj_definitions(env, code)
self.generate_method_table(env, code)
self.generate_filename_init_prototype(code)
self.generate_module_init_func(modules[:-1], env, code)
code.mark_pos(None)
self.generate_module_cleanup_func(env, code)
self.generate_filename_table(code)
self.generate_utility_functions(env, code)
......@@ -284,6 +286,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln('')
code.putln('static PyObject *%s;' % env.module_cname)
code.putln('static PyObject *%s;' % Naming.builtins_cname)
code.putln('static PyObject *%s;' % Naming.empty_tuple)
if Options.pre_import is not None:
code.putln('static PyObject *%s;' % Naming.preimport_cname)
code.putln('static int %s;' % Naming.lineno_cname)
......@@ -605,6 +608,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
type.declaration_code("")))
def generate_new_function(self, scope, code):
tp_slot = TypeSlots.ConstructorSlot("tp_new", '__new__')
slot_func = scope.mangle_internal("tp_new")
type = scope.parent_type
base_type = type.base_type
py_attrs = []
......@@ -621,9 +626,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
"%s;"
% scope.parent_type.declaration_code("p"))
if base_type:
tp_new = TypeSlots.get_base_slot_function(scope, tp_slot)
if tp_new is None:
tp_new = "%s->tp_new" % base_type.typeptr_cname
code.putln(
"PyObject *o = %s->tp_new(t, a, k);" %
base_type.typeptr_cname)
"PyObject *o = %s(t, a, k);" % tp_new)
else:
code.putln(
"PyObject *o = (*t->tp_alloc)(t, 0);")
......@@ -647,9 +654,13 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.put_init_var_to_py_none(entry, "p->%s")
entry = scope.lookup_here("__new__")
if entry:
if entry.trivial_signature:
cinit_args = "o, %s, NULL" % Naming.empty_tuple
else:
cinit_args = "o, a, k"
code.putln(
"if (%s(o, a, k) < 0) {" %
entry.func_cname)
"if (%s(%s) < 0) {" %
(entry.func_cname, cinit_args))
code.put_decref_clear("o", py_object_type);
code.putln(
"}")
......@@ -659,7 +670,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
"}")
def generate_dealloc_function(self, scope, code):
tp_slot = TypeSlots.ConstructorSlot("tp_dealloc", '__dealloc__')
slot_func = scope.mangle_internal("tp_dealloc")
base_type = scope.parent_type.base_type
if tp_slot.slot_code(scope) != slot_func:
return # never used
code.putln("")
code.putln(
"static void %s(PyObject *o) {"
......@@ -676,12 +691,14 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
for entry in py_attrs:
code.put_xdecref("p->%s" % entry.cname, entry.type)
if base_type:
tp_dealloc = TypeSlots.get_base_slot_function(scope, tp_slot)
if tp_dealloc is None:
tp_dealloc = "%s->tp_dealloc" % base_type.typeptr_cname
code.putln(
"%s->tp_dealloc(o);" %
base_type.typeptr_cname)
"%s(o);" % tp_dealloc)
else:
code.putln(
"(*o->ob_type->tp_free)(o);")
"(*o->ob_type->tp_free)(o);")
code.putln(
"}")
......@@ -709,26 +726,34 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
"}")
def generate_traverse_function(self, scope, code):
tp_slot = TypeSlots.GCDependentSlot("tp_traverse")
slot_func = scope.mangle_internal("tp_traverse")
base_type = scope.parent_type.base_type
if tp_slot.slot_code(scope) != slot_func:
return # never used
code.putln("")
code.putln(
"static int %s(PyObject *o, visitproc v, void *a) {"
% scope.mangle_internal("tp_traverse"))
% slot_func)
py_attrs = []
for entry in scope.var_entries:
if entry.type.is_pyobject and entry.name != "__weakref__":
py_attrs.append(entry)
if base_type or py_attrs:
code.putln(
"int e;")
code.putln("int e;")
if py_attrs:
self.generate_self_cast(scope, code)
if base_type:
code.putln("if (%s->tp_traverse) {" % base_type.typeptr_cname)
code.putln(
"e = %s->tp_traverse(o, v, a); if (e) return e;" %
base_type.typeptr_cname)
code.putln("}")
# want to call it explicitly if possible so inlining can be performed
static_call = TypeSlots.get_base_slot_function(scope, tp_slot)
if static_call:
code.putln("e = %s(o, v, a); if (e) return e;" % static_call)
else:
code.putln("if (%s->tp_traverse) {" % base_type.typeptr_cname)
code.putln(
"e = %s->tp_traverse(o, v, a); if (e) return e;" %
base_type.typeptr_cname)
code.putln("}")
for entry in py_attrs:
var_code = "p->%s" % entry.cname
code.putln(
......@@ -747,11 +772,13 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
"}")
def generate_clear_function(self, scope, code):
tp_slot = TypeSlots.GCDependentSlot("tp_clear")
slot_func = scope.mangle_internal("tp_clear")
base_type = scope.parent_type.base_type
if tp_slot.slot_code(scope) != slot_func:
return # never used
code.putln("")
code.putln(
"static int %s(PyObject *o) {"
% scope.mangle_internal("tp_clear"))
code.putln("static int %s(PyObject *o) {" % slot_func)
py_attrs = []
for entry in scope.var_entries:
if entry.type.is_pyobject and entry.name != "__weakref__":
......@@ -760,21 +787,26 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_self_cast(scope, code)
code.putln("PyObject* tmp;")
if base_type:
code.putln("if (%s->tp_clear) {" % base_type.typeptr_cname)
code.putln(
"%s->tp_clear(o);" %
base_type.typeptr_cname)
code.putln("}")
# want to call it explicitly if possible so inlining can be performed
static_call = TypeSlots.get_base_slot_function(scope, tp_slot)
if static_call:
code.putln("%s(o);" % static_call)
else:
code.putln("if (%s->tp_clear) {" % base_type.typeptr_cname)
code.putln("%s->tp_clear(o);" % base_type.typeptr_cname)
code.putln("}")
for entry in py_attrs:
name = "p->%s" % entry.cname
if entry.type.is_extension_type:
name = "((PyObject*)%s)" % name
code.putln("tmp = %s;" % name)
code.put_init_to_py_none(name, entry.type)
code.put_init_to_py_none(name, PyrexTypes.py_object_type)
code.putln("Py_XDECREF(tmp);")
code.putln(
"return 0;")
code.putln(
"}")
def generate_getitem_int_function(self, scope, code):
# This function is put into the sq_item slot when
# a __getitem__ method is present. It converts its
......@@ -1203,6 +1235,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
"};")
def generate_interned_name_table(self, env, code):
code.mark_pos(None)
items = env.intern_map.items()
if items:
items.sort()
......@@ -1268,6 +1301,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if Options.cache_builtins:
code.putln("/*--- Builtin init code ---*/")
self.generate_builtin_init_code(env, code)
code.putln("%s = PyTuple_New(0); %s" % (Naming.empty_tuple, code.error_goto_if_null(Naming.empty_tuple, self.pos)));
code.putln("/*--- Global init code ---*/")
self.generate_global_init_code(env, code)
......@@ -1287,6 +1322,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_type_import_code_for_module(module, env, code)
code.putln("/*--- Execution code ---*/")
code.mark_pos(None)
self.body.generate_execution_code(code)
if Options.generate_cleanup_code:
......@@ -1320,6 +1356,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("/*--- Builtin cleanup code ---*/")
for entry in env.cached_builtins:
code.put_var_decref_clear(entry)
code.putln("Py_DECREF(%s); %s = 0;" % (Naming.empty_tuple, Naming.empty_tuple));
code.putln("/*--- Intern cleanup code ---*/")
for entry in env.pynum_entries:
code.put_var_decref_clear(entry)
......
......@@ -58,6 +58,8 @@ vtabslot_cname = pyrex_prefix + "vtab"
c_api_tab_cname = pyrex_prefix + "c_api_tab"
gilstate_cname = pyrex_prefix + "state"
skip_dispatch_cname = pyrex_prefix + "skip_dispatch"
empty_tuple = pyrex_prefix + "empty_tuple"
extern_c_macro = pyrex_prefix.upper() + "EXTERN_C"
......
......@@ -88,8 +88,6 @@ class Node:
# mro does the wrong thing
if isinstance(self, BlockNode):
self.body.annotate(code)
else:
print "skipping", self
class BlockNode:
......@@ -985,6 +983,8 @@ class DefNode(FuncDefNode):
elif len(self.args) == 2:
if self.args[1].default is None and not self.args[1].kw_only:
self.entry.signature = TypeSlots.ibinaryfunc
elif self.entry.is_special:
self.entry.trivial_signature = len(self.args) == 1 and not (self.star_arg or self.starstar_arg)
sig = self.entry.signature
nfixed = sig.num_fixed_args()
for i in range(nfixed):
......@@ -1363,12 +1363,13 @@ class DefNode(FuncDefNode):
def generate_arg_conversion_from_pyobject(self, arg, code):
new_type = arg.type
func = new_type.from_py_function
# copied from CoerceFromPyTypeNode
if func:
code.putln("%s = %s(%s); %s" % (
arg.entry.cname,
func,
arg.hdr_cname,
code.error_goto_if_PyErr(arg.pos)))
code.error_goto_if(new_type.error_condition(arg.entry.cname), arg.pos)))
else:
error(arg.pos,
"Cannot convert Python object argument to type '%s'"
......
......@@ -21,7 +21,7 @@ incref_local_binop = 0
# 0: None, 1+: interned objects, 2+: cdef globals, 3+: types objects
# Mostly for reducing noise for Valgrind, only executes at process exit
# (when all memory will be reclaimed anyways).
generate_cleanup_code = 3
generate_cleanup_code = 0
annotate = 0
......
......@@ -302,6 +302,19 @@ class CType(PyrexType):
from_py_function = None
exception_value = None
exception_check = 1
def error_condition(self, result_code):
conds = []
if self.is_string:
conds.append("(!%s)" % result_code)
elif self.exception_value is not None:
conds.append("(%s == (%s)%s)" % (result_code, self.sign_and_name(), self.exception_value))
if self.exception_check:
conds.append("PyErr_Occurred()")
if len(conds) > 0:
return " && ".join(conds)
else:
return 0
class CVoidType(CType):
......@@ -432,7 +445,7 @@ class CUIntType(CIntType):
to_py_function = "PyLong_FromUnsignedLong"
from_py_function = "PyInt_AsUnsignedLongMask"
exception_value = None
exception_value = -1
class CULongType(CUIntType):
......
......@@ -184,6 +184,7 @@ class Scope:
self.pow_function_used = 0
self.string_to_entry = {}
self.num_to_entry = {}
self.obj_to_entry = {}
self.pystring_entries = []
def __str__(self):
......@@ -469,6 +470,27 @@ class Scope:
genv.num_to_entry[value] = entry
genv.pynum_entries.append(entry)
return entry
def add_py_obj(self, obj, c_prefix=''):
obj.check_const()
cname = self.new_const_cname(c_prefix)
entry = Entry("", cname, py_object_type, init = value)
entry.used = 1
entry.is_interned = 1
self.const_entries.append(entry)
self.interned_objs.append(entry)
return entry
def get_py_obj(self, obj, c_prefix=''):
# Get entry for a generic constant. Returns an existing
# one if possible, otherwise creates a new one.
genv = self.global_scope()
entry = genv.obj_to_entry.get(obj)
if not entry:
entry = genv.add_py_num(obj, c_prefix)
genv.obj_to_entry[obj] = entry
return entry
def new_const_cname(self):
# Create a new globally-unique name for a constant.
......@@ -675,6 +697,7 @@ class ModuleScope(Scope):
self.intern_map = {}
self.interned_names = []
self.interned_nums = []
self.interned_objs = []
self.all_pystring_entries = []
self.types_imported = {}
self.pynum_entries = []
......@@ -813,11 +836,11 @@ class ModuleScope(Scope):
self.default_entries.append(entry)
return entry
def new_const_cname(self):
def new_const_cname(self, prefix=''):
# Create a new globally-unique name for a constant.
n = self.const_counter
self.const_counter = n + 1
return "%s%d" % (Naming.const_prefix, n)
return "%s%s_%d" % (Naming.const_prefix, prefix, n)
def use_utility_code(self, new_code):
# Add string to list of utility code to be included,
......
......@@ -228,7 +228,31 @@ class GCDependentSlot(InternalMethodSlot):
# functions are defined in the same module
parent_type_scope = scope.parent_type.base_type.scope
if scope.parent_scope is parent_type_scope.parent_scope:
return self.slot_code(parent_type_scope)
entry = scope.parent_scope.lookup_here(scope.parent_type.base_type.name)
if entry.visibility != 'extern':
return self.slot_code(parent_type_scope)
return InternalMethodSlot.slot_code(self, scope)
class ConstructorSlot(InternalMethodSlot):
# Descriptor for tp_new and tp_dealloc.
def __init__(self, slot_name, method):
InternalMethodSlot.__init__(self, slot_name)
self.method = method
def slot_code(self, scope):
if scope.parent_type.base_type \
and not scope.has_pyobject_attrs \
and not scope.lookup_here(self.method):
# if the type does not have object attributes, it can
# delegate GC methods to its parent - iff the parent
# functions are defined in the same module
parent_type_scope = scope.parent_type.base_type.scope
if scope.parent_scope is parent_type_scope.parent_scope:
entry = scope.parent_scope.lookup_here(scope.parent_type.base_type.name)
if entry.visibility != 'extern':
return self.slot_code(parent_type_scope)
return InternalMethodSlot.slot_code(self, scope)
......@@ -373,6 +397,19 @@ def get_property_accessor_signature(name):
# Return signature of accessor for an extension type
# property, else None.
return property_accessor_signatures.get(name)
def get_base_slot_function(scope, slot):
# Returns the function implementing this slot in the baseclass.
# This is useful for enabling the compiler to optimize calls
# that recursively climb the class hierarchy.
base_type = scope.parent_type.base_type
if scope.parent_scope is base_type.scope.parent_scope:
parent_slot = slot.slot_code(base_type.scope)
if parent_slot != '0':
entry = scope.parent_scope.lookup_here(scope.parent_type.base_type.name)
if entry.visibility != 'extern':
return parent_slot
return None
#------------------------------------------------------------------------------------------
#
......@@ -558,7 +595,7 @@ PyBufferProcs = (
#------------------------------------------------------------------------------------------
slot_table = (
InternalMethodSlot("tp_dealloc"),
ConstructorSlot("tp_dealloc", '__dealloc__'),
EmptySlot("tp_print"), #MethodSlot(printfunc, "tp_print", "__print__"),
EmptySlot("tp_getattr"),
EmptySlot("tp_setattr"),
......
version = '0.9.6.10b'
version = '0.9.6.11'
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