# Pyrex - Code output module
#
+import re
import codecs
import Naming
import Options
-from Cython.Utils import open_new_file, open_source_file
+import StringEncoding
+from Cython import Utils
from PyrexTypes import py_object_type, typecast
from TypeSlots import method_coexist
from Scanning import SourceDescriptor
from Cython.StringIOTree import StringIOTree
-import DebugFlags
try:
set
except NameError:
self.temps_used_type = {} # name -> (type, manage_ref)
self.temp_counter = 0
+ # labels
+
def new_label(self, name=None):
n = self.label_counter
self.label_counter = n + 1
def label_used(self, lbl):
return lbl in self.labels_used
+ # temp handling
+
def allocate_temp(self, type, manage_ref):
"""
Allocates a temporary (which may create a new one or get a previously
if manage_ref
for cname in freelist]
+
+class IntConst(object):
+ """Global info about a Python integer constant held by GlobalState.
+ """
+ # cname string
+ # value int
+ # is_long boolean
+
+ def __init__(self, cname, value, is_long):
+ self.cname = cname
+ self.value = value
+ self.is_long = is_long
+
+possible_identifier = re.compile(ur"(?![0-9])\w+$", re.U).match
+nice_identifier = re.compile('^[a-zA-Z0-0_]+$').match
+
+class StringConst(object):
+ """Global info about a C string constant held by GlobalState.
+ """
+ # cname string
+ # text EncodedString or BytesLiteral
+ # py_strings {(identifier, encoding) : PyStringConst}
+
+ def __init__(self, cname, text, byte_string):
+ self.cname = cname
+ self.text = text
+ self.escaped_value = StringEncoding.escape_byte_string(byte_string)
+ self.py_strings = None
+
+ def get_py_string_const(self, encoding, identifier=None):
+ py_strings = self.py_strings
+ text = self.text
+ if encoding is not None:
+ encoding = encoding.upper()
+
+ key = (bool(identifier), encoding)
+ if py_strings is not None and key in py_strings:
+ py_string = py_strings[key]
+ else:
+ if py_strings is None:
+ self.py_strings = {}
+ is_unicode = encoding is None
+ intern = bool(identifier or (
+ identifier is None and possible_identifier(text)))
+ if intern:
+ prefix = Naming.interned_str_prefix
+ else:
+ prefix = Naming.py_const_prefix
+ pystring_cname = "%s%s%s_%s" % (
+ prefix,
+ is_unicode and 'u' or 'b',
+ identifier and 'i' or '',
+ self.cname[len(Naming.const_prefix):])
+
+ py_string = PyStringConst(
+ pystring_cname, is_unicode, bool(identifier), intern)
+ self.py_strings[key] = py_string
+
+ return py_string
+
+class PyStringConst(object):
+ """Global info about a Python string constant held by GlobalState.
+ """
+ # cname string
+ # unicode boolean
+ # intern boolean
+ # identifier boolean
+
+ def __init__(self, cname, is_unicode, identifier=False, intern=False):
+ self.cname = cname
+ self.identifier = identifier
+ self.unicode = is_unicode
+ self.intern = intern
+
+
class GlobalState(object):
# filename_table {string : int} for finding filename table indexes
# filename_list [string] filenames in filename table order
# check if constants are already added).
# In time, hopefully the literals etc. will be
# supplied directly instead.
+ #
+ # const_cname_counter int global counter for constant identifiers
+ #
# interned_strings
self.input_file_contents = {}
self.used_utility_code = set()
self.declared_cnames = {}
- self.pystring_table_needed = False
self.in_utility_code_generation = False
self.emit_linenums = emit_linenums
+ self.const_cname_counter = 1
+ self.string_const_index = {}
+ self.int_const_index = {}
+
def initwriters(self, rootwriter):
self.utilprotowriter = rootwriter.new_writer()
self.utildefwriter = rootwriter.new_writer()
self.cleanupwriter.putln("")
self.cleanupwriter.putln("static void __Pyx_CleanupGlobals(void) {")
- self.pystring_table.putln("")
- self.pystring_table.putln("static __Pyx_StringTabEntry %s[] = {" %
- Naming.stringtab_cname)
-
#
# Global constants, interned objects, etc.
#
def close_global_decls(self):
# This is called when it is known that no more global declarations will
# declared (but can be called before or after insert_XXX).
- if self.pystring_table_needed:
- self.pystring_table.putln("{0, 0, 0, 0, 0, 0}")
- self.pystring_table.putln("};")
- import Nodes
- self.use_utility_code(Nodes.init_string_tab_utility_code)
- self.initwriter.putln(
- "if (__Pyx_InitStrings(%s) < 0) %s;" % (
- Naming.stringtab_cname,
- self.initwriter.error_goto(self.module_pos)))
-
+ self.generate_const_declarations()
if Options.cache_builtins:
w = self.init_cached_builtins_writer
w.putln("return 0;")
w.exit_cfunc_scope()
def insert_initcode_into(self, code):
- if self.pystring_table_needed:
- code.insert(self.pystring_table)
+ code.insert(self.pystring_table)
if Options.cache_builtins:
code.insert(self.init_cached_builtins_writer)
code.insert(self.initwriter)
def put_pyobject_decl(self, entry):
self.decls_writer.putln("static PyObject *%s;" % entry.cname)
+ # constant handling at code generation time
+
+ def get_int_const(self, str_value, longness=False):
+ longness = bool(longness or Utils.long_literal(str_value))
+ try:
+ c = self.int_const_index[(str_value, longness)]
+ except KeyError:
+ c = self.new_int_const(str_value, longness)
+ return c
+
+ def get_string_const(self, text):
+ # return a C string constant, creating a new one if necessary
+ if text.is_unicode:
+ byte_string = text.utf8encode()
+ else:
+ byte_string = text.byteencode()
+ try:
+ c = self.string_const_index[byte_string]
+ except KeyError:
+ c = self.new_string_const(text, byte_string)
+ return c
+
+ def get_py_string_const(self, text, identifier=None):
+ # return a Python string constant, creating a new one if necessary
+ c_string = self.get_string_const(text)
+ py_string = c_string.get_py_string_const(text.encoding, identifier)
+ return py_string
+
+ def new_string_const(self, text, byte_string):
+ cname = self.new_string_const_cname(text)
+ c = StringConst(cname, text, byte_string)
+ self.string_const_index[byte_string] = c
+ return c
+
+ def new_int_const(self, value, longness):
+ cname = self.new_int_const_cname(value, longness)
+ c = IntConst(cname, value, longness)
+ self.int_const_index[(value, longness)] = c
+ return c
+
+ def new_string_const_cname(self, value, intern=None):
+ # Create a new globally-unique nice name for a C string constant.
+ if len(value) < 20 and nice_identifier(value):
+ return "%s%s" % (Naming.const_prefix, value)
+ else:
+ return self.new_const_cname()
+
+ def new_int_const_cname(self, value, longness):
+ if longness:
+ value += 'L'
+ cname = "%s%s" % (Naming.interned_num_prefix, value)
+ cname = cname.replace('-', 'neg_').replace('.','_')
+ return cname
+
+ def new_const_cname(self, prefix=''):
+ n = self.const_cname_counter
+ self.const_cname_counter += 1
+ return "%s%s%d" % (Naming.const_prefix, prefix, n)
+
+ def add_cached_builtin_decl(self, entry):
+ if Options.cache_builtins:
+ if self.should_declare(entry.cname, entry):
+ interned_cname = self.get_py_string_const(entry.name, True).cname
+ self.put_pyobject_decl(entry)
+ self.init_cached_builtins_writer.putln('%s = __Pyx_GetName(%s, %s); if (!%s) %s' % (
+ entry.cname,
+ Naming.builtins_cname,
+ interned_cname,
+ entry.cname,
+ self.init_cached_builtins_writer.error_goto(entry.pos)))
+
+ def generate_const_declarations(self):
+ self.generate_string_constants()
+ self.generate_int_constants()
+
+ def generate_string_constants(self):
+ c_consts = [ (len(c.cname), c.cname, c)
+ for c in self.string_const_index.itervalues() ]
+ c_consts.sort()
+ py_strings = []
+ for _, cname, c in c_consts:
+ self.decls_writer.putln('static char %s[] = "%s";' % (
+ cname, c.escaped_value))
+ if c.py_strings is not None:
+ for py_string in c.py_strings.itervalues():
+ py_strings.append((c.cname, len(py_string.cname), py_string))
+
+ if py_strings:
+ import Nodes
+ self.use_utility_code(Nodes.init_string_tab_utility_code)
+
+ py_strings.sort()
+ self.pystring_table.putln("")
+ self.pystring_table.putln("static __Pyx_StringTabEntry %s[] = {" %
+ Naming.stringtab_cname)
+ for c_cname, _, py_string in py_strings:
+ self.decls_writer.putln(
+ "static PyObject *%s;" % py_string.cname)
+ self.pystring_table.putln(
+ "{&%s, %s, sizeof(%s), %d, %d, %d}," % (
+ py_string.cname,
+ c_cname,
+ c_cname,
+ py_string.unicode,
+ py_string.intern,
+ py_string.identifier
+ ))
+ self.pystring_table.putln("{0, 0, 0, 0, 0, 0}")
+ self.pystring_table.putln("};")
+
+ self.initwriter.putln(
+ "if (__Pyx_InitStrings(%s) < 0) %s;" % (
+ Naming.stringtab_cname,
+ self.initwriter.error_goto(self.module_pos)))
+
+ def generate_int_constants(self):
+ consts = [ (len(c.value), c.value, c.is_long, c)
+ for c in self.int_const_index.itervalues() ]
+ consts.sort()
+ for _, value, longness, c in consts:
+ cname = c.cname
+ self.decls_writer.putln("static PyObject *%s;" % cname)
+ if longness:
+ function = '%s = PyLong_FromString((char *)"%s", 0, 0); %s;'
+ else:
+ function = "%s = PyInt_FromLong(%s); %s;"
+ self.initwriter.putln(function % (
+ cname,
+ value,
+ self.initwriter.error_goto_if_null(cname, self.module_pos)))
+
# The functions below are there in a transition phase only
# and will be deprecated. They are called from Nodes.BlockNode.
# The copy&paste duplication is intentional in order to be able
self.declared_cnames[cname] = entry
return True
- def add_const_definition(self, entry):
- if self.should_declare(entry.cname, entry):
- self.decls_writer.put_var_declaration(entry, static = 1)
-
- def add_interned_string_decl(self, entry):
- if self.should_declare(entry.cname, entry):
- self.decls_writer.put_var_declaration(entry, static = 1)
- self.add_py_string_decl(entry)
-
- def add_py_string_decl(self, entry):
- if self.should_declare(entry.pystring_cname, entry):
- self.decls_writer.putln("static PyObject *%s;" % entry.pystring_cname)
- self.pystring_table_needed = True
- self.pystring_table.putln("{&%s, %s, sizeof(%s), %d, %d, %d}," % (
- entry.pystring_cname,
- entry.cname,
- entry.cname,
- entry.type.is_unicode,
- entry.is_interned,
- entry.is_identifier
- ))
-
- def add_interned_num_decl(self, entry):
- if self.should_declare(entry.cname, entry):
- if entry.init[-1] == "L":
- self.initwriter.putln('%s = PyLong_FromString((char *)"%s", 0, 0); %s;' % (
- entry.cname,
- entry.init[:-1], # strip 'L' for Py3 compatibility
- self.initwriter.error_goto_if_null(entry.cname, self.module_pos)))
- else:
- self.initwriter.putln("%s = PyInt_FromLong(%s); %s;" % (
- entry.cname,
- entry.init,
- self.initwriter.error_goto_if_null(entry.cname, self.module_pos)))
-
- self.put_pyobject_decl(entry)
-
- def add_cached_builtin_decl(self, entry):
- if Options.cache_builtins:
- if self.should_declare(entry.cname, entry):
- self.put_pyobject_decl(entry)
- self.init_cached_builtins_writer.putln('%s = __Pyx_GetName(%s, %s); if (!%s) %s' % (
- entry.cname,
- Naming.builtins_cname,
- entry.interned_cname,
- entry.cname,
- self.init_cached_builtins_writer.error_goto(entry.pos)))
-
-
#
# File name state
#
setattr(self.funcstate, name, value)
return property(get, set)
+
class CCodeWriter(object):
"""
Utility class to output C code.
def exit_cfunc_scope(self):
self.funcstate = None
+ # constant handling
+
+ def get_py_num(self, str_value, longness):
+ return self.globalstate.get_int_const(str_value, longness).cname
+
+ def get_string_const(self, text):
+ return self.globalstate.get_string_const(text).cname
+
+ def get_py_string_const(self, text, identifier=None):
+ return self.globalstate.get_py_string_const(text, identifier).cname
+
+ def intern(self, text):
+ return self.get_py_string_const(text)
+
+ def intern_identifier(self, text):
+ return self.get_py_string_const(text, True)
+
+ # code generation
+
def putln(self, code = ""):
if self.marker and self.bol:
self.emit_marker()
else:
self.putln("%s;" % decl)
+ def put_h_guard(self, guard):
+ self.putln("#ifndef %s" % guard)
+ self.putln("#define %s" % guard)
+
+ def unlikely(self, cond):
+ if Options.gcc_branch_hints:
+ return 'unlikely(%s)' % cond
+ else:
+ return cond
+
+ # Python objects and reference counting
+
def entry_as_pyobject(self, entry):
type = entry.type
if (not entry.is_self_arg and not entry.type.is_complete()
doc_code,
term))
+ # error handling
+
def put_error_if_neg(self, pos, value):
# return self.putln("if (unlikely(%s < 0)) %s" % (value, self.error_goto(pos))) # TODO this path is almost _never_ taken, yet this macro makes is slower!
return self.putln("if (%s < 0) %s" % (value, self.error_goto(pos)))
- def put_h_guard(self, guard):
- self.putln("#ifndef %s" % guard)
- self.putln("#define %s" % guard)
-
- def unlikely(self, cond):
- if Options.gcc_branch_hints:
- return 'unlikely(%s)' % cond
- else:
- return cond
-
def set_error_info(self, pos):
if Options.c_line_in_traceback:
cinfo = " %s = %s;" % (Naming.clineno_cname, Naming.line_c_macro)
# level int indentation level
def __init__(self, outfile_name):
- self.f = open_new_file(outfile_name)
+ self.f = Utils.open_new_file(outfile_name)
self.level = 0
def putln(self, code):
src_type = self.type
src_is_py_type = src_type.is_pyobject
dst_is_py_type = dst_type.is_pyobject
-
+
if dst_type.is_pyobject:
if not src.type.is_pyobject:
src = CoerceToPyTypeNode(src, env)
# Arrange for a Python version of the number to be pre-allocated
# when coercing to a Python type.
if dst_type.is_pyobject:
- self.entry = env.get_py_num(self.value, self.longness)
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,
self.type = PyrexTypes.c_bint_type
return self
- def calculate_result_code(self):
+ def generate_evaluation_code(self, code):
if self.type.is_pyobject:
- return self.entry.cname
+ self.result_code = code.get_py_num(self.value, self.longness)
else:
- return str(self.value) + self.unsigned + self.longness
+ self.result_code = str(self.value) + self.unsigned + self.longness
+
+ def calculate_result_code(self):
+ return self.result_code
def calculate_constant_result(self):
self.constant_result = int(self.value, 0)
class StringNode(ConstNode):
- # entry Symtab.Entry
-
type = PyrexTypes.c_char_ptr_type
def compile_time_value(self, denv):
return self.value
-
- def analyse_types(self, env):
- self.entry = env.add_string_const(self.value)
-
+
def analyse_as_type(self, env):
type = PyrexTypes.parse_basic_type(self.value)
if type is not None:
sizeof_node.analyse_types(env)
if isinstance(sizeof_node, SizeofTypeNode):
return sizeof_node.arg_type
-
+
def coerce_to(self, dst_type, env):
if dst_type == PyrexTypes.c_char_ptr_type:
self.type = PyrexTypes.c_char_ptr_type
return self
-
+
if dst_type.is_int:
- if not self.type.is_pyobject and len(self.entry.init) == 1:
+ if not self.type.is_pyobject and len(self.value) == 1:
return CharNode(self.pos, value=self.value)
else:
- error(self.pos, "Only coerce single-character ascii strings can be used as ints.")
+ error(self.pos, "Only single-character byte strings can be coerced into ints.")
return self
# Arrange for a Python version of the string to be pre-allocated
# when coercing to a Python type.
return ConstNode.coerce_to(node, dst_type, env)
def as_py_string_node(self, env):
- # Return a new StringNode with the same entry as this node
+ # Return a new StringNode with the same value as this node
# but whose type is a Python type instead of a C type.
- entry = self.entry
- env.add_py_string(entry)
- return StringNode(self.pos, value = self.value, entry = entry, type = py_object_type)
-
- def calculate_result_code(self):
+ return StringNode(self.pos, value = self.value, type = py_object_type)
+
+ def generate_evaluation_code(self, code):
if self.type.is_pyobject:
- return self.entry.pystring_cname
+ self.result_code = code.get_py_string_const(self.value)
else:
- return self.entry.cname
+ self.result_code = code.get_string_const(self.value)
+
+ def calculate_result_code(self):
+ return self.result_code
class UnicodeNode(PyConstNode):
- # entry Symtab.Entry
-
type = unicode_type
-
- def analyse_types(self, env):
- self.entry = env.add_string_const(self.value)
- env.add_py_string(self.entry)
-
- def calculate_result_code(self):
- return self.entry.pystring_cname
- def _coerce_to(self, dst_type, env):
- if not dst_type.is_pyobject:
- node = StringNode(self.pos, entry = entry, type = py_object_type)
- return ConstNode.coerce_to(node, dst_type, env)
+ def coerce_to(self, dst_type, env):
+ if dst_type.is_pyobject:
+ return self
else:
+ error(self.pos, "Unicode objects do not support coercion to C types.")
return self
- # 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.
+
+ def generate_evaluation_code(self, code):
+ if self.type.is_pyobject:
+ self.result_code = code.get_py_string_const(self.value)
+ else:
+ self.result_code = code.get_string_const(self.value)
+
+ def calculate_result_code(self):
+ return self.result_code
def compile_time_value(self, env):
return self.value
# keyword arguments in a call, or for imported names
type = PyrexTypes.py_object_type
- def analyse_types(self, env):
- self.cname = env.intern_identifier(self.value)
+ def generate_evaluation_code(self, code):
+ if self.type.is_pyobject:
+ self.result_code = code.get_py_string_const(self.value, True)
+ else:
+ self.result_code = code.get_string_const(self.value)
def calculate_result_code(self):
- return self.cname
+ return self.result_code
class LongNode(AtomicNewTempExprNode):
# name string Python name of the variable
#
# entry Entry Symbol table entry
- # interned_cname string
is_name = True
is_cython_module = False
entry = self.entry
type = entry.type
self.type = type
- if entry.is_pyglobal or entry.is_builtin:
- assert type.is_pyobject, "Python global or builtin not a Python object"
- self.interned_cname = self.entry.interned_cname = \
- env.intern_identifier(self.entry.name)
def check_identifier_kind(self):
#print "NameNode.check_identifier_kind:", self.entry.name ###
if entry.is_builtin and Options.cache_builtins:
return # Lookup already cached
elif entry.is_pyglobal or entry.is_builtin:
+ assert entry.type.is_pyobject, "Python global or builtin not a Python object"
+ interned_cname = code.intern_identifier(self.entry.name)
if entry.is_builtin:
namespace = Naming.builtins_cname
else: # entry.is_pyglobal
'%s = __Pyx_GetName(%s, %s); %s' % (
self.result(),
namespace,
- self.interned_cname,
+ interned_cname,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
# 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:
+ assert entry.type.is_pyobject, "Python global or builtin not a Python object"
+ interned_cname = code.intern_identifier(self.entry.name)
namespace = self.entry.scope.namespace_cname
if entry.is_member:
# if the entry is a member we have to cheat: SetAttr does not work
code.put_error_if_neg(self.pos,
'PyDict_SetItem(%s->tp_dict, %s, %s)' % (
namespace,
- self.interned_cname,
+ interned_cname,
rhs.py_result()))
rhs.generate_disposal_code(code)
rhs.free_temps(code)
code.put_error_if_neg(self.pos,
'PyObject_SetAttr(%s, %s, %s)' % (
namespace,
- self.interned_cname,
+ interned_cname,
rhs.py_result()))
if debug_disposal_code:
print("NameNode.generate_assignment_code:")
# member string C name of struct member
# is_called boolean Function call is being done on result
# entry Entry Symbol table entry of attribute
- # interned_attr_cname string C name of interned attribute name
is_attribute = 1
subexprs = ['obj']
if obj_type.is_pyobject:
self.type = py_object_type
self.is_py_attr = 1
- self.interned_attr_cname = env.intern_identifier(self.attribute)
else:
if not obj_type.is_error:
error(self.pos,
return "%s%s%s" % (obj_code, self.op, self.member)
def generate_result_code(self, code):
+ interned_attr_cname = code.intern_identifier(self.attribute)
if self.is_py_attr:
code.putln(
'%s = PyObject_GetAttr(%s, %s); %s' % (
self.result(),
self.obj.py_result(),
- self.interned_attr_cname,
+ interned_attr_cname,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
else:
self.put_nonecheck(code)
def generate_assignment_code(self, rhs, code):
+ interned_attr_cname = code.intern_identifier(self.attribute)
self.obj.generate_evaluation_code(code)
if self.is_py_attr:
code.put_error_if_neg(self.pos,
'PyObject_SetAttr(%s, %s, %s)' % (
self.obj.py_result(),
- self.interned_attr_cname,
+ interned_attr_cname,
rhs.py_result()))
rhs.generate_disposal_code(code)
rhs.free_temps(code)
self.obj.free_temps(code)
def generate_deletion_code(self, code):
+ interned_attr_cname = code.intern_identifier(self.attribute)
self.obj.generate_evaluation_code(code)
if self.is_py_attr:
code.put_error_if_neg(self.pos,
'PyObject_DelAttr(%s, %s)' % (
self.obj.py_result(),
- self.interned_attr_cname))
+ interned_attr_cname))
else:
error(self.pos, "Cannot delete C attribute of extension type")
self.obj.generate_disposal_code(code)
# a name, tuple of bases and class dictionary.
#
# name EncodedString Name of the class
- # cname string Class name as a Python string
# bases ExprNode Base class tuple
# dict ExprNode Class dict (not owned by this node)
# doc ExprNode or None Doc string
subexprs = ['bases', 'doc']
def analyse_types(self, env):
- self.cname = env.intern_identifier(self.name)
self.bases.analyse_types(env)
if self.doc:
self.doc.analyse_types(env)
gil_message = "Constructing Python class"
def generate_result_code(self, code):
+ cname = code.intern_identifier(self.name)
if self.doc:
code.put_error_if_neg(self.pos,
'PyDict_SetItemString(%s, "__doc__", %s)' % (
self.result(),
self.bases.py_result(),
self.dict.py_result(),
- self.cname,
+ cname,
self.module_name,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
self.generate_py_string_decls(env, code)
code.globalstate.insert_global_var_declarations_into(code)
-
+
self.generate_cached_builtins_decls(env, code)
self.body.generate_function_definitions(env, code)
code.mark_pos(None)
code.putln('static const char * %s= %s;' % (Naming.cfilenm_cname, Naming.file_c_macro))
code.putln('static const char *%s;' % Naming.filename_cname)
code.putln('static const char **%s;' % Naming.filetable_cname)
- if env.doc:
- docstr = env.doc
- if not isinstance(docstr, str):
- docstr = docstr.utf8encode()
- code.putln('')
- code.putln('static char %s[] = "%s";' % (
- env.doc_cname, escape_byte_string(docstr)))
env.use_utility_code(streq_utility_code)
"static struct PyGetSetDef %s[] = {" %
env.getset_table_cname)
for entry in env.property_entries:
+ if entry.doc:
+ doc_code = "__Pyx_DOCSTR(%s)" % code.get_string_const(entry.doc)
+ else:
+ doc_code = "0"
code.putln(
'{(char *)"%s", %s, %s, %s, 0},' % (
entry.name,
entry.getter_cname or "0",
entry.setter_cname or "0",
- entry.doc_cname or "0"))
+ doc_code))
code.putln(
"{0, 0, 0, 0, 0}")
code.putln(
def generate_pymoduledef_struct(self, env, code):
if env.doc:
- doc = "__Pyx_DOCSTR(%s)" % env.doc_cname
+ doc = "__Pyx_DOCSTR(%s)" % code.get_string_const(env.doc)
else:
doc = "0"
code.putln("")
# Generate code to create the module object and
# install the builtins.
if env.doc:
- doc = env.doc_cname
+ doc = "__Pyx_DOCSTR(%s)" % code.get_string_const(env.doc)
else:
doc = "0"
code.putln("#if PY_MAJOR_VERSION < 3")
pymethdef_prefix = pyrex_prefix + "mdef_"
methtab_prefix = pyrex_prefix + "methods_"
memtab_prefix = pyrex_prefix + "members_"
+interned_str_prefix = pyrex_prefix + "n_"
interned_num_prefix = pyrex_prefix + "int_"
objstruct_prefix = pyrex_prefix + "obj_"
typeptr_prefix = pyrex_prefix + "ptype_"
self.dimension.analyse_const_expression(env)
if not self.dimension.type.is_int:
error(self.dimension.pos, "Array dimension not integer")
- size = self.dimension.result()
+ size = self.dimension.compile_time_value(env)
try:
size = int(size)
except ValueError:
"Exception value must be a Python exception or cdef function with no arguments.")
exc_val = self.exception_value
else:
- exc_val = self.exception_value.result()
+ exc_val = self.exception_value.compile_time_value(env)
if not return_type.assignable_from(self.exception_value.type):
error(self.exception_value.pos,
"Exception value incompatible with function return type")
else:
return self.name_declarator, self.type
+ def prepare_default_result_code(self, code):
+ if self.default:
+ if self.default.is_literal:
+ # FIXME: IS IT OK TO CALL THIS HERE???
+ self.default.generate_evaluation_code(code)
+ self.default_result_code = self.default.result()
+ if self.default.type != self.type and not self.type.is_int:
+ self.default_result_code = self.type.cast_code(self.default_result_code)
+ else:
+ self.default_result_code = self.default_entry.cname
+
def annotate(self, code):
if self.default:
self.default.annotate(code)
if not self.value.type.is_int:
self.value = self.value.coerce_to(PyrexTypes.c_int_type, env)
self.value.analyse_const_expression(env)
- value = self.value.result()
+ value = self.value.compile_time_value(env)
else:
value = self.name
entry = env.declare_const(self.name, enum_entry.type,
arg.default = arg.default.coerce_to(arg.type, genv)
if arg.default.is_literal:
arg.default_entry = arg.default
- arg.default_result_code = arg.default.calculate_result_code()
- if arg.default.type != arg.type and not arg.type.is_int:
- arg.default_result_code = arg.type.cast_code(arg.default_result_code)
else:
arg.default.allocate_temps(genv)
arg.default_entry = genv.add_default_value(arg.type)
if arg.type.is_pyobject:
arg.default_entry.init = 0
arg.default_entry.used = 1
- arg.default_result_code = arg.default_entry.cname
else:
error(arg.pos,
"This argument cannot have a default value")
arg.default = None
-
+
def need_gil_acquisition(self, lenv):
return 0
self.entry.as_variable = self.py_func.entry
# Reset scope entry the above cfunction
env.entries[name] = self.entry
- self.py_func.interned_attr_cname = env.intern_identifier(
- self.py_func.entry.name)
if not env.is_module_scope or Options.lookup_module_cpdef:
self.override = OverrideCheckNode(self.pos, py_func = self.py_func)
self.body = StatListNode(self.pos, stats=[self.override, self.body])
def generate_argument_declarations(self, env, code):
for arg in self.args:
if arg.default:
- code.putln('%s = %s;' % (arg.type.declaration_code(arg.cname), arg.default_result_code))
+ arg.prepare_default_result_code(code)
+ code.putln('%s = %s;' % (
+ arg.type.declaration_code(arg.cname), arg.default_result_code))
def generate_keyword_list(self, code):
pass
arg.entry = self.declare_argument(env, arg)
arg.entry.used = 1
arg.entry.is_self_arg = arg.is_self_arg
- if not arg.is_self_arg:
- arg.name_entry = env.get_string_const(
- arg.name, identifier = True)
- env.add_py_string(arg.name_entry, identifier = True)
if arg.hdr_type:
if arg.is_self_arg or \
(arg.type.is_extension_type and not arg.hdr_type.is_extension_type):
code.putln("PyObject *%s = 0;" % arg.hdr_cname)
else:
code.put_var_declaration(arg.entry)
+ if arg.default:
+ arg.prepare_default_result_code(code)
def generate_keyword_list(self, code):
if self.signature_has_generic_args() and \
Naming.pykwdlist_cname)
for arg in self.args:
if arg.is_generic:
- code.put('&%s,' % arg.name_entry.pystring_cname)
+ pystring_cname = code.intern_identifier(arg.name)
+ code.put('&%s,' % pystring_cname)
code.putln("0};")
def generate_argument_parsing_code(self, env, code):
code.putln('} else {')
for i, arg in enumerate(kw_only_args):
if not arg.default:
+ pystring_cname = code.intern_identifier(arg.name)
# required keyword-only argument missing
code.put('__Pyx_RaiseKeywordRequired("%s", %s); ' % (
self.name.utf8encode(),
- arg.name_entry.pystring_cname))
+ pystring_cname))
code.putln(code.error_goto(self.pos))
break
code.putln('default:')
else:
code.putln('case %2d:' % i)
+ pystring_cname = code.intern_identifier(arg.name)
if arg.default:
if arg.kw_only:
# handled separately below
continue
code.putln('if (kw_args > %d) {' % num_required_args)
code.putln('PyObject* value = PyDict_GetItem(%s, %s);' % (
- Naming.kwds_cname, arg.name_entry.pystring_cname))
+ Naming.kwds_cname, pystring_cname))
code.putln('if (unlikely(value)) { values[%d] = value; kw_args--; }' % i)
code.putln('}')
else:
num_required_args -= 1
code.putln('values[%d] = PyDict_GetItem(%s, %s);' % (
- i, Naming.kwds_cname, arg.name_entry.pystring_cname))
+ i, Naming.kwds_cname, pystring_cname))
code.putln('if (likely(values[%d])) kw_args--;' % i);
if i < min_positional_args:
if i == 0:
elif arg.kw_only:
code.putln('else {')
code.put('__Pyx_RaiseKeywordRequired("%s", %s); ' %(
- self.name.utf8encode(), arg.name_entry.pystring_cname))
+ self.name.utf8encode(), pystring_cname))
code.putln(code.error_goto(self.pos))
code.putln('}')
if max_positional_args > 0:
code.putln('while (kw_args > 0) {')
code.putln('PyObject* value;')
for i, arg in optional_args:
+ pystring_cname = code.intern_identifier(arg.name)
code.putln(
'value = PyDict_GetItem(%s, %s);' % (
- Naming.kwds_cname, arg.name_entry.pystring_cname))
+ Naming.kwds_cname, pystring_cname))
code.putln(
'if (value) { values[%d] = value; if (!(--kw_args)) break; }' % i)
code.putln('break;')
self.body.analyse_expressions(env)
def generate_execution_code(self, code):
+ interned_attr_cname = code.intern_identifier(self.py_func.entry.name)
# Check to see if we are an extension type
if self.py_func.is_module_scope:
self_arg = "((PyObject *)%s)" % Naming.module_cname
code.putln("else if (unlikely(Py_TYPE(%s)->tp_dictoffset != 0)) {" % self_arg)
err = code.error_goto_if_null(self.func_node.result(), self.pos)
# need to get attribute manually--scope would return cdef method
- code.putln("%s = PyObject_GetAttr(%s, %s); %s" % (self.func_node.result(), self_arg, self.py_func.interned_attr_cname, err))
+ code.putln("%s = PyObject_GetAttr(%s, %s); %s" % (
+ self.func_node.result(), self_arg, interned_attr_cname, err))
code.put_gotref(self.func_node.py_result())
is_builtin_function_or_method = 'PyCFunction_Check(%s)' % self.func_node.result()
- is_overridden = '(PyCFunction_GET_FUNCTION(%s) != (void *)&%s)' % (self.func_node.result(), self.py_func.entry.func_cname)
+ is_overridden = '(PyCFunction_GET_FUNCTION(%s) != (void *)&%s)' % (
+ self.func_node.result(), 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('}')
def analyse_declarations(self, env):
entry = env.declare_property(self.name, self.doc, self.pos)
if entry:
- if self.doc and Options.docstrings:
- doc_entry = env.get_string_const(
- self.doc, identifier = False)
- entry.doc_cname = doc_entry.cname
self.body.analyse_declarations(entry.scope)
def analyse_expressions(self, env):
else:
coerced_item = self.item.coerce_to(target.type, env)
self.interned_items.append(
- (env.intern_identifier(name), target, coerced_item))
+ (name, target, coerced_item))
#target.release_target_temp(env) # was release_temp ?!?
self.module.release_temp(env)
self.item.release_temp(env)
Naming.import_star,
self.module.py_result(),
code.error_goto(self.pos)))
- for cname, target, coerced_item in self.interned_items:
+ for name, target, coerced_item in self.interned_items:
+ cname = code.intern_identifier(name)
code.putln(
'%s = PyObject_GetAttr(%s, %s); %s' % (
self.item.result(),
def __str__(self):
return "<%s %s>" % (self.__class__.__name__, self.qualified_name)
- def intern_identifier(self, name):
- return self.global_scope().intern_identifier(name)
-
def qualifying_scope(self):
return self.parent_scope
if entry and entry.is_type:
return entry.type
- def add_string_const(self, value, identifier = False):
- # Add an entry for a string constant.
- if identifier:
- cname = self.new_string_const_cname(value)
- else:
- cname = self.new_const_cname()
- if value.is_unicode:
- c_type = PyrexTypes.c_utf8_char_array_type
- value = value.utf8encode()
- else:
- c_type = PyrexTypes.c_char_array_type
- value = value.byteencode()
- entry = Entry("", cname, c_type, init = value)
- entry.used = 1
- self.const_entries.append(entry)
- return entry
-
- def get_string_const(self, value, identifier = False):
- # Get entry for string constant. Returns an existing
- # one if possible, otherwise creates a new one.
- genv = self.global_scope()
- if identifier:
- string_map = genv.identifier_to_entry
- else:
- string_map = genv.string_to_entry
- entry = string_map.get(value)
- if not entry:
- entry = self.add_string_const(value, identifier)
- entry.is_identifier = identifier
- string_map[value] = entry
- return entry
-
- def add_py_string(self, entry, identifier = None):
- # If not already done, allocate a C name for a Python version of
- # a string literal, and add it to the list of Python strings to
- # be created at module init time. If the string resembles a
- # Python identifier, it will be interned.
- if entry.pystring_cname:
- return
- value = entry.init
- entry.pystring_cname = Naming.py_const_prefix + entry.cname[len(Naming.const_prefix):]
- self.pystring_entries.append(entry)
- self.global_scope().all_pystring_entries.append(entry)
- if identifier or (identifier is None and possible_identifier(value)):
- entry.is_interned = 1
- self.global_scope().new_interned_string_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, py_object_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, longness):
- # Get entry for int constant. Returns an existing
- # one if possible, otherwise creates a new one.
- if longness or Utils.long_literal(value):
- value += "L"
- 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 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_string_const_cname(self, value):
- # Create a new globally-unique nice name for a string constant.
- if len(value) < 20 and nice_identifier(value):
- return "%s%s" % (Naming.const_prefix, value)
- else:
- return self.global_scope().new_const_cname()
-
- def new_const_cname(self):
- # Create a new globally-unique name for a constant.
- return self.global_scope().new_const_cname()
-
def allocate_temp(self, type):
# Allocate a temporary variable of the given type from the
# free list if available, otherwise create a new one.
entry.is_builtin = 1
return entry
- def intern_identifier(self, name):
- string_entry = self.get_string_const(name, identifier = True)
- self.add_py_string(string_entry, identifier = 1)
- return string_entry.pystring_cname
-
def find_module(self, module_name, pos):
# Find a module in the import namespace, interpreting
# relative imports relative to this module's parent.
# I keep it in for now. is_member should be enough
# later on
self.namespace_cname = "(PyObject *)%s" % self.parent_type.typeptr_cname
- entry.interned_cname = self.intern_identifier(name)
return entry
cdef int c2 = "te" # fails
cdef int cx = "test" # fails
+cdef int x1 = "\xFF" # works
+cdef int x2 = u"\xFF" # fails
+
+
_ERRORS = u"""
-2:14: Only coerce single-character ascii strings can be used as ints.
-3:14: Only coerce single-character ascii strings can be used as ints.
+2:14: Only single-character byte strings can be coerced into ints.
+3:14: Only single-character byte strings can be coerced into ints.
+6:14: Unicode objects do not support coercion to C types.
"""