Cython has moved to github.
cython-devel
view Cython/Compiler/Code.py @ 2158:50307880f9c1
almost complete refactoring of constant allocation to move it into the code generation phase
| author | Stefan Behnel <scoder@users.berlios.de> |
|---|---|
| date | Wed Mar 18 21:44:34 2009 +0100 (3 years ago) |
| parents | 519f0fb67849 |
| children | a77d63eb5225 |
line source
1 #
2 # Pyrex - Code output module
3 #
5 import re
6 import codecs
7 import Naming
8 import Options
9 import StringEncoding
10 from Cython import Utils
11 from PyrexTypes import py_object_type, typecast
12 from TypeSlots import method_coexist
13 from Scanning import SourceDescriptor
14 from Cython.StringIOTree import StringIOTree
15 try:
16 set
17 except NameError:
18 from sets import Set as set
19 import DebugFlags
21 class FunctionState(object):
22 # return_label string function return point label
23 # error_label string error catch point label
24 # continue_label string loop continue point label
25 # break_label string loop break point label
26 # return_from_error_cleanup_label string
27 # label_counter integer counter for naming labels
28 # in_try_finally boolean inside try of try...finally
29 # exc_vars (string * 3) exception variables for reraise, or None
31 # Not used for now, perhaps later
32 def __init__(self, owner, names_taken=set()):
33 self.names_taken = names_taken
34 self.owner = owner
36 self.error_label = None
37 self.label_counter = 0
38 self.labels_used = {}
39 self.return_label = self.new_label()
40 self.new_error_label()
41 self.continue_label = None
42 self.break_label = None
44 self.in_try_finally = 0
45 self.exc_vars = None
47 self.temps_allocated = [] # of (name, type, manage_ref)
48 self.temps_free = {} # (type, manage_ref) -> list of free vars with same type/managed status
49 self.temps_used_type = {} # name -> (type, manage_ref)
50 self.temp_counter = 0
52 # labels
54 def new_label(self, name=None):
55 n = self.label_counter
56 self.label_counter = n + 1
57 label = "%s%d" % (Naming.label_prefix, n)
58 if name is not None:
59 label += '_' + name
60 return label
62 def new_error_label(self):
63 old_err_lbl = self.error_label
64 self.error_label = self.new_label('error')
65 return old_err_lbl
67 def get_loop_labels(self):
68 return (
69 self.continue_label,
70 self.break_label)
72 def set_loop_labels(self, labels):
73 (self.continue_label,
74 self.break_label) = labels
76 def new_loop_labels(self):
77 old_labels = self.get_loop_labels()
78 self.set_loop_labels(
79 (self.new_label("continue"),
80 self.new_label("break")))
81 return old_labels
83 def get_all_labels(self):
84 return (
85 self.continue_label,
86 self.break_label,
87 self.return_label,
88 self.error_label)
90 def set_all_labels(self, labels):
91 (self.continue_label,
92 self.break_label,
93 self.return_label,
94 self.error_label) = labels
96 def all_new_labels(self):
97 old_labels = self.get_all_labels()
98 new_labels = []
99 for old_label in old_labels:
100 if old_label:
101 new_labels.append(self.new_label())
102 else:
103 new_labels.append(old_label)
104 self.set_all_labels(new_labels)
105 return old_labels
107 def use_label(self, lbl):
108 self.labels_used[lbl] = 1
110 def label_used(self, lbl):
111 return lbl in self.labels_used
113 # temp handling
115 def allocate_temp(self, type, manage_ref):
116 """
117 Allocates a temporary (which may create a new one or get a previously
118 allocated and released one of the same type). Type is simply registered
119 and handed back, but will usually be a PyrexType.
121 If type.is_pyobject, manage_ref comes into play. If manage_ref is set to
122 True, the temp will be decref-ed on return statements and in exception
123 handling clauses. Otherwise the caller has to deal with any reference
124 counting of the variable.
126 If not type.is_pyobject, then manage_ref will be ignored, but it
127 still has to be passed. It is recommended to pass False by convention
128 if it is known that type will never be a Python object.
130 A C string referring to the variable is returned.
131 """
132 if not type.is_pyobject:
133 # Make manage_ref canonical, so that manage_ref will always mean
134 # a decref is needed.
135 manage_ref = False
136 freelist = self.temps_free.get((type, manage_ref))
137 if freelist is not None and len(freelist) > 0:
138 result = freelist.pop()
139 else:
140 while True:
141 self.temp_counter += 1
142 result = "%s%d" % (Naming.codewriter_temp_prefix, self.temp_counter)
143 if not result in self.names_taken: break
144 self.temps_allocated.append((result, type, manage_ref))
145 self.temps_used_type[result] = (type, manage_ref)
146 if DebugFlags.debug_temp_code_comments:
147 self.owner.putln("/* %s allocated */" % result)
148 return result
150 def release_temp(self, name):
151 """
152 Releases a temporary so that it can be reused by other code needing
153 a temp of the same type.
154 """
155 type, manage_ref = self.temps_used_type[name]
156 freelist = self.temps_free.get((type, manage_ref))
157 if freelist is None:
158 freelist = []
159 self.temps_free[(type, manage_ref)] = freelist
160 if name in freelist:
161 raise RuntimeError("Temp %s freed twice!" % name)
162 freelist.append(name)
163 if DebugFlags.debug_temp_code_comments:
164 self.owner.putln("/* %s released */" % name)
166 def temps_in_use(self):
167 """Return a list of (cname,type,manage_ref) tuples of temp names and their type
168 that are currently in use.
169 """
170 used = []
171 for name, type, manage_ref in self.temps_allocated:
172 freelist = self.temps_free.get((type, manage_ref))
173 if freelist is None or name not in freelist:
174 used.append((name, type, manage_ref))
175 return used
177 def temps_holding_reference(self):
178 """Return a list of (cname,type) tuples of temp names and their type
179 that are currently in use. This includes only temps of a
180 Python object type which owns its reference.
181 """
182 return [(name, type)
183 for name, type, manage_ref in self.temps_in_use()
184 if manage_ref]
186 def all_managed_temps(self):
187 """Return a list of (cname, type) tuples of refcount-managed Python objects.
188 """
189 return [(cname, type)
190 for cname, type, manage_ref in self.temps_allocated
191 if manage_ref]
193 def all_free_managed_temps(self):
194 """Return a list of (cname, type) tuples of refcount-managed Python
195 objects that are not currently in use. This is used by
196 try-except and try-finally blocks to clean up temps in the
197 error case.
198 """
199 return [(cname, type)
200 for (type, manage_ref), freelist in self.temps_free.iteritems()
201 if manage_ref
202 for cname in freelist]
205 class IntConst(object):
206 """Global info about a Python integer constant held by GlobalState.
207 """
208 # cname string
209 # value int
210 # is_long boolean
212 def __init__(self, cname, value, is_long):
213 self.cname = cname
214 self.value = value
215 self.is_long = is_long
217 possible_identifier = re.compile(ur"(?![0-9])\w+$", re.U).match
218 nice_identifier = re.compile('^[a-zA-Z0-0_]+$').match
220 class StringConst(object):
221 """Global info about a C string constant held by GlobalState.
222 """
223 # cname string
224 # text EncodedString or BytesLiteral
225 # py_strings {(identifier, encoding) : PyStringConst}
227 def __init__(self, cname, text, byte_string):
228 self.cname = cname
229 self.text = text
230 self.escaped_value = StringEncoding.escape_byte_string(byte_string)
231 self.py_strings = None
233 def get_py_string_const(self, encoding, identifier=None):
234 py_strings = self.py_strings
235 text = self.text
236 if encoding is not None:
237 encoding = encoding.upper()
239 key = (bool(identifier), encoding)
240 if py_strings is not None and key in py_strings:
241 py_string = py_strings[key]
242 else:
243 if py_strings is None:
244 self.py_strings = {}
245 is_unicode = encoding is None
246 intern = bool(identifier or (
247 identifier is None and possible_identifier(text)))
248 if intern:
249 prefix = Naming.interned_str_prefix
250 else:
251 prefix = Naming.py_const_prefix
252 pystring_cname = "%s%s%s_%s" % (
253 prefix,
254 is_unicode and 'u' or 'b',
255 identifier and 'i' or '',
256 self.cname[len(Naming.const_prefix):])
258 py_string = PyStringConst(
259 pystring_cname, is_unicode, bool(identifier), intern)
260 self.py_strings[key] = py_string
262 return py_string
264 class PyStringConst(object):
265 """Global info about a Python string constant held by GlobalState.
266 """
267 # cname string
268 # unicode boolean
269 # intern boolean
270 # identifier boolean
272 def __init__(self, cname, is_unicode, identifier=False, intern=False):
273 self.cname = cname
274 self.identifier = identifier
275 self.unicode = is_unicode
276 self.intern = intern
279 class GlobalState(object):
280 # filename_table {string : int} for finding filename table indexes
281 # filename_list [string] filenames in filename table order
282 # input_file_contents dict contents (=list of lines) of any file that was used as input
283 # to create this output C code. This is
284 # used to annotate the comments.
285 #
286 # used_utility_code set(string|int) Ids of used utility code (to avoid reinsertion)
287 # utilprotowriter CCodeWriter
288 # utildefwriter CCodeWriter
289 #
290 # declared_cnames {string:Entry} used in a transition phase to merge pxd-declared
291 # constants etc. into the pyx-declared ones (i.e,
292 # check if constants are already added).
293 # In time, hopefully the literals etc. will be
294 # supplied directly instead.
295 #
296 # const_cname_counter int global counter for constant identifiers
297 #
300 # interned_strings
301 # consts
302 # py_string_decls
303 # interned_nums
304 # cached_builtins
306 # directives set Temporary variable used to track
307 # the current set of directives in the code generation
308 # process.
310 directives = {}
312 def __init__(self, rootwriter, emit_linenums=False):
313 self.filename_table = {}
314 self.filename_list = []
315 self.input_file_contents = {}
316 self.used_utility_code = set()
317 self.declared_cnames = {}
318 self.in_utility_code_generation = False
319 self.emit_linenums = emit_linenums
321 self.const_cname_counter = 1
322 self.string_const_index = {}
323 self.int_const_index = {}
325 def initwriters(self, rootwriter):
326 self.utilprotowriter = rootwriter.new_writer()
327 self.utildefwriter = rootwriter.new_writer()
328 self.decls_writer = rootwriter.new_writer()
329 self.pystring_table = rootwriter.new_writer()
330 self.init_cached_builtins_writer = rootwriter.new_writer()
331 self.initwriter = rootwriter.new_writer()
332 self.cleanupwriter = rootwriter.new_writer()
334 if Options.cache_builtins:
335 self.init_cached_builtins_writer.enter_cfunc_scope()
336 self.init_cached_builtins_writer.putln("static int __Pyx_InitCachedBuiltins(void) {")
338 self.initwriter.enter_cfunc_scope()
339 self.initwriter.putln("")
340 self.initwriter.putln("static int __Pyx_InitGlobals(void) {")
342 self.cleanupwriter.enter_cfunc_scope()
343 self.cleanupwriter.putln("")
344 self.cleanupwriter.putln("static void __Pyx_CleanupGlobals(void) {")
346 #
347 # Global constants, interned objects, etc.
348 #
349 def insert_global_var_declarations_into(self, code):
350 code.insert(self.decls_writer)
352 def close_global_decls(self):
353 # This is called when it is known that no more global declarations will
354 # declared (but can be called before or after insert_XXX).
355 self.generate_const_declarations()
356 if Options.cache_builtins:
357 w = self.init_cached_builtins_writer
358 w.putln("return 0;")
359 w.put_label(w.error_label)
360 w.putln("return -1;")
361 w.putln("}")
362 w.exit_cfunc_scope()
364 w = self.initwriter
365 w.putln("return 0;")
366 w.put_label(w.error_label)
367 w.putln("return -1;")
368 w.putln("}")
369 w.exit_cfunc_scope()
371 w = self.cleanupwriter
372 w.putln("}")
373 w.exit_cfunc_scope()
375 def insert_initcode_into(self, code):
376 code.insert(self.pystring_table)
377 if Options.cache_builtins:
378 code.insert(self.init_cached_builtins_writer)
379 code.insert(self.initwriter)
381 def insert_cleanupcode_into(self, code):
382 code.insert(self.cleanupwriter)
384 def put_pyobject_decl(self, entry):
385 self.decls_writer.putln("static PyObject *%s;" % entry.cname)
387 # constant handling at code generation time
389 def get_int_const(self, str_value, longness=False):
390 longness = bool(longness or Utils.long_literal(str_value))
391 try:
392 c = self.int_const_index[(str_value, longness)]
393 except KeyError:
394 c = self.new_int_const(str_value, longness)
395 return c
397 def get_string_const(self, text):
398 # return a C string constant, creating a new one if necessary
399 if text.is_unicode:
400 byte_string = text.utf8encode()
401 else:
402 byte_string = text.byteencode()
403 try:
404 c = self.string_const_index[byte_string]
405 except KeyError:
406 c = self.new_string_const(text, byte_string)
407 return c
409 def get_py_string_const(self, text, identifier=None):
410 # return a Python string constant, creating a new one if necessary
411 c_string = self.get_string_const(text)
412 py_string = c_string.get_py_string_const(text.encoding, identifier)
413 return py_string
415 def new_string_const(self, text, byte_string):
416 cname = self.new_string_const_cname(text)
417 c = StringConst(cname, text, byte_string)
418 self.string_const_index[byte_string] = c
419 return c
421 def new_int_const(self, value, longness):
422 cname = self.new_int_const_cname(value, longness)
423 c = IntConst(cname, value, longness)
424 self.int_const_index[(value, longness)] = c
425 return c
427 def new_string_const_cname(self, value, intern=None):
428 # Create a new globally-unique nice name for a C string constant.
429 if len(value) < 20 and nice_identifier(value):
430 return "%s%s" % (Naming.const_prefix, value)
431 else:
432 return self.new_const_cname()
434 def new_int_const_cname(self, value, longness):
435 if longness:
436 value += 'L'
437 cname = "%s%s" % (Naming.interned_num_prefix, value)
438 cname = cname.replace('-', 'neg_').replace('.','_')
439 return cname
441 def new_const_cname(self, prefix=''):
442 n = self.const_cname_counter
443 self.const_cname_counter += 1
444 return "%s%s%d" % (Naming.const_prefix, prefix, n)
446 def add_cached_builtin_decl(self, entry):
447 if Options.cache_builtins:
448 if self.should_declare(entry.cname, entry):
449 interned_cname = self.get_py_string_const(entry.name, True).cname
450 self.put_pyobject_decl(entry)
451 self.init_cached_builtins_writer.putln('%s = __Pyx_GetName(%s, %s); if (!%s) %s' % (
452 entry.cname,
453 Naming.builtins_cname,
454 interned_cname,
455 entry.cname,
456 self.init_cached_builtins_writer.error_goto(entry.pos)))
458 def generate_const_declarations(self):
459 self.generate_string_constants()
460 self.generate_int_constants()
462 def generate_string_constants(self):
463 c_consts = [ (len(c.cname), c.cname, c)
464 for c in self.string_const_index.itervalues() ]
465 c_consts.sort()
466 py_strings = []
467 for _, cname, c in c_consts:
468 self.decls_writer.putln('static char %s[] = "%s";' % (
469 cname, c.escaped_value))
470 if c.py_strings is not None:
471 for py_string in c.py_strings.itervalues():
472 py_strings.append((c.cname, len(py_string.cname), py_string))
474 if py_strings:
475 import Nodes
476 self.use_utility_code(Nodes.init_string_tab_utility_code)
478 py_strings.sort()
479 self.pystring_table.putln("")
480 self.pystring_table.putln("static __Pyx_StringTabEntry %s[] = {" %
481 Naming.stringtab_cname)
482 for c_cname, _, py_string in py_strings:
483 self.decls_writer.putln(
484 "static PyObject *%s;" % py_string.cname)
485 self.pystring_table.putln(
486 "{&%s, %s, sizeof(%s), %d, %d, %d}," % (
487 py_string.cname,
488 c_cname,
489 c_cname,
490 py_string.unicode,
491 py_string.intern,
492 py_string.identifier
493 ))
494 self.pystring_table.putln("{0, 0, 0, 0, 0, 0}")
495 self.pystring_table.putln("};")
497 self.initwriter.putln(
498 "if (__Pyx_InitStrings(%s) < 0) %s;" % (
499 Naming.stringtab_cname,
500 self.initwriter.error_goto(self.module_pos)))
502 def generate_int_constants(self):
503 consts = [ (len(c.value), c.value, c.is_long, c)
504 for c in self.int_const_index.itervalues() ]
505 consts.sort()
506 for _, value, longness, c in consts:
507 cname = c.cname
508 self.decls_writer.putln("static PyObject *%s;" % cname)
509 if longness:
510 function = '%s = PyLong_FromString((char *)"%s", 0, 0); %s;'
511 else:
512 function = "%s = PyInt_FromLong(%s); %s;"
513 self.initwriter.putln(function % (
514 cname,
515 value,
516 self.initwriter.error_goto_if_null(cname, self.module_pos)))
518 # The functions below are there in a transition phase only
519 # and will be deprecated. They are called from Nodes.BlockNode.
520 # The copy&paste duplication is intentional in order to be able
521 # to see quickly how BlockNode worked, until this is replaced.
523 def should_declare(self, cname, entry):
524 if cname in self.declared_cnames:
525 other = self.declared_cnames[cname]
526 assert str(entry.type) == str(other.type)
527 assert entry.init == other.init
528 return False
529 else:
530 self.declared_cnames[cname] = entry
531 return True
533 #
534 # File name state
535 #
537 def lookup_filename(self, filename):
538 try:
539 index = self.filename_table[filename]
540 except KeyError:
541 index = len(self.filename_list)
542 self.filename_list.append(filename)
543 self.filename_table[filename] = index
544 return index
546 def commented_file_contents(self, source_desc):
547 try:
548 return self.input_file_contents[source_desc]
549 except KeyError:
550 F = [u' * ' + line.rstrip().replace(
551 u'*/', u'*[inserted by cython to avoid comment closer]/'
552 ).replace(
553 u'/*', u'/[inserted by cython to avoid comment start]*'
554 ).encode('ASCII', 'replace') # + Py2 auto-decode to unicode
555 for line in source_desc.get_lines()]
556 if len(F) == 0: F.append(u'')
557 self.input_file_contents[source_desc] = F
558 return F
560 #
561 # Utility code state
562 #
564 def use_utility_code(self, utility_code, name=None):
565 """
566 Adds the given utility code to the C file if needed.
568 codetup should unpack into one prototype code part and one
569 definition code part, both strings inserted directly in C.
571 If name is provided, it is used as an identifier to avoid inserting
572 code twice. Otherwise, id(codetup) is used as such an identifier.
573 """
574 if name is None: name = id(utility_code)
575 if self.check_utility_code_needed_and_register(name):
576 if utility_code.requires:
577 for dependency in utility_code.requires:
578 self.use_utility_code(dependency)
579 if utility_code.proto:
580 self.utilprotowriter.put(utility_code.proto)
581 if utility_code.impl:
582 self.utildefwriter.put(utility_code.impl)
583 utility_code.write_init_code(self.initwriter, self.module_pos)
584 utility_code.write_cleanup_code(self.cleanupwriter, self.module_pos)
586 def has_code(self, name):
587 return name in self.used_utility_code
589 def use_code_from(self, func, name, *args, **kw):
590 """
591 Requests that the utility code that func can generate is used in the C
592 file. func is called like this:
594 func(proto, definition, name, *args, **kw)
596 where proto and definition are two CCodeWriter instances; the
597 former should have the prototype written to it and the other the definition.
599 The call might happen at some later point (if compiling multiple modules
600 into a cache for instance), and will only happen once per utility code.
602 name is used to identify the utility code, so that it isn't regenerated
603 when the same code is requested again.
604 """
605 if self.check_utility_code_needed_and_register(name):
606 func(self.utilprotowriter, self.utildefwriter,
607 name, *args, **kw)
609 def check_utility_code_needed_and_register(self, name):
610 if name in self.used_utility_code:
611 return False
612 else:
613 self.used_utility_code.add(name)
614 return True
616 def put_utility_code_protos(self, writer):
617 writer.insert(self.utilprotowriter)
619 def put_utility_code_defs(self, writer):
620 if self.emit_linenums:
621 writer.write('\n#line 1 "cython_utility"\n')
622 writer.insert(self.utildefwriter)
625 def funccontext_property(name):
626 def get(self):
627 return getattr(self.funcstate, name)
628 def set(self, value):
629 setattr(self.funcstate, name, value)
630 return property(get, set)
633 class CCodeWriter(object):
634 """
635 Utility class to output C code.
637 When creating an insertion point one must care about the state that is
638 kept:
639 - formatting state (level, bol) is cloned and used in insertion points
640 as well
641 - labels, temps, exc_vars: One must construct a scope in which these can
642 exist by calling enter_cfunc_scope/exit_cfunc_scope (these are for
643 sanity checking and forward compatabilty). Created insertion points
644 looses this scope and cannot access it.
645 - marker: Not copied to insertion point
646 - filename_table, filename_list, input_file_contents: All codewriters
647 coming from the same root share the same instances simultaneously.
648 """
650 # f file output file
651 # buffer StringIOTree
653 # level int indentation level
654 # bol bool beginning of line?
655 # marker string comment to emit before next line
656 # funcstate FunctionState contains state local to a C function used for code
657 # generation (labels and temps state etc.)
658 # globalstate GlobalState contains state global for a C file (input file info,
659 # utility code, declared constants etc.)
660 # emit_linenums boolean whether or not to write #line pragmas
662 def __init__(self, create_from=None, buffer=None, copy_formatting=False, emit_linenums=None):
663 if buffer is None: buffer = StringIOTree()
664 self.buffer = buffer
665 self.marker = None
666 self.last_marker_line = 0
667 self.source_desc = ""
669 self.funcstate = None
670 self.level = 0
671 self.call_level = 0
672 self.bol = 1
673 if create_from is None:
674 # Root CCodeWriter
675 self.globalstate = GlobalState(self, emit_linenums=emit_linenums)
676 self.globalstate.initwriters(self)
677 # ^^^ need seperate step because this will reference self.globalstate
678 else:
679 # Use same global state
680 self.globalstate = create_from.globalstate
681 # Clone formatting state
682 if copy_formatting:
683 self.level = create_from.level
684 self.bol = create_from.bol
685 self.call_level = create_from.call_level
686 if emit_linenums is None:
687 self.emit_linenums = self.globalstate.emit_linenums
688 else:
689 self.emit_linenums = emit_linenums
691 def create_new(self, create_from, buffer, copy_formatting):
692 # polymorphic constructor -- very slightly more versatile
693 # than using __class__
694 return CCodeWriter(create_from, buffer, copy_formatting)
696 def copyto(self, f):
697 self.buffer.copyto(f)
699 def getvalue(self):
700 return self.buffer.getvalue()
702 def write(self, s):
703 self.buffer.write(s)
705 def insertion_point(self):
706 other = self.create_new(create_from=self, buffer=self.buffer.insertion_point(), copy_formatting=True)
707 return other
709 def new_writer(self):
710 """
711 Creates a new CCodeWriter connected to the same global state, which
712 can later be inserted using insert.
713 """
714 return CCodeWriter(create_from=self)
716 def insert(self, writer):
717 """
718 Inserts the contents of another code writer (created with
719 the same global state) in the current location.
721 It is ok to write to the inserted writer also after insertion.
722 """
723 assert writer.globalstate is self.globalstate
724 self.buffer.insert(writer.buffer)
726 # Properties delegated to function scope
727 label_counter = funccontext_property("label_counter")
728 return_label = funccontext_property("return_label")
729 error_label = funccontext_property("error_label")
730 labels_used = funccontext_property("labels_used")
731 continue_label = funccontext_property("continue_label")
732 break_label = funccontext_property("break_label")
733 return_from_error_cleanup_label = funccontext_property("return_from_error_cleanup_label")
735 # Functions delegated to function scope
736 def new_label(self, name=None): return self.funcstate.new_label(name)
737 def new_error_label(self): return self.funcstate.new_error_label()
738 def get_loop_labels(self): return self.funcstate.get_loop_labels()
739 def set_loop_labels(self, labels): return self.funcstate.set_loop_labels(labels)
740 def new_loop_labels(self): return self.funcstate.new_loop_labels()
741 def get_all_labels(self): return self.funcstate.get_all_labels()
742 def set_all_labels(self, labels): return self.funcstate.set_all_labels(labels)
743 def all_new_labels(self): return self.funcstate.all_new_labels()
744 def use_label(self, lbl): return self.funcstate.use_label(lbl)
745 def label_used(self, lbl): return self.funcstate.label_used(lbl)
748 def enter_cfunc_scope(self):
749 self.funcstate = FunctionState(self)
751 def exit_cfunc_scope(self):
752 self.funcstate = None
754 # constant handling
756 def get_py_num(self, str_value, longness):
757 return self.globalstate.get_int_const(str_value, longness).cname
759 def get_string_const(self, text):
760 return self.globalstate.get_string_const(text).cname
762 def get_py_string_const(self, text, identifier=None):
763 return self.globalstate.get_py_string_const(text, identifier).cname
765 def intern(self, text):
766 return self.get_py_string_const(text)
768 def intern_identifier(self, text):
769 return self.get_py_string_const(text, True)
771 # code generation
773 def putln(self, code = ""):
774 if self.marker and self.bol:
775 self.emit_marker()
776 if self.emit_linenums and self.last_marker_line != 0:
777 self.write('\n#line %s "%s"\n' % (self.last_marker_line, self.source_desc))
778 if code:
779 self.put(code)
780 self.write("\n");
781 self.bol = 1
783 def emit_marker(self):
784 self.write("\n");
785 self.indent()
786 self.write("/* %s */\n" % self.marker[1])
787 self.last_marker_line = self.marker[0]
788 self.marker = None
790 def put_safe(self, code):
791 # put code, but ignore {}
792 self.write(code)
793 self.bol = 0
795 def put(self, code):
796 fix_indent = False
797 if "{" in code:
798 dl = code.count("{")
799 else:
800 dl = 0
801 if "}" in code:
802 dl -= code.count("}")
803 if dl < 0:
804 self.level += dl
805 elif dl == 0 and code[0] == "}":
806 # special cases like "} else {" need a temporary dedent
807 fix_indent = True
808 self.level -= 1
809 if self.bol:
810 self.indent()
811 self.write(code)
812 self.bol = 0
813 if dl > 0:
814 self.level += dl
815 elif fix_indent:
816 self.level += 1
818 def increase_indent(self):
819 self.level = self.level + 1
821 def decrease_indent(self):
822 self.level = self.level - 1
824 def begin_block(self):
825 self.putln("{")
826 self.increase_indent()
828 def end_block(self):
829 self.decrease_indent()
830 self.putln("}")
832 def indent(self):
833 self.write(" " * self.level)
835 def get_py_version_hex(self, pyversion):
836 return "0x%02X%02X%02X%02X" % (tuple(pyversion) + (0,0,0,0))[:4]
838 def mark_pos(self, pos):
839 if pos is None:
840 return
841 source_desc, line, col = pos
842 if self.last_marker_line == line:
843 return
844 assert isinstance(source_desc, SourceDescriptor)
845 contents = self.globalstate.commented_file_contents(source_desc)
846 lines = contents[max(0,line-3):line] # line numbers start at 1
847 lines[-1] += u' # <<<<<<<<<<<<<<'
848 lines += contents[line:line+2]
850 marker = u'"%s":%d\n%s\n' % (
851 source_desc.get_escaped_description(), line, u'\n'.join(lines))
852 self.marker = (line, marker)
853 if self.emit_linenums:
854 self.source_desc = source_desc.get_escaped_description()
856 def put_label(self, lbl):
857 if lbl in self.funcstate.labels_used:
858 self.putln("%s:;" % lbl)
860 def put_goto(self, lbl):
861 self.funcstate.use_label(lbl)
862 self.putln("goto %s;" % lbl)
864 def put_var_declarations(self, entries, static = 0, dll_linkage = None,
865 definition = True):
866 for entry in entries:
867 if not entry.in_cinclude:
868 self.put_var_declaration(entry, static, dll_linkage, definition)
870 def put_var_declaration(self, entry, static = 0, dll_linkage = None,
871 definition = True):
872 #print "Code.put_var_declaration:", entry.name, "definition =", definition ###
873 if entry.in_closure:
874 return
875 visibility = entry.visibility
876 if visibility == 'private' and not definition:
877 #print "...private and not definition, skipping" ###
878 return
879 if not entry.used and visibility == "private":
880 #print "not used and private, skipping", entry.cname ###
881 return
882 storage_class = ""
883 if visibility == 'extern':
884 storage_class = Naming.extern_c_macro
885 elif visibility == 'public':
886 if not definition:
887 storage_class = Naming.extern_c_macro
888 elif visibility == 'private':
889 if static:
890 storage_class = "static"
891 if storage_class:
892 self.put("%s " % storage_class)
893 if visibility != 'public':
894 dll_linkage = None
895 self.put(entry.type.declaration_code(entry.cname,
896 dll_linkage = dll_linkage))
897 if entry.init is not None:
898 self.put_safe(" = %s" % entry.type.literal_code(entry.init))
899 self.putln(";")
901 def put_temp_declarations(self, func_context):
902 for name, type, manage_ref in func_context.temps_allocated:
903 decl = type.declaration_code(name)
904 if type.is_pyobject:
905 self.putln("%s = NULL;" % decl)
906 else:
907 self.putln("%s;" % decl)
909 def put_h_guard(self, guard):
910 self.putln("#ifndef %s" % guard)
911 self.putln("#define %s" % guard)
913 def unlikely(self, cond):
914 if Options.gcc_branch_hints:
915 return 'unlikely(%s)' % cond
916 else:
917 return cond
919 # Python objects and reference counting
921 def entry_as_pyobject(self, entry):
922 type = entry.type
923 if (not entry.is_self_arg and not entry.type.is_complete()
924 or entry.type.is_extension_type):
925 return "(PyObject *)" + entry.cname
926 else:
927 return entry.cname
929 def as_pyobject(self, cname, type):
930 return typecast(py_object_type, type, cname)
932 def put_gotref(self, cname):
933 self.putln("__Pyx_GOTREF(%s);" % cname)
935 def put_giveref(self, cname):
936 self.putln("__Pyx_GIVEREF(%s);" % cname)
938 def put_xgiveref(self, cname):
939 self.putln("__Pyx_XGIVEREF(%s);" % cname)
941 def put_xgotref(self, cname):
942 self.putln("__Pyx_XGOTREF(%s);" % cname)
944 def put_incref(self, cname, type, nanny=True):
945 if nanny:
946 self.putln("__Pyx_INCREF(%s);" % self.as_pyobject(cname, type))
947 else:
948 self.putln("Py_INCREF(%s);" % self.as_pyobject(cname, type))
950 def put_decref(self, cname, type, nanny=True):
951 if nanny:
952 self.putln("__Pyx_DECREF(%s);" % self.as_pyobject(cname, type))
953 else:
954 self.putln("Py_DECREF(%s);" % self.as_pyobject(cname, type))
956 def put_var_gotref(self, entry):
957 if entry.type.is_pyobject:
958 self.putln("__Pyx_GOTREF(%s);" % self.entry_as_pyobject(entry))
960 def put_var_giveref(self, entry):
961 if entry.type.is_pyobject:
962 self.putln("__Pyx_GIVEREF(%s);" % self.entry_as_pyobject(entry))
964 def put_var_xgotref(self, entry):
965 if entry.type.is_pyobject:
966 self.putln("__Pyx_XGOTREF(%s);" % self.entry_as_pyobject(entry))
968 def put_var_xgiveref(self, entry):
969 if entry.type.is_pyobject:
970 self.putln("__Pyx_XGIVEREF(%s);" % self.entry_as_pyobject(entry))
972 def put_var_incref(self, entry):
973 if entry.type.is_pyobject:
974 self.putln("__Pyx_INCREF(%s);" % self.entry_as_pyobject(entry))
976 def put_decref_clear(self, cname, type, nanny=True):
977 if nanny:
978 self.putln("__Pyx_DECREF(%s); %s = 0;" % (
979 typecast(py_object_type, type, cname), cname))
980 else:
981 self.putln("Py_DECREF(%s); %s = 0;" % (
982 typecast(py_object_type, type, cname), cname))
984 def put_xdecref(self, cname, type, nanny=True):
985 if nanny:
986 self.putln("__Pyx_XDECREF(%s);" % self.as_pyobject(cname, type))
987 else:
988 self.putln("Py_XDECREF(%s);" % self.as_pyobject(cname, type))
990 def put_xdecref_clear(self, cname, type, nanny=True):
991 if nanny:
992 self.putln("__Pyx_XDECREF(%s); %s = 0;" % (
993 self.as_pyobject(cname, type), cname))
994 else:
995 self.putln("Py_XDECREF(%s); %s = 0;" % (
996 self.as_pyobject(cname, type), cname))
998 def put_var_decref(self, entry):
999 if entry.type.is_pyobject:
1000 if entry.init_to_none is False:
1001 self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry))
1002 else:
1003 self.putln("__Pyx_DECREF(%s);" % self.entry_as_pyobject(entry))
1005 def put_var_decref_clear(self, entry):
1006 if entry.type.is_pyobject:
1007 self.putln("__Pyx_DECREF(%s); %s = 0;" % (
1008 self.entry_as_pyobject(entry), entry.cname))
1010 def put_var_xdecref(self, entry):
1011 if entry.type.is_pyobject:
1012 self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry))
1014 def put_var_xdecref_clear(self, entry):
1015 if entry.type.is_pyobject:
1016 self.putln("__Pyx_XDECREF(%s); %s = 0;" % (
1017 self.entry_as_pyobject(entry), entry.cname))
1019 def put_var_decrefs(self, entries, used_only = 0):
1020 for entry in entries:
1021 if not used_only or entry.used:
1022 if entry.xdecref_cleanup:
1023 self.put_var_xdecref(entry)
1024 else:
1025 self.put_var_decref(entry)
1027 def put_var_xdecrefs(self, entries):
1028 for entry in entries:
1029 self.put_var_xdecref(entry)
1031 def put_var_xdecrefs_clear(self, entries):
1032 for entry in entries:
1033 self.put_var_xdecref_clear(entry)
1035 def put_init_to_py_none(self, cname, type, nanny=True):
1036 py_none = typecast(type, py_object_type, "Py_None")
1037 if nanny:
1038 self.putln("%s = %s; __Pyx_INCREF(Py_None);" % (cname, py_none))
1039 else:
1040 self.putln("%s = %s; Py_INCREF(Py_None);" % (cname, py_none))
1042 def put_init_var_to_py_none(self, entry, template = "%s", nanny=True):
1043 code = template % entry.cname
1044 #if entry.type.is_extension_type:
1045 # code = "((PyObject*)%s)" % code
1046 self.put_init_to_py_none(code, entry.type, nanny)
1048 def put_pymethoddef(self, entry, term):
1049 if entry.doc:
1050 doc_code = entry.doc_cname
1051 else:
1052 doc_code = 0
1053 method_flags = entry.signature.method_flags()
1054 if method_flags:
1055 if entry.is_special:
1056 method_flags += [method_coexist]
1057 self.putln(
1058 '{__Pyx_NAMESTR("%s"), (PyCFunction)%s, %s, __Pyx_DOCSTR(%s)}%s' % (
1059 entry.name,
1060 entry.func_cname,
1061 "|".join(method_flags),
1062 doc_code,
1063 term))
1065 # error handling
1067 def put_error_if_neg(self, pos, value):
1068 # 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!
1069 return self.putln("if (%s < 0) %s" % (value, self.error_goto(pos)))
1071 def set_error_info(self, pos):
1072 if Options.c_line_in_traceback:
1073 cinfo = " %s = %s;" % (Naming.clineno_cname, Naming.line_c_macro)
1074 else:
1075 cinfo = ""
1076 return "%s = %s[%s]; %s = %s;%s" % (
1077 Naming.filename_cname,
1078 Naming.filetable_cname,
1079 self.lookup_filename(pos[0]),
1080 Naming.lineno_cname,
1081 pos[1],
1082 cinfo)
1084 def error_goto(self, pos):
1085 lbl = self.funcstate.error_label
1086 self.funcstate.use_label(lbl)
1087 return "{%s goto %s;}" % (
1088 self.set_error_info(pos),
1089 lbl)
1091 def error_goto_if(self, cond, pos):
1092 return "if (%s) %s" % (self.unlikely(cond), self.error_goto(pos))
1094 def error_goto_if_null(self, cname, pos):
1095 return self.error_goto_if("!%s" % cname, pos)
1097 def error_goto_if_neg(self, cname, pos):
1098 return self.error_goto_if("%s < 0" % cname, pos)
1100 def error_goto_if_PyErr(self, pos):
1101 return self.error_goto_if("PyErr_Occurred()", pos)
1103 def lookup_filename(self, filename):
1104 return self.globalstate.lookup_filename(filename)
1106 def put_setup_refcount_context(self, name):
1107 self.putln('__Pyx_SetupRefcountContext("%s");' % name)
1109 def put_finish_refcount_context(self):
1110 self.putln("__Pyx_FinishRefcountContext();")
1113 class PyrexCodeWriter(object):
1114 # f file output file
1115 # level int indentation level
1117 def __init__(self, outfile_name):
1118 self.f = Utils.open_new_file(outfile_name)
1119 self.level = 0
1121 def putln(self, code):
1122 self.f.write("%s%s\n" % (" " * self.level, code))
1124 def indent(self):
1125 self.level += 1
1127 def dedent(self):
1128 self.level -= 1
