Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / third_party / cython / src / Cython / Compiler / FusedNode.py
blob7a9d1ab48bc6fd8ab64c58fe1c43deed16703cf8
1 import copy
3 from Cython.Compiler import (ExprNodes, PyrexTypes, MemoryView,
4 ParseTreeTransforms, StringEncoding,
5 Errors)
6 from Cython.Compiler.ExprNodes import CloneNode, ProxyNode, TupleNode
7 from Cython.Compiler.Nodes import (FuncDefNode, CFuncDefNode, StatListNode,
8 DefNode)
10 class FusedCFuncDefNode(StatListNode):
11 """
12 This node replaces a function with fused arguments. It deep-copies the
13 function for every permutation of fused types, and allocates a new local
14 scope for it. It keeps track of the original function in self.node, and
15 the entry of the original function in the symbol table is given the
16 'fused_cfunction' attribute which points back to us.
17 Then when a function lookup occurs (to e.g. call it), the call can be
18 dispatched to the right function.
20 node FuncDefNode the original function
21 nodes [FuncDefNode] list of copies of node with different specific types
22 py_func DefNode the fused python function subscriptable from
23 Python space
24 __signatures__ A DictNode mapping signature specialization strings
25 to PyCFunction nodes
26 resulting_fused_function PyCFunction for the fused DefNode that delegates
27 to specializations
28 fused_func_assignment Assignment of the fused function to the function name
29 defaults_tuple TupleNode of defaults (letting PyCFunctionNode build
30 defaults would result in many different tuples)
31 specialized_pycfuncs List of synthesized pycfunction nodes for the
32 specializations
33 code_object CodeObjectNode shared by all specializations and the
34 fused function
36 fused_compound_types All fused (compound) types (e.g. floating[:])
37 """
39 __signatures__ = None
40 resulting_fused_function = None
41 fused_func_assignment = None
42 defaults_tuple = None
43 decorators = None
45 child_attrs = StatListNode.child_attrs + [
46 '__signatures__', 'resulting_fused_function', 'fused_func_assignment']
48 def __init__(self, node, env):
49 super(FusedCFuncDefNode, self).__init__(node.pos)
51 self.nodes = []
52 self.node = node
54 is_def = isinstance(self.node, DefNode)
55 if is_def:
56 # self.node.decorators = []
57 self.copy_def(env)
58 else:
59 self.copy_cdef(env)
61 # Perform some sanity checks. If anything fails, it's a bug
62 for n in self.nodes:
63 assert not n.entry.type.is_fused
64 assert not n.local_scope.return_type.is_fused
65 if node.return_type.is_fused:
66 assert not n.return_type.is_fused
68 if not is_def and n.cfunc_declarator.optional_arg_count:
69 assert n.type.op_arg_struct
71 node.entry.fused_cfunction = self
72 # Copy the nodes as AnalyseDeclarationsTransform will prepend
73 # self.py_func to self.stats, as we only want specialized
74 # CFuncDefNodes in self.nodes
75 self.stats = self.nodes[:]
77 def copy_def(self, env):
78 """
79 Create a copy of the original def or lambda function for specialized
80 versions.
81 """
82 fused_compound_types = PyrexTypes.unique(
83 [arg.type for arg in self.node.args if arg.type.is_fused])
84 permutations = PyrexTypes.get_all_specialized_permutations(fused_compound_types)
86 self.fused_compound_types = fused_compound_types
88 if self.node.entry in env.pyfunc_entries:
89 env.pyfunc_entries.remove(self.node.entry)
91 for cname, fused_to_specific in permutations:
92 copied_node = copy.deepcopy(self.node)
94 self._specialize_function_args(copied_node.args, fused_to_specific)
95 copied_node.return_type = self.node.return_type.specialize(
96 fused_to_specific)
98 copied_node.analyse_declarations(env)
99 # copied_node.is_staticmethod = self.node.is_staticmethod
100 # copied_node.is_classmethod = self.node.is_classmethod
101 self.create_new_local_scope(copied_node, env, fused_to_specific)
102 self.specialize_copied_def(copied_node, cname, self.node.entry,
103 fused_to_specific, fused_compound_types)
105 PyrexTypes.specialize_entry(copied_node.entry, cname)
106 copied_node.entry.used = True
107 env.entries[copied_node.entry.name] = copied_node.entry
109 if not self.replace_fused_typechecks(copied_node):
110 break
112 self.orig_py_func = self.node
113 self.py_func = self.make_fused_cpdef(self.node, env, is_def=True)
115 def copy_cdef(self, env):
117 Create a copy of the original c(p)def function for all specialized
118 versions.
120 permutations = self.node.type.get_all_specialized_permutations()
121 # print 'Node %s has %d specializations:' % (self.node.entry.name,
122 # len(permutations))
123 # import pprint; pprint.pprint([d for cname, d in permutations])
125 if self.node.entry in env.cfunc_entries:
126 env.cfunc_entries.remove(self.node.entry)
128 # Prevent copying of the python function
129 self.orig_py_func = orig_py_func = self.node.py_func
130 self.node.py_func = None
131 if orig_py_func:
132 env.pyfunc_entries.remove(orig_py_func.entry)
134 fused_types = self.node.type.get_fused_types()
135 self.fused_compound_types = fused_types
137 for cname, fused_to_specific in permutations:
138 copied_node = copy.deepcopy(self.node)
140 # Make the types in our CFuncType specific
141 type = copied_node.type.specialize(fused_to_specific)
142 entry = copied_node.entry
144 copied_node.type = type
145 entry.type, type.entry = type, entry
147 entry.used = (entry.used or
148 self.node.entry.defined_in_pxd or
149 env.is_c_class_scope or
150 entry.is_cmethod)
152 if self.node.cfunc_declarator.optional_arg_count:
153 self.node.cfunc_declarator.declare_optional_arg_struct(
154 type, env, fused_cname=cname)
156 copied_node.return_type = type.return_type
157 self.create_new_local_scope(copied_node, env, fused_to_specific)
159 # Make the argument types in the CFuncDeclarator specific
160 self._specialize_function_args(copied_node.cfunc_declarator.args,
161 fused_to_specific)
163 type.specialize_entry(entry, cname)
164 env.cfunc_entries.append(entry)
166 # If a cpdef, declare all specialized cpdefs (this
167 # also calls analyse_declarations)
168 copied_node.declare_cpdef_wrapper(env)
169 if copied_node.py_func:
170 env.pyfunc_entries.remove(copied_node.py_func.entry)
172 self.specialize_copied_def(
173 copied_node.py_func, cname, self.node.entry.as_variable,
174 fused_to_specific, fused_types)
176 if not self.replace_fused_typechecks(copied_node):
177 break
179 if orig_py_func:
180 self.py_func = self.make_fused_cpdef(orig_py_func, env,
181 is_def=False)
182 else:
183 self.py_func = orig_py_func
185 def _specialize_function_args(self, args, fused_to_specific):
186 for arg in args:
187 if arg.type.is_fused:
188 arg.type = arg.type.specialize(fused_to_specific)
189 if arg.type.is_memoryviewslice:
190 MemoryView.validate_memslice_dtype(arg.pos, arg.type.dtype)
192 def create_new_local_scope(self, node, env, f2s):
194 Create a new local scope for the copied node and append it to
195 self.nodes. A new local scope is needed because the arguments with the
196 fused types are aready in the local scope, and we need the specialized
197 entries created after analyse_declarations on each specialized version
198 of the (CFunc)DefNode.
199 f2s is a dict mapping each fused type to its specialized version
201 node.create_local_scope(env)
202 node.local_scope.fused_to_specific = f2s
204 # This is copied from the original function, set it to false to
205 # stop recursion
206 node.has_fused_arguments = False
207 self.nodes.append(node)
209 def specialize_copied_def(self, node, cname, py_entry, f2s, fused_types):
210 """Specialize the copy of a DefNode given the copied node,
211 the specialization cname and the original DefNode entry"""
212 type_strings = [
213 PyrexTypes.specialization_signature_string(fused_type, f2s)
214 for fused_type in fused_types
217 node.specialized_signature_string = '|'.join(type_strings)
219 node.entry.pymethdef_cname = PyrexTypes.get_fused_cname(
220 cname, node.entry.pymethdef_cname)
221 node.entry.doc = py_entry.doc
222 node.entry.doc_cname = py_entry.doc_cname
224 def replace_fused_typechecks(self, copied_node):
226 Branch-prune fused type checks like
228 if fused_t is int:
231 Returns whether an error was issued and whether we should stop in
232 in order to prevent a flood of errors.
234 num_errors = Errors.num_errors
235 transform = ParseTreeTransforms.ReplaceFusedTypeChecks(
236 copied_node.local_scope)
237 transform(copied_node)
239 if Errors.num_errors > num_errors:
240 return False
242 return True
244 def _fused_instance_checks(self, normal_types, pyx_code, env):
246 Genereate Cython code for instance checks, matching an object to
247 specialized types.
249 if_ = 'if'
250 for specialized_type in normal_types:
251 # all_numeric = all_numeric and specialized_type.is_numeric
252 py_type_name = specialized_type.py_type_name()
253 specialized_type_name = specialized_type.specialization_string
254 pyx_code.context.update(locals())
255 pyx_code.put_chunk(
256 u"""
257 {{if_}} isinstance(arg, {{py_type_name}}):
258 dest_sig[{{dest_sig_idx}}] = '{{specialized_type_name}}'
259 """)
260 if_ = 'elif'
262 if not normal_types:
263 # we need an 'if' to match the following 'else'
264 pyx_code.putln("if 0: pass")
266 def _dtype_name(self, dtype):
267 if dtype.is_typedef:
268 return '___pyx_%s' % dtype
269 return str(dtype).replace(' ', '_')
271 def _dtype_type(self, dtype):
272 if dtype.is_typedef:
273 return self._dtype_name(dtype)
274 return str(dtype)
276 def _sizeof_dtype(self, dtype):
277 if dtype.is_pyobject:
278 return 'sizeof(void *)'
279 else:
280 return "sizeof(%s)" % self._dtype_type(dtype)
282 def _buffer_check_numpy_dtype_setup_cases(self, pyx_code):
283 "Setup some common cases to match dtypes against specializations"
284 if pyx_code.indenter("if dtype.kind in ('i', 'u'):"):
285 pyx_code.putln("pass")
286 pyx_code.named_insertion_point("dtype_int")
287 pyx_code.dedent()
289 if pyx_code.indenter("elif dtype.kind == 'f':"):
290 pyx_code.putln("pass")
291 pyx_code.named_insertion_point("dtype_float")
292 pyx_code.dedent()
294 if pyx_code.indenter("elif dtype.kind == 'c':"):
295 pyx_code.putln("pass")
296 pyx_code.named_insertion_point("dtype_complex")
297 pyx_code.dedent()
299 if pyx_code.indenter("elif dtype.kind == 'O':"):
300 pyx_code.putln("pass")
301 pyx_code.named_insertion_point("dtype_object")
302 pyx_code.dedent()
304 match = "dest_sig[{{dest_sig_idx}}] = '{{specialized_type_name}}'"
305 no_match = "dest_sig[{{dest_sig_idx}}] = None"
306 def _buffer_check_numpy_dtype(self, pyx_code, specialized_buffer_types):
308 Match a numpy dtype object to the individual specializations.
310 self._buffer_check_numpy_dtype_setup_cases(pyx_code)
312 for specialized_type in specialized_buffer_types:
313 dtype = specialized_type.dtype
314 pyx_code.context.update(
315 itemsize_match=self._sizeof_dtype(dtype) + " == itemsize",
316 signed_match="not (%s_is_signed ^ dtype_signed)" % self._dtype_name(dtype),
317 dtype=dtype,
318 specialized_type_name=specialized_type.specialization_string)
320 dtypes = [
321 (dtype.is_int, pyx_code.dtype_int),
322 (dtype.is_float, pyx_code.dtype_float),
323 (dtype.is_complex, pyx_code.dtype_complex)
326 for dtype_category, codewriter in dtypes:
327 if dtype_category:
328 cond = '{{itemsize_match}} and arg.ndim == %d' % (
329 specialized_type.ndim,)
330 if dtype.is_int:
331 cond += ' and {{signed_match}}'
333 if codewriter.indenter("if %s:" % cond):
334 # codewriter.putln("print 'buffer match found based on numpy dtype'")
335 codewriter.putln(self.match)
336 codewriter.putln("break")
337 codewriter.dedent()
339 def _buffer_parse_format_string_check(self, pyx_code, decl_code,
340 specialized_type, env):
342 For each specialized type, try to coerce the object to a memoryview
343 slice of that type. This means obtaining a buffer and parsing the
344 format string.
345 TODO: separate buffer acquisition from format parsing
347 dtype = specialized_type.dtype
348 if specialized_type.is_buffer:
349 axes = [('direct', 'strided')] * specialized_type.ndim
350 else:
351 axes = specialized_type.axes
353 memslice_type = PyrexTypes.MemoryViewSliceType(dtype, axes)
354 memslice_type.create_from_py_utility_code(env)
355 pyx_code.context.update(
356 coerce_from_py_func=memslice_type.from_py_function,
357 dtype=dtype)
358 decl_code.putln(
359 "{{memviewslice_cname}} {{coerce_from_py_func}}(object)")
361 pyx_code.context.update(
362 specialized_type_name=specialized_type.specialization_string,
363 sizeof_dtype=self._sizeof_dtype(dtype))
365 pyx_code.put_chunk(
366 u"""
367 # try {{dtype}}
368 if itemsize == -1 or itemsize == {{sizeof_dtype}}:
369 memslice = {{coerce_from_py_func}}(arg)
370 if memslice.memview:
371 __PYX_XDEC_MEMVIEW(&memslice, 1)
372 # print 'found a match for the buffer through format parsing'
374 break
375 else:
376 __pyx_PyErr_Clear()
377 """ % self.match)
379 def _buffer_checks(self, buffer_types, pyx_code, decl_code, env):
381 Generate Cython code to match objects to buffer specializations.
382 First try to get a numpy dtype object and match it against the individual
383 specializations. If that fails, try naively to coerce the object
384 to each specialization, which obtains the buffer each time and tries
385 to match the format string.
387 from Cython.Compiler import ExprNodes
388 if buffer_types:
389 if pyx_code.indenter(u"else:"):
390 # The first thing to find a match in this loop breaks out of the loop
391 if pyx_code.indenter(u"while 1:"):
392 pyx_code.put_chunk(
393 u"""
394 if numpy is not None:
395 if isinstance(arg, numpy.ndarray):
396 dtype = arg.dtype
397 elif (__pyx_memoryview_check(arg) and
398 isinstance(arg.base, numpy.ndarray)):
399 dtype = arg.base.dtype
400 else:
401 dtype = None
403 itemsize = -1
404 if dtype is not None:
405 itemsize = dtype.itemsize
406 kind = ord(dtype.kind)
407 dtype_signed = kind == ord('i')
408 """)
409 pyx_code.indent(2)
410 pyx_code.named_insertion_point("numpy_dtype_checks")
411 self._buffer_check_numpy_dtype(pyx_code, buffer_types)
412 pyx_code.dedent(2)
414 for specialized_type in buffer_types:
415 self._buffer_parse_format_string_check(
416 pyx_code, decl_code, specialized_type, env)
418 pyx_code.putln(self.no_match)
419 pyx_code.putln("break")
420 pyx_code.dedent()
422 pyx_code.dedent()
423 else:
424 pyx_code.putln("else: %s" % self.no_match)
426 def _buffer_declarations(self, pyx_code, decl_code, all_buffer_types):
428 If we have any buffer specializations, write out some variable
429 declarations and imports.
431 decl_code.put_chunk(
432 u"""
433 ctypedef struct {{memviewslice_cname}}:
434 void *memview
436 void __PYX_XDEC_MEMVIEW({{memviewslice_cname}} *, int have_gil)
437 bint __pyx_memoryview_check(object)
438 """)
440 pyx_code.local_variable_declarations.put_chunk(
441 u"""
442 cdef {{memviewslice_cname}} memslice
443 cdef Py_ssize_t itemsize
444 cdef bint dtype_signed
445 cdef char kind
447 itemsize = -1
448 """)
450 pyx_code.imports.put_chunk(
451 u"""
452 try:
453 import numpy
454 except ImportError:
455 numpy = None
456 """)
458 seen_int_dtypes = set()
459 for buffer_type in all_buffer_types:
460 dtype = buffer_type.dtype
461 if dtype.is_typedef:
462 #decl_code.putln("ctypedef %s %s" % (dtype.resolve(),
463 # self._dtype_name(dtype)))
464 decl_code.putln('ctypedef %s %s "%s"' % (dtype.resolve(),
465 self._dtype_name(dtype),
466 dtype.declaration_code("")))
468 if buffer_type.dtype.is_int:
469 if str(dtype) not in seen_int_dtypes:
470 seen_int_dtypes.add(str(dtype))
471 pyx_code.context.update(dtype_name=self._dtype_name(dtype),
472 dtype_type=self._dtype_type(dtype))
473 pyx_code.local_variable_declarations.put_chunk(
474 u"""
475 cdef bint {{dtype_name}}_is_signed
476 {{dtype_name}}_is_signed = <{{dtype_type}}> -1 < 0
477 """)
479 def _split_fused_types(self, arg):
481 Specialize fused types and split into normal types and buffer types.
483 specialized_types = PyrexTypes.get_specialized_types(arg.type)
484 # Prefer long over int, etc
485 # specialized_types.sort()
486 seen_py_type_names = set()
487 normal_types, buffer_types = [], []
488 for specialized_type in specialized_types:
489 py_type_name = specialized_type.py_type_name()
490 if py_type_name:
491 if py_type_name in seen_py_type_names:
492 continue
493 seen_py_type_names.add(py_type_name)
494 normal_types.append(specialized_type)
495 elif specialized_type.is_buffer or specialized_type.is_memoryviewslice:
496 buffer_types.append(specialized_type)
498 return normal_types, buffer_types
500 def _unpack_argument(self, pyx_code):
501 pyx_code.put_chunk(
502 u"""
503 # PROCESSING ARGUMENT {{arg_tuple_idx}}
504 if {{arg_tuple_idx}} < len(args):
505 arg = args[{{arg_tuple_idx}}]
506 elif '{{arg.name}}' in kwargs:
507 arg = kwargs['{{arg.name}}']
508 else:
509 {{if arg.default:}}
510 arg = defaults[{{default_idx}}]
511 {{else}}
512 raise TypeError("Expected at least %d arguments" % len(args))
513 {{endif}}
514 """)
516 def make_fused_cpdef(self, orig_py_func, env, is_def):
518 This creates the function that is indexable from Python and does
519 runtime dispatch based on the argument types. The function gets the
520 arg tuple and kwargs dict (or None) and the defaults tuple
521 as arguments from the Binding Fused Function's tp_call.
523 from Cython.Compiler import TreeFragment, Code, MemoryView, UtilityCode
525 # { (arg_pos, FusedType) : specialized_type }
526 seen_fused_types = set()
528 context = {
529 'memviewslice_cname': MemoryView.memviewslice_cname,
530 'func_args': self.node.args,
531 'n_fused': len([arg for arg in self.node.args]),
532 'name': orig_py_func.entry.name,
535 pyx_code = Code.PyxCodeWriter(context=context)
536 decl_code = Code.PyxCodeWriter(context=context)
537 decl_code.put_chunk(
538 u"""
539 cdef extern from *:
540 void __pyx_PyErr_Clear "PyErr_Clear" ()
541 """)
542 decl_code.indent()
544 pyx_code.put_chunk(
545 u"""
546 def __pyx_fused_cpdef(signatures, args, kwargs, defaults):
547 dest_sig = [{{for _ in range(n_fused)}}None,{{endfor}}]
549 if kwargs is None:
550 kwargs = {}
552 cdef Py_ssize_t i
554 # instance check body
555 """)
556 pyx_code.indent() # indent following code to function body
557 pyx_code.named_insertion_point("imports")
558 pyx_code.named_insertion_point("local_variable_declarations")
560 fused_index = 0
561 default_idx = 0
562 all_buffer_types = set()
563 for i, arg in enumerate(self.node.args):
564 if arg.type.is_fused and arg.type not in seen_fused_types:
565 seen_fused_types.add(arg.type)
567 context.update(
568 arg_tuple_idx=i,
569 arg=arg,
570 dest_sig_idx=fused_index,
571 default_idx=default_idx,
574 normal_types, buffer_types = self._split_fused_types(arg)
575 self._unpack_argument(pyx_code)
576 self._fused_instance_checks(normal_types, pyx_code, env)
577 self._buffer_checks(buffer_types, pyx_code, decl_code, env)
578 fused_index += 1
580 all_buffer_types.update(buffer_types)
582 if arg.default:
583 default_idx += 1
585 if all_buffer_types:
586 self._buffer_declarations(pyx_code, decl_code, all_buffer_types)
587 env.use_utility_code(Code.UtilityCode.load_cached("Import", "ImportExport.c"))
589 pyx_code.put_chunk(
590 u"""
591 candidates = []
592 for sig in signatures:
593 match_found = False
594 for src_type, dst_type in zip(sig.strip('()').split('|'), dest_sig):
595 if dst_type is not None:
596 if src_type == dst_type:
597 match_found = True
598 else:
599 match_found = False
600 break
602 if match_found:
603 candidates.append(sig)
605 if not candidates:
606 raise TypeError("No matching signature found")
607 elif len(candidates) > 1:
608 raise TypeError("Function call with ambiguous argument types")
609 else:
610 return signatures[candidates[0]]
611 """)
613 fragment_code = pyx_code.getvalue()
614 # print decl_code.getvalue()
615 # print fragment_code
616 fragment = TreeFragment.TreeFragment(fragment_code, level='module')
617 ast = TreeFragment.SetPosTransform(self.node.pos)(fragment.root)
618 UtilityCode.declare_declarations_in_scope(decl_code.getvalue(),
619 env.global_scope())
620 ast.scope = env
621 ast.analyse_declarations(env)
622 py_func = ast.stats[-1] # the DefNode
623 self.fragment_scope = ast.scope
625 if isinstance(self.node, DefNode):
626 py_func.specialized_cpdefs = self.nodes[:]
627 else:
628 py_func.specialized_cpdefs = [n.py_func for n in self.nodes]
630 return py_func
632 def update_fused_defnode_entry(self, env):
633 copy_attributes = (
634 'name', 'pos', 'cname', 'func_cname', 'pyfunc_cname',
635 'pymethdef_cname', 'doc', 'doc_cname', 'is_member',
636 'scope'
639 entry = self.py_func.entry
641 for attr in copy_attributes:
642 setattr(entry, attr,
643 getattr(self.orig_py_func.entry, attr))
645 self.py_func.name = self.orig_py_func.name
646 self.py_func.doc = self.orig_py_func.doc
648 env.entries.pop('__pyx_fused_cpdef', None)
649 if isinstance(self.node, DefNode):
650 env.entries[entry.name] = entry
651 else:
652 env.entries[entry.name].as_variable = entry
654 env.pyfunc_entries.append(entry)
656 self.py_func.entry.fused_cfunction = self
657 for node in self.nodes:
658 if isinstance(self.node, DefNode):
659 node.fused_py_func = self.py_func
660 else:
661 node.py_func.fused_py_func = self.py_func
662 node.entry.as_variable = entry
664 self.synthesize_defnodes()
665 self.stats.append(self.__signatures__)
667 def analyse_expressions(self, env):
669 Analyse the expressions. Take care to only evaluate default arguments
670 once and clone the result for all specializations
672 for fused_compound_type in self.fused_compound_types:
673 for fused_type in fused_compound_type.get_fused_types():
674 for specialization_type in fused_type.types:
675 if specialization_type.is_complex:
676 specialization_type.create_declaration_utility_code(env)
678 if self.py_func:
679 self.__signatures__ = self.__signatures__.analyse_expressions(env)
680 self.py_func = self.py_func.analyse_expressions(env)
681 self.resulting_fused_function = self.resulting_fused_function.analyse_expressions(env)
682 self.fused_func_assignment = self.fused_func_assignment.analyse_expressions(env)
684 self.defaults = defaults = []
686 for arg in self.node.args:
687 if arg.default:
688 arg.default = arg.default.analyse_expressions(env)
689 defaults.append(ProxyNode(arg.default))
690 else:
691 defaults.append(None)
693 for i, stat in enumerate(self.stats):
694 stat = self.stats[i] = stat.analyse_expressions(env)
695 if isinstance(stat, FuncDefNode):
696 for arg, default in zip(stat.args, defaults):
697 if default is not None:
698 arg.default = CloneNode(default).coerce_to(arg.type, env)
700 if self.py_func:
701 args = [CloneNode(default) for default in defaults if default]
702 self.defaults_tuple = TupleNode(self.pos, args=args)
703 self.defaults_tuple = self.defaults_tuple.analyse_types(env, skip_children=True)
704 self.defaults_tuple = ProxyNode(self.defaults_tuple)
705 self.code_object = ProxyNode(self.specialized_pycfuncs[0].code_object)
707 fused_func = self.resulting_fused_function.arg
708 fused_func.defaults_tuple = CloneNode(self.defaults_tuple)
709 fused_func.code_object = CloneNode(self.code_object)
711 for i, pycfunc in enumerate(self.specialized_pycfuncs):
712 pycfunc.code_object = CloneNode(self.code_object)
713 pycfunc = self.specialized_pycfuncs[i] = pycfunc.analyse_types(env)
714 pycfunc.defaults_tuple = CloneNode(self.defaults_tuple)
715 return self
717 def synthesize_defnodes(self):
719 Create the __signatures__ dict of PyCFunctionNode specializations.
721 if isinstance(self.nodes[0], CFuncDefNode):
722 nodes = [node.py_func for node in self.nodes]
723 else:
724 nodes = self.nodes
726 signatures = [
727 StringEncoding.EncodedString(node.specialized_signature_string)
728 for node in nodes]
729 keys = [ExprNodes.StringNode(node.pos, value=sig)
730 for node, sig in zip(nodes, signatures)]
731 values = [ExprNodes.PyCFunctionNode.from_defnode(node, True)
732 for node in nodes]
733 self.__signatures__ = ExprNodes.DictNode.from_pairs(self.pos,
734 zip(keys, values))
736 self.specialized_pycfuncs = values
737 for pycfuncnode in values:
738 pycfuncnode.is_specialization = True
740 def generate_function_definitions(self, env, code):
741 if self.py_func:
742 self.py_func.pymethdef_required = True
743 self.fused_func_assignment.generate_function_definitions(env, code)
745 for stat in self.stats:
746 if isinstance(stat, FuncDefNode) and stat.entry.used:
747 code.mark_pos(stat.pos)
748 stat.generate_function_definitions(env, code)
750 def generate_execution_code(self, code):
751 # Note: all def function specialization are wrapped in PyCFunction
752 # nodes in the self.__signatures__ dictnode.
753 for default in self.defaults:
754 if default is not None:
755 default.generate_evaluation_code(code)
757 if self.py_func:
758 self.defaults_tuple.generate_evaluation_code(code)
759 self.code_object.generate_evaluation_code(code)
761 for stat in self.stats:
762 code.mark_pos(stat.pos)
763 if isinstance(stat, ExprNodes.ExprNode):
764 stat.generate_evaluation_code(code)
765 else:
766 stat.generate_execution_code(code)
768 if self.__signatures__:
769 self.resulting_fused_function.generate_evaluation_code(code)
771 code.putln(
772 "((__pyx_FusedFunctionObject *) %s)->__signatures__ = %s;" %
773 (self.resulting_fused_function.result(),
774 self.__signatures__.result()))
775 code.put_giveref(self.__signatures__.result())
777 self.fused_func_assignment.generate_execution_code(code)
779 # Dispose of results
780 self.resulting_fused_function.generate_disposal_code(code)
781 self.defaults_tuple.generate_disposal_code(code)
782 self.code_object.generate_disposal_code(code)
784 for default in self.defaults:
785 if default is not None:
786 default.generate_disposal_code(code)
788 def annotate(self, code):
789 for stat in self.stats:
790 stat.annotate(code)