3
cython.declare(UtilityCode=object, EncodedString=object, bytes_literal=object, encoded_string=object,
4
Nodes=object, ExprNodes=object, PyrexTypes=object, Builtin=object,
5
UtilNodes=object, _py_int_types=object,
6
re=object, copy=object, codecs=object, itertools=object, attrgetter=object)
13
from operator import attrgetter
15
from . import TypeSlots
16
from .ExprNodes import UnicodeNode, not_a_constant
18
_py_string_types = (bytes, str)
22
from . import ExprNodes
23
from . import PyrexTypes
26
from . import UtilNodes
29
from .Code import UtilityCode, TempitaUtilityCode
30
from .StringEncoding import EncodedString, bytes_literal, encoded_string
31
from .Errors import error, warning
32
from .ParseTreeTransforms import SkipDeclarations
35
from functools import reduce
38
def load_c_utility(name):
39
return UtilityCode.load_cached(name, "Optimize.c")
42
def unwrap_coerced_node(node, coercion_nodes=(ExprNodes.CoerceToPyTypeNode, ExprNodes.CoerceFromPyTypeNode)):
43
if isinstance(node, coercion_nodes):
49
while isinstance(node, UtilNodes.ResultRefNode):
50
node = node.expression
54
def is_common_value(a, b):
57
if isinstance(a, ExprNodes.NameNode) and isinstance(b, ExprNodes.NameNode):
58
return a.name == b.name
59
if isinstance(a, ExprNodes.AttributeNode) and isinstance(b, ExprNodes.AttributeNode):
60
return not a.is_py_attr and is_common_value(a.obj, b.obj) and a.attribute == b.attribute
64
def filter_none_node(node):
65
if node is not None and node.constant_result is None:
70
class _YieldNodeCollector(Visitor.TreeVisitor):
72
YieldExprNode finder for generator expressions.
75
Visitor.TreeVisitor.__init__(self)
76
self.yield_stat_nodes = {}
79
visit_Node = Visitor.TreeVisitor.visitchildren
81
def visit_YieldExprNode(self, node):
82
self.yield_nodes.append(node)
83
self.visitchildren(node)
85
def visit_ExprStatNode(self, node):
86
self.visitchildren(node)
87
if node.expr in self.yield_nodes:
88
self.yield_stat_nodes[node.expr] = node
92
def visit_GeneratorExpressionNode(self, node):
95
def visit_LambdaNode(self, node):
98
def visit_FuncDefNode(self, node):
102
def _find_single_yield_expression(node):
103
yield_statements = _find_yield_statements(node)
104
if len(yield_statements) != 1:
106
return yield_statements[0]
109
def _find_yield_statements(node):
110
collector = _YieldNodeCollector()
111
collector.visitchildren(node)
114
(yield_node.arg, collector.yield_stat_nodes[yield_node])
115
for yield_node in collector.yield_nodes
119
yield_statements = []
120
return yield_statements
123
class IterationTransform(Visitor.EnvTransform):
124
"""Transform some common for-in loop patterns into efficient C loops:
126
- for-in-dict loop becomes a while loop calling PyDict_Next()
127
- for-in-enumerate is replaced by an external counter variable
128
- for-in-range loop becomes a plain C for loop
130
def visit_PrimaryCmpNode(self, node):
131
if node.is_ptr_contains():
141
result_ref = UtilNodes.ResultRefNode(node)
142
if node.operand2.is_subscript:
143
base_type = node.operand2.base.type.base_type
145
base_type = node.operand2.type.base_type
146
target_handle = UtilNodes.TempHandle(base_type)
147
target = target_handle.ref(pos)
148
cmp_node = ExprNodes.PrimaryCmpNode(
149
pos, operator='==', operand1=node.operand1, operand2=target)
150
if_body = Nodes.StatListNode(
152
stats = [Nodes.SingleAssignmentNode(pos, lhs=result_ref, rhs=ExprNodes.BoolNode(pos, value=1)),
153
Nodes.BreakStatNode(pos)])
154
if_node = Nodes.IfStatNode(
156
if_clauses=[Nodes.IfClauseNode(pos, condition=cmp_node, body=if_body)],
158
for_loop = UtilNodes.TempsBlockNode(
160
temps = [target_handle],
161
body = Nodes.ForInStatNode(
164
iterator=ExprNodes.IteratorNode(node.operand2.pos, sequence=node.operand2),
166
else_clause=Nodes.SingleAssignmentNode(pos, lhs=result_ref, rhs=ExprNodes.BoolNode(pos, value=0))))
167
for_loop = for_loop.analyse_expressions(self.current_env())
168
for_loop = self.visit(for_loop)
169
new_node = UtilNodes.TempResultFromStatNode(result_ref, for_loop)
171
if node.operator == 'not_in':
172
new_node = ExprNodes.NotNode(pos, operand=new_node)
176
self.visitchildren(node)
179
def visit_ForInStatNode(self, node):
180
self.visitchildren(node)
181
return self._optimise_for_loop(node, node.iterator.sequence)
183
def _optimise_for_loop(self, node, iterable, reversed=False):
184
annotation_type = None
185
if (iterable.is_name or iterable.is_attribute) and iterable.entry and iterable.entry.annotation:
186
annotation = iterable.entry.annotation.expr
187
if annotation.is_subscript:
188
annotation = annotation.base
190
if Builtin.dict_type in (iterable.type, annotation_type):
195
return self._transform_dict_iteration(
196
node, dict_obj=iterable, method=None, keys=True, values=False)
198
if (Builtin.set_type in (iterable.type, annotation_type) or
199
Builtin.frozenset_type in (iterable.type, annotation_type)):
203
return self._transform_set_iteration(node, iterable)
206
if iterable.type.is_ptr or iterable.type.is_array:
207
return self._transform_carray_iteration(node, iterable, reversed=reversed)
208
if iterable.type is Builtin.bytes_type:
209
return self._transform_bytes_iteration(node, iterable, reversed=reversed)
210
if iterable.type is Builtin.unicode_type:
211
return self._transform_unicode_iteration(node, iterable, reversed=reversed)
214
if iterable.type is Builtin.bytearray_type:
215
return self._transform_indexable_iteration(node, iterable, is_mutable=True, reversed=reversed)
216
if isinstance(iterable, ExprNodes.CoerceToPyTypeNode) and iterable.arg.type.is_memoryviewslice:
217
return self._transform_indexable_iteration(node, iterable.arg, is_mutable=False, reversed=reversed)
220
if not isinstance(iterable, ExprNodes.SimpleCallNode):
223
if iterable.args is None:
224
arg_count = iterable.arg_tuple and len(iterable.arg_tuple.args) or 0
226
arg_count = len(iterable.args)
227
if arg_count and iterable.self is not None:
230
function = iterable.function
232
if function.is_attribute and not reversed and not arg_count:
233
base_obj = iterable.self or function.obj
234
method = function.attribute
236
is_safe_iter = self.global_scope().context.language_level >= 3
238
if not is_safe_iter and method in ('keys', 'values', 'items'):
240
if isinstance(base_obj, ExprNodes.CallNode):
241
inner_function = base_obj.function
242
if (inner_function.is_name and inner_function.name == 'dict'
243
and inner_function.entry
244
and inner_function.entry.is_builtin):
248
keys = values = False
249
if method == 'iterkeys' or (is_safe_iter and method == 'keys'):
251
elif method == 'itervalues' or (is_safe_iter and method == 'values'):
253
elif method == 'iteritems' or (is_safe_iter and method == 'items'):
257
return self._transform_dict_iteration(
258
node, base_obj, method, keys, values)
261
if iterable.self is None and function.is_name and \
262
function.entry and function.entry.is_builtin:
263
if function.name == 'enumerate':
267
return self._transform_enumerate_iteration(node, iterable)
268
elif function.name == 'reversed':
272
return self._transform_reversed_iteration(node, iterable)
275
if Options.convert_range and 1 <= arg_count <= 3 and (
276
iterable.self is None and
277
function.is_name and function.name in ('range', 'xrange') and
278
function.entry and function.entry.is_builtin):
279
if node.target.type.is_int or node.target.type.is_enum:
280
return self._transform_range_iteration(node, iterable, reversed=reversed)
281
if node.target.type.is_pyobject:
283
for arg in (iterable.arg_tuple.args if iterable.args is None else iterable.args):
284
if isinstance(arg, ExprNodes.IntNode):
285
if arg.has_constant_result() and -2**30 <= arg.constant_result < 2**30:
289
return self._transform_range_iteration(node, iterable, reversed=reversed)
293
def _transform_reversed_iteration(self, node, reversed_function):
294
args = reversed_function.arg_tuple.args
296
error(reversed_function.pos,
297
"reversed() requires an iterable argument")
300
error(reversed_function.pos,
301
"reversed() takes exactly 1 argument")
306
if arg.type in (Builtin.tuple_type, Builtin.list_type):
307
node.iterator.sequence = arg.as_none_safe_node("'NoneType' object is not iterable")
308
node.iterator.reversed = True
311
return self._optimise_for_loop(node, arg, reversed=True)
313
def _transform_indexable_iteration(self, node, slice_node, is_mutable, reversed=False):
314
"""In principle can handle any iterable that Cython has a len() for and knows how to index"""
315
unpack_temp_node = UtilNodes.LetRefNode(
316
slice_node.as_none_safe_node("'NoneType' is not iterable"),
317
may_hold_none=False, is_temp=True
320
start_node = ExprNodes.IntNode(
321
node.pos, value='0', constant_result=0, type=PyrexTypes.c_py_ssize_t_type)
322
def make_length_call():
324
builtin_len = ExprNodes.NameNode(node.pos, name="len",
325
entry=Builtin.builtin_scope.lookup("len"))
326
return ExprNodes.SimpleCallNode(node.pos,
327
function=builtin_len,
328
args=[unpack_temp_node]
330
length_temp = UtilNodes.LetRefNode(make_length_call(), type=PyrexTypes.c_py_ssize_t_type, is_temp=True)
331
end_node = length_temp
334
relation1, relation2 = '>', '>='
335
start_node, end_node = end_node, start_node
337
relation1, relation2 = '<=', '<'
339
counter_ref = UtilNodes.LetRefNode(pos=node.pos, type=PyrexTypes.c_py_ssize_t_type)
341
target_value = ExprNodes.IndexNode(slice_node.pos, base=unpack_temp_node,
344
target_assign = Nodes.SingleAssignmentNode(
345
pos = node.target.pos,
351
env = self.current_env()
352
new_directives = Options.copy_inherited_directives(env.directives, boundscheck=False, wraparound=False)
353
target_assign = Nodes.CompilerDirectivesNode(
355
directives=new_directives,
359
body = Nodes.StatListNode(
361
stats = [target_assign])
367
loop_length_reassign = Nodes.SingleAssignmentNode(node.pos,
369
rhs = make_length_call())
370
body.stats.append(loop_length_reassign)
372
loop_node = Nodes.ForFromStatNode(
374
bound1=start_node, relation1=relation1,
376
relation2=relation2, bound2=end_node,
377
step=None, body=body,
378
else_clause=node.else_clause,
381
ret = UtilNodes.LetNode(
388
Nodes.ExprStatNode(node.pos,
389
expr=UtilNodes.TempResultFromStatNode(
395
).analyse_expressions(env)
396
body.stats.insert(1, node.body)
399
PyBytes_AS_STRING_func_type = PyrexTypes.CFuncType(
400
PyrexTypes.c_char_ptr_type, [
401
PyrexTypes.CFuncTypeArg("s", Builtin.bytes_type, None)
402
], exception_value="NULL")
404
PyBytes_GET_SIZE_func_type = PyrexTypes.CFuncType(
405
PyrexTypes.c_py_ssize_t_type, [
406
PyrexTypes.CFuncTypeArg("s", Builtin.bytes_type, None)
410
def _transform_bytes_iteration(self, node, slice_node, reversed=False):
411
target_type = node.target.type
412
if not target_type.is_int and target_type is not Builtin.bytes_type:
417
unpack_temp_node = UtilNodes.LetRefNode(
418
slice_node.as_none_safe_node("'NoneType' is not iterable"))
420
slice_base_node = ExprNodes.PythonCapiCallNode(
421
slice_node.pos, "__Pyx_PyBytes_AsWritableString",
422
self.PyBytes_AS_STRING_func_type,
423
args = [unpack_temp_node],
427
len_node = ExprNodes.PythonCapiCallNode(
428
slice_node.pos, "__Pyx_PyBytes_GET_SIZE",
429
self.PyBytes_GET_SIZE_func_type,
430
args = [unpack_temp_node],
434
return UtilNodes.LetNode(
436
self._transform_carray_iteration(
438
ExprNodes.SliceIndexNode(
440
base = slice_base_node,
444
type = slice_base_node.type,
447
reversed = reversed))
449
PyUnicode_READ_func_type = PyrexTypes.CFuncType(
450
PyrexTypes.c_py_ucs4_type, [
451
PyrexTypes.CFuncTypeArg("kind", PyrexTypes.c_int_type, None),
452
PyrexTypes.CFuncTypeArg("data", PyrexTypes.c_void_ptr_type, None),
453
PyrexTypes.CFuncTypeArg("index", PyrexTypes.c_py_ssize_t_type, None)
456
init_unicode_iteration_func_type = PyrexTypes.CFuncType(
457
PyrexTypes.c_int_type, [
458
PyrexTypes.CFuncTypeArg("s", PyrexTypes.py_object_type, None),
459
PyrexTypes.CFuncTypeArg("length", PyrexTypes.c_py_ssize_t_ptr_type, None),
460
PyrexTypes.CFuncTypeArg("data", PyrexTypes.c_void_ptr_ptr_type, None),
461
PyrexTypes.CFuncTypeArg("kind", PyrexTypes.c_int_ptr_type, None)
465
def _transform_unicode_iteration(self, node, slice_node, reversed=False):
466
if slice_node.is_literal:
469
bytes_value = bytes_literal(slice_node.value.encode('latin1'), 'iso8859-1')
470
except UnicodeEncodeError:
473
bytes_slice = ExprNodes.SliceIndexNode(
475
base=ExprNodes.BytesNode(
476
slice_node.pos, value=bytes_value,
477
constant_result=bytes_value,
478
type=PyrexTypes.c_const_char_ptr_type).coerce_to(
479
PyrexTypes.c_const_uchar_ptr_type, self.current_env()),
481
stop=ExprNodes.IntNode(
482
slice_node.pos, value=str(len(bytes_value)),
483
constant_result=len(bytes_value),
484
type=PyrexTypes.c_py_ssize_t_type),
485
type=Builtin.unicode_type,
487
return self._transform_carray_iteration(node, bytes_slice, reversed)
489
unpack_temp_node = UtilNodes.LetRefNode(
490
slice_node.as_none_safe_node("'NoneType' is not iterable"))
492
start_node = ExprNodes.IntNode(
493
node.pos, value='0', constant_result=0, type=PyrexTypes.c_py_ssize_t_type)
494
length_temp = UtilNodes.TempHandle(PyrexTypes.c_py_ssize_t_type)
495
end_node = length_temp.ref(node.pos)
497
relation1, relation2 = '>', '>='
498
start_node, end_node = end_node, start_node
500
relation1, relation2 = '<=', '<'
502
kind_temp = UtilNodes.TempHandle(PyrexTypes.c_int_type)
503
data_temp = UtilNodes.TempHandle(PyrexTypes.c_void_ptr_type)
504
counter_temp = UtilNodes.TempHandle(PyrexTypes.c_py_ssize_t_type)
506
target_value = ExprNodes.PythonCapiCallNode(
507
slice_node.pos, "__Pyx_PyUnicode_READ",
508
self.PyUnicode_READ_func_type,
509
args = [kind_temp.ref(slice_node.pos),
510
data_temp.ref(slice_node.pos),
511
counter_temp.ref(node.target.pos)],
514
if target_value.type != node.target.type:
515
target_value = target_value.coerce_to(node.target.type,
517
target_assign = Nodes.SingleAssignmentNode(
518
pos = node.target.pos,
521
body = Nodes.StatListNode(
523
stats = [target_assign, node.body])
525
loop_node = Nodes.ForFromStatNode(
527
bound1=start_node, relation1=relation1,
528
target=counter_temp.ref(node.target.pos),
529
relation2=relation2, bound2=end_node,
530
step=None, body=body,
531
else_clause=node.else_clause,
534
setup_node = Nodes.ExprStatNode(
536
expr = ExprNodes.PythonCapiCallNode(
537
slice_node.pos, "__Pyx_init_unicode_iteration",
538
self.init_unicode_iteration_func_type,
539
args = [unpack_temp_node,
540
ExprNodes.AmpersandNode(slice_node.pos, operand=length_temp.ref(slice_node.pos),
541
type=PyrexTypes.c_py_ssize_t_ptr_type),
542
ExprNodes.AmpersandNode(slice_node.pos, operand=data_temp.ref(slice_node.pos),
543
type=PyrexTypes.c_void_ptr_ptr_type),
544
ExprNodes.AmpersandNode(slice_node.pos, operand=kind_temp.ref(slice_node.pos),
545
type=PyrexTypes.c_int_ptr_type),
548
result_is_used = False,
549
utility_code=UtilityCode.load_cached("unicode_iter", "Optimize.c"),
551
return UtilNodes.LetNode(
553
UtilNodes.TempsBlockNode(
554
node.pos, temps=[counter_temp, length_temp, data_temp, kind_temp],
555
body=Nodes.StatListNode(node.pos, stats=[setup_node, loop_node])))
557
def _transform_carray_iteration(self, node, slice_node, reversed=False):
559
if isinstance(slice_node, ExprNodes.SliceIndexNode):
560
slice_base = slice_node.base
561
start = filter_none_node(slice_node.start)
562
stop = filter_none_node(slice_node.stop)
565
if not slice_base.type.is_pyobject:
566
error(slice_node.pos, "C array iteration requires known end index")
569
elif slice_node.is_subscript:
570
assert isinstance(slice_node.index, ExprNodes.SliceNode)
571
slice_base = slice_node.base
572
index = slice_node.index
573
start = filter_none_node(index.start)
574
stop = filter_none_node(index.stop)
575
step = filter_none_node(index.step)
577
if not isinstance(step.constant_result, int) \
578
or step.constant_result == 0 \
579
or step.constant_result > 0 and not stop \
580
or step.constant_result < 0 and not start:
581
if not slice_base.type.is_pyobject:
582
error(step.pos, "C array iteration requires known step size and end index")
586
step_value = step.constant_result
588
step_value = -step_value
589
neg_step = step_value < 0
590
step = ExprNodes.IntNode(step.pos, type=PyrexTypes.c_py_ssize_t_type,
591
value=str(abs(step_value)),
592
constant_result=abs(step_value))
594
elif slice_node.type.is_array:
595
if slice_node.type.size is None:
596
error(slice_node.pos, "C array iteration requires known end index")
598
slice_base = slice_node
600
stop = ExprNodes.IntNode(
601
slice_node.pos, value=str(slice_node.type.size),
602
type=PyrexTypes.c_py_ssize_t_type, constant_result=slice_node.type.size)
606
if not slice_node.type.is_pyobject:
607
error(slice_node.pos, "C array iteration requires known end index")
611
start = start.coerce_to(PyrexTypes.c_py_ssize_t_type, self.current_env())
613
stop = stop.coerce_to(PyrexTypes.c_py_ssize_t_type, self.current_env())
616
stop = ExprNodes.IntNode(
617
slice_node.pos, value='-1', type=PyrexTypes.c_py_ssize_t_type, constant_result=-1)
619
error(slice_node.pos, "C array iteration requires known step size and end index")
624
start = ExprNodes.IntNode(slice_node.pos, value="0", constant_result=0,
625
type=PyrexTypes.c_py_ssize_t_type)
627
start, stop = stop, start
629
ptr_type = slice_base.type
630
if ptr_type.is_array:
631
ptr_type = ptr_type.element_ptr_type()
632
carray_ptr = slice_base.coerce_to_simple(self.current_env())
634
if start and start.constant_result != 0:
635
start_ptr_node = ExprNodes.AddNode(
642
start_ptr_node = carray_ptr
644
if stop and stop.constant_result != 0:
645
stop_ptr_node = ExprNodes.AddNode(
647
operand1=ExprNodes.CloneNode(carray_ptr),
651
).coerce_to_simple(self.current_env())
653
stop_ptr_node = ExprNodes.CloneNode(carray_ptr)
655
counter = UtilNodes.TempHandle(ptr_type)
656
counter_temp = counter.ref(node.target.pos)
658
if slice_base.type.is_string and node.target.type.is_pyobject:
660
if slice_node.type is Builtin.unicode_type:
661
target_value = ExprNodes.CastNode(
662
ExprNodes.DereferenceNode(
663
node.target.pos, operand=counter_temp,
664
type=ptr_type.base_type),
665
PyrexTypes.c_py_ucs4_type).coerce_to(
666
node.target.type, self.current_env())
669
target_value = ExprNodes.SliceIndexNode(
671
start=ExprNodes.IntNode(node.target.pos, value='0',
673
type=PyrexTypes.c_int_type),
674
stop=ExprNodes.IntNode(node.target.pos, value='1',
676
type=PyrexTypes.c_int_type),
678
type=Builtin.bytes_type,
680
elif node.target.type.is_ptr and not node.target.type.assignable_from(ptr_type.base_type):
682
target_value = counter_temp
685
target_value = ExprNodes.IndexNode(
687
index=ExprNodes.IntNode(node.target.pos, value='0',
689
type=PyrexTypes.c_int_type),
691
type=ptr_type.base_type)
693
if target_value.type != node.target.type:
694
target_value = target_value.coerce_to(node.target.type,
697
target_assign = Nodes.SingleAssignmentNode(
698
pos = node.target.pos,
702
body = Nodes.StatListNode(
704
stats = [target_assign, node.body])
706
relation1, relation2 = self._find_for_from_node_relations(neg_step, reversed)
708
for_node = Nodes.ForFromStatNode(
710
bound1=start_ptr_node, relation1=relation1,
712
relation2=relation2, bound2=stop_ptr_node,
713
step=step, body=body,
714
else_clause=node.else_clause,
717
return UtilNodes.TempsBlockNode(
718
node.pos, temps=[counter],
721
def _transform_enumerate_iteration(self, node, enumerate_function):
722
args = enumerate_function.arg_tuple.args
724
error(enumerate_function.pos,
725
"enumerate() requires an iterable argument")
728
error(enumerate_function.pos,
729
"enumerate() takes at most 2 arguments")
732
if not node.target.is_sequence_constructor:
735
targets = node.target.args
736
if len(targets) != 2:
740
enumerate_target, iterable_target = targets
741
counter_type = enumerate_target.type
743
if not counter_type.is_pyobject and not counter_type.is_int:
748
start = unwrap_coerced_node(args[1]).coerce_to(counter_type, self.current_env())
750
start = ExprNodes.IntNode(enumerate_function.pos,
754
temp = UtilNodes.LetRefNode(start)
756
inc_expression = ExprNodes.AddNode(
757
enumerate_function.pos,
759
operand2 = ExprNodes.IntNode(node.pos, value='1',
765
is_temp = counter_type.is_pyobject
769
Nodes.SingleAssignmentNode(
770
pos = enumerate_target.pos,
771
lhs = enumerate_target,
773
Nodes.SingleAssignmentNode(
774
pos = enumerate_target.pos,
776
rhs = inc_expression)
779
if isinstance(node.body, Nodes.StatListNode):
780
node.body.stats = loop_body + node.body.stats
782
loop_body.append(node.body)
783
node.body = Nodes.StatListNode(
787
node.target = iterable_target
788
node.item = node.item.coerce_to(iterable_target.type, self.current_env())
789
node.iterator.sequence = args[0]
792
return UtilNodes.LetNode(temp, self._optimise_for_loop(node, node.iterator.sequence))
794
def _find_for_from_node_relations(self, neg_step_value, reversed):
806
def _transform_range_iteration(self, node, range_function, reversed=False):
807
args = range_function.arg_tuple.args
809
step_pos = range_function.pos
811
step = ExprNodes.IntNode(step_pos, value='1', constant_result=1)
815
if not isinstance(step.constant_result, int):
818
step_value = step.constant_result
822
step = ExprNodes.IntNode(step_pos, value=str(step_value),
823
constant_result=step_value)
826
bound1 = ExprNodes.IntNode(range_function.pos, value='0',
828
bound2 = args[0].coerce_to_index(self.current_env())
830
bound1 = args[0].coerce_to_index(self.current_env())
831
bound2 = args[1].coerce_to_index(self.current_env())
833
relation1, relation2 = self._find_for_from_node_relations(step_value < 0, reversed)
835
bound2_ref_node = None
837
bound1, bound2 = bound2, bound1
838
abs_step = abs(step_value)
840
if (isinstance(bound1.constant_result, int) and
841
isinstance(bound2.constant_result, int)):
844
begin_value = bound2.constant_result
845
end_value = bound1.constant_result
846
bound1_value = begin_value - abs_step * ((begin_value - end_value - 1) // abs_step) - 1
848
begin_value = bound1.constant_result
849
end_value = bound2.constant_result
850
bound1_value = end_value + abs_step * ((begin_value - end_value - 1) // abs_step) + 1
852
bound1 = ExprNodes.IntNode(
853
bound1.pos, value=str(bound1_value), constant_result=bound1_value,
854
type=PyrexTypes.spanning_type(bound1.type, bound2.type))
857
bound2_ref_node = UtilNodes.LetRefNode(bound2)
858
bound1 = self._build_range_step_calculation(
859
bound1, bound2_ref_node, step, step_value)
862
step_value = -step_value
863
step.value = str(step_value)
864
step.constant_result = step_value
865
step = step.coerce_to_index(self.current_env())
867
if not bound2.is_literal:
869
bound2_is_temp = True
870
bound2 = bound2_ref_node or UtilNodes.LetRefNode(bound2)
872
bound2_is_temp = False
874
for_node = Nodes.ForFromStatNode(
877
bound1=bound1, relation1=relation1,
878
relation2=relation2, bound2=bound2,
879
step=step, body=node.body,
880
else_clause=node.else_clause,
882
for_node.set_up_loop(self.current_env())
885
for_node = UtilNodes.LetNode(bound2, for_node)
889
def _build_range_step_calculation(self, bound1, bound2_ref_node, step, step_value):
890
abs_step = abs(step_value)
891
spanning_type = PyrexTypes.spanning_type(bound1.type, bound2_ref_node.type)
892
if step.type.is_int and abs_step < 0x7FFF:
894
spanning_step_type = PyrexTypes.spanning_type(spanning_type, PyrexTypes.c_int_type)
896
spanning_step_type = PyrexTypes.spanning_type(spanning_type, step.type)
898
begin_value = bound2_ref_node
903
end_value = bound2_ref_node
906
step_calculation_node = ExprNodes.binop_node(
908
operand1=ExprNodes.binop_node(
910
operand1=bound2_ref_node,
912
operand2=ExprNodes.MulNode(
914
operand1=ExprNodes.IntNode(
917
constant_result=abs_step,
918
type=spanning_step_type),
920
operand2=ExprNodes.DivNode(
922
operand1=ExprNodes.SubNode(
924
operand1=ExprNodes.SubNode(
926
operand1=begin_value,
931
operand2=ExprNodes.IntNode(
935
type=spanning_step_type),
937
operand2=ExprNodes.IntNode(
940
constant_result=abs_step,
941
type=spanning_step_type),
942
type=spanning_step_type),
943
type=spanning_step_type),
944
type=spanning_step_type),
946
operand2=ExprNodes.IntNode(
951
return step_calculation_node
953
def _transform_dict_iteration(self, node, dict_obj, method, keys, values):
955
temp = UtilNodes.TempHandle(PyrexTypes.py_object_type)
957
dict_temp = temp.ref(dict_obj.pos)
958
temp = UtilNodes.TempHandle(PyrexTypes.c_py_ssize_t_type)
960
pos_temp = temp.ref(node.pos)
962
key_target = value_target = tuple_target = None
964
if node.target.is_sequence_constructor:
965
if len(node.target.args) == 2:
966
key_target, value_target = node.target.args
971
tuple_target = node.target
973
key_target = node.target
975
value_target = node.target
977
if isinstance(node.body, Nodes.StatListNode):
980
body = Nodes.StatListNode(pos = node.body.pos,
984
dict_len_temp = UtilNodes.TempHandle(PyrexTypes.c_py_ssize_t_type)
985
temps.append(dict_len_temp)
986
dict_len_temp_addr = ExprNodes.AmpersandNode(
987
node.pos, operand=dict_len_temp.ref(dict_obj.pos),
988
type=PyrexTypes.c_ptr_type(dict_len_temp.type))
989
temp = UtilNodes.TempHandle(PyrexTypes.c_int_type)
991
is_dict_temp = temp.ref(node.pos)
992
is_dict_temp_addr = ExprNodes.AmpersandNode(
993
node.pos, operand=is_dict_temp,
994
type=PyrexTypes.c_ptr_type(temp.type))
996
iter_next_node = Nodes.DictIterationNextNode(
997
dict_temp, dict_len_temp.ref(dict_obj.pos), pos_temp,
998
key_target, value_target, tuple_target,
1000
iter_next_node = iter_next_node.analyse_expressions(self.current_env())
1001
body.stats[0:0] = [iter_next_node]
1004
method_node = ExprNodes.IdentifierStringNode(dict_obj.pos, value=method)
1005
dict_obj = dict_obj.as_none_safe_node(
1006
"'NoneType' object has no attribute '%{}s'".format('.30' if len(method) <= 30 else ''),
1007
error = "PyExc_AttributeError",
1008
format_args = [method])
1010
method_node = ExprNodes.NullNode(dict_obj.pos)
1011
dict_obj = dict_obj.as_none_safe_node("'NoneType' object is not iterable")
1013
def flag_node(value):
1014
value = value and 1 or 0
1015
return ExprNodes.IntNode(node.pos, value=str(value), constant_result=value)
1018
Nodes.SingleAssignmentNode(
1021
rhs = ExprNodes.IntNode(node.pos, value='0',
1022
constant_result=0)),
1023
Nodes.SingleAssignmentNode(
1026
rhs = ExprNodes.PythonCapiCallNode(
1028
"__Pyx_dict_iterator",
1029
self.PyDict_Iterator_func_type,
1030
utility_code = UtilityCode.load_cached("dict_iter", "Optimize.c"),
1031
args = [dict_obj, flag_node(dict_obj.type is Builtin.dict_type),
1032
method_node, dict_len_temp_addr, is_dict_temp_addr,
1036
Nodes.WhileStatNode(
1040
else_clause = node.else_clause
1044
return UtilNodes.TempsBlockNode(
1045
node.pos, temps=temps,
1046
body=Nodes.StatListNode(
1051
PyDict_Iterator_func_type = PyrexTypes.CFuncType(
1052
PyrexTypes.py_object_type, [
1053
PyrexTypes.CFuncTypeArg("dict", PyrexTypes.py_object_type, None),
1054
PyrexTypes.CFuncTypeArg("is_dict", PyrexTypes.c_int_type, None),
1055
PyrexTypes.CFuncTypeArg("method_name", PyrexTypes.py_object_type, None),
1056
PyrexTypes.CFuncTypeArg("p_orig_length", PyrexTypes.c_py_ssize_t_ptr_type, None),
1057
PyrexTypes.CFuncTypeArg("p_is_dict", PyrexTypes.c_int_ptr_type, None),
1060
PySet_Iterator_func_type = PyrexTypes.CFuncType(
1061
PyrexTypes.py_object_type, [
1062
PyrexTypes.CFuncTypeArg("set", PyrexTypes.py_object_type, None),
1063
PyrexTypes.CFuncTypeArg("is_set", PyrexTypes.c_int_type, None),
1064
PyrexTypes.CFuncTypeArg("p_orig_length", PyrexTypes.c_py_ssize_t_ptr_type, None),
1065
PyrexTypes.CFuncTypeArg("p_is_set", PyrexTypes.c_int_ptr_type, None),
1068
def _transform_set_iteration(self, node, set_obj):
1070
temp = UtilNodes.TempHandle(PyrexTypes.py_object_type)
1072
set_temp = temp.ref(set_obj.pos)
1073
temp = UtilNodes.TempHandle(PyrexTypes.c_py_ssize_t_type)
1075
pos_temp = temp.ref(node.pos)
1077
if isinstance(node.body, Nodes.StatListNode):
1080
body = Nodes.StatListNode(pos = node.body.pos,
1081
stats = [node.body])
1084
set_len_temp = UtilNodes.TempHandle(PyrexTypes.c_py_ssize_t_type)
1085
temps.append(set_len_temp)
1086
set_len_temp_addr = ExprNodes.AmpersandNode(
1087
node.pos, operand=set_len_temp.ref(set_obj.pos),
1088
type=PyrexTypes.c_ptr_type(set_len_temp.type))
1089
temp = UtilNodes.TempHandle(PyrexTypes.c_int_type)
1091
is_set_temp = temp.ref(node.pos)
1092
is_set_temp_addr = ExprNodes.AmpersandNode(
1093
node.pos, operand=is_set_temp,
1094
type=PyrexTypes.c_ptr_type(temp.type))
1096
value_target = node.target
1097
iter_next_node = Nodes.SetIterationNextNode(
1098
set_temp, set_len_temp.ref(set_obj.pos), pos_temp, value_target, is_set_temp)
1099
iter_next_node = iter_next_node.analyse_expressions(self.current_env())
1100
body.stats[0:0] = [iter_next_node]
1102
def flag_node(value):
1103
value = value and 1 or 0
1104
return ExprNodes.IntNode(node.pos, value=str(value), constant_result=value)
1107
Nodes.SingleAssignmentNode(
1110
rhs=ExprNodes.IntNode(node.pos, value='0', constant_result=0)),
1111
Nodes.SingleAssignmentNode(
1114
rhs=ExprNodes.PythonCapiCallNode(
1116
"__Pyx_set_iterator",
1117
self.PySet_Iterator_func_type,
1118
utility_code=UtilityCode.load_cached("set_iter", "Optimize.c"),
1119
args=[set_obj, flag_node(set_obj.type is Builtin.set_type),
1120
set_len_temp_addr, is_set_temp_addr,
1124
Nodes.WhileStatNode(
1128
else_clause=node.else_clause,
1132
return UtilNodes.TempsBlockNode(
1133
node.pos, temps=temps,
1134
body=Nodes.StatListNode(
1140
class SwitchTransform(Visitor.EnvTransform):
1142
This transformation tries to turn long if statements into C switch statements.
1143
The requirement is that every clause be an (or of) var == value, where the var
1144
is common among all clauses and both var and value are ints.
1146
NO_MATCH = (None, None, None)
1148
def extract_conditions(self, cond, allow_not_in):
1150
if isinstance(cond, (ExprNodes.CoerceToTempNode,
1151
ExprNodes.CoerceToBooleanNode)):
1153
elif isinstance(cond, ExprNodes.BoolBinopResultNode):
1155
elif isinstance(cond, UtilNodes.EvalWithTempExprNode):
1157
cond = cond.subexpression
1158
elif isinstance(cond, ExprNodes.TypecastNode):
1163
if isinstance(cond, ExprNodes.PrimaryCmpNode):
1164
if cond.cascade is not None:
1165
return self.NO_MATCH
1166
elif cond.is_c_string_contains() and \
1167
isinstance(cond.operand2, (ExprNodes.UnicodeNode, ExprNodes.BytesNode)):
1168
not_in = cond.operator == 'not_in'
1169
if not_in and not allow_not_in:
1170
return self.NO_MATCH
1171
if isinstance(cond.operand2, ExprNodes.UnicodeNode) and \
1172
cond.operand2.contains_surrogates():
1176
return self.NO_MATCH
1177
return not_in, cond.operand1, self.extract_in_string_conditions(cond.operand2)
1178
elif not cond.is_python_comparison():
1179
if cond.operator == '==':
1181
elif allow_not_in and cond.operator == '!=':
1184
return self.NO_MATCH
1187
if is_common_value(cond.operand1, cond.operand1):
1188
if cond.operand2.is_literal:
1189
return not_in, cond.operand1, [cond.operand2]
1190
elif getattr(cond.operand2, 'entry', None) \
1191
and cond.operand2.entry.is_const:
1192
return not_in, cond.operand1, [cond.operand2]
1193
if is_common_value(cond.operand2, cond.operand2):
1194
if cond.operand1.is_literal:
1195
return not_in, cond.operand2, [cond.operand1]
1196
elif getattr(cond.operand1, 'entry', None) \
1197
and cond.operand1.entry.is_const:
1198
return not_in, cond.operand2, [cond.operand1]
1199
elif isinstance(cond, ExprNodes.BoolBinopNode):
1200
if cond.operator == 'or' or (allow_not_in and cond.operator == 'and'):
1201
allow_not_in = (cond.operator == 'and')
1202
not_in_1, t1, c1 = self.extract_conditions(cond.operand1, allow_not_in)
1203
not_in_2, t2, c2 = self.extract_conditions(cond.operand2, allow_not_in)
1204
if t1 is not None and not_in_1 == not_in_2 and is_common_value(t1, t2):
1205
if (not not_in_1) or allow_not_in:
1206
return not_in_1, t1, c1+c2
1207
return self.NO_MATCH
1209
def extract_in_string_conditions(self, string_literal):
1210
if isinstance(string_literal, ExprNodes.UnicodeNode):
1211
charvals = list(map(ord, set(string_literal.value)))
1213
return [ ExprNodes.IntNode(string_literal.pos, value=str(charval),
1214
constant_result=charval)
1215
for charval in charvals ]
1220
characters = string_literal.value
1221
characters = list({ characters[i:i+1] for i in range(len(characters)) })
1223
return [ ExprNodes.CharNode(string_literal.pos, value=charval,
1224
constant_result=charval)
1225
for charval in characters ]
1227
def extract_common_conditions(self, common_var, condition, allow_not_in):
1228
not_in, var, conditions = self.extract_conditions(condition, allow_not_in)
1230
return self.NO_MATCH
1231
elif common_var is not None and not is_common_value(var, common_var):
1232
return self.NO_MATCH
1233
elif not (var.type.is_int or var.type.is_enum) or any(
1234
[not (cond.type.is_int or cond.type.is_enum) for cond in conditions]):
1235
return self.NO_MATCH
1236
return not_in, var, conditions
1238
def has_duplicate_values(self, condition_values):
1241
for value in condition_values:
1242
if value.has_constant_result():
1243
if value.constant_result in seen:
1245
seen.add(value.constant_result)
1250
value_entry = value.entry
1251
if ((value_entry.type.is_enum or value_entry.type.is_cpp_enum)
1252
and value_entry.enum_int_value is not None):
1253
value_for_seen = value_entry.enum_int_value
1255
value_for_seen = value_entry.cname
1256
except AttributeError:
1258
if value_for_seen in seen:
1260
seen.add(value_for_seen)
1263
def visit_IfStatNode(self, node):
1264
if not self.current_directives.get('optimize.use_switch'):
1265
self.visitchildren(node)
1270
for if_clause in node.if_clauses:
1271
_, common_var, conditions = self.extract_common_conditions(
1272
common_var, if_clause.condition, False)
1273
if common_var is None:
1274
self.visitchildren(node)
1276
cases.append(Nodes.SwitchCaseNode(pos=if_clause.pos,
1277
conditions=conditions,
1278
body=if_clause.body))
1280
condition_values = [
1281
cond for case in cases for cond in case.conditions]
1282
if len(condition_values) < 2:
1283
self.visitchildren(node)
1285
if self.has_duplicate_values(condition_values):
1286
self.visitchildren(node)
1290
self.visitchildren(node, 'else_clause')
1292
self.visitchildren(case, 'body')
1294
common_var = unwrap_node(common_var)
1295
switch_node = Nodes.SwitchStatNode(pos=node.pos,
1298
else_clause=node.else_clause)
1301
def visit_CondExprNode(self, node):
1302
if not self.current_directives.get('optimize.use_switch'):
1303
self.visitchildren(node)
1306
not_in, common_var, conditions = self.extract_common_conditions(
1307
None, node.test, True)
1308
if common_var is None \
1309
or len(conditions) < 2 \
1310
or self.has_duplicate_values(conditions):
1311
self.visitchildren(node)
1314
return self.build_simple_switch_statement(
1315
node, common_var, conditions, not_in,
1316
node.true_val, node.false_val)
1318
def visit_BoolBinopNode(self, node):
1319
if not self.current_directives.get('optimize.use_switch'):
1320
self.visitchildren(node)
1323
not_in, common_var, conditions = self.extract_common_conditions(
1325
if common_var is None \
1326
or len(conditions) < 2 \
1327
or self.has_duplicate_values(conditions):
1328
self.visitchildren(node)
1329
node.wrap_operands(self.current_env())
1332
return self.build_simple_switch_statement(
1333
node, common_var, conditions, not_in,
1334
ExprNodes.BoolNode(node.pos, value=True, constant_result=True),
1335
ExprNodes.BoolNode(node.pos, value=False, constant_result=False))
1337
def visit_PrimaryCmpNode(self, node):
1338
if not self.current_directives.get('optimize.use_switch'):
1339
self.visitchildren(node)
1342
not_in, common_var, conditions = self.extract_common_conditions(
1344
if common_var is None \
1345
or len(conditions) < 2 \
1346
or self.has_duplicate_values(conditions):
1347
self.visitchildren(node)
1350
return self.build_simple_switch_statement(
1351
node, common_var, conditions, not_in,
1352
ExprNodes.BoolNode(node.pos, value=True, constant_result=True),
1353
ExprNodes.BoolNode(node.pos, value=False, constant_result=False))
1355
def build_simple_switch_statement(self, node, common_var, conditions,
1356
not_in, true_val, false_val):
1357
result_ref = UtilNodes.ResultRefNode(node)
1358
true_body = Nodes.SingleAssignmentNode(
1361
rhs=true_val.coerce_to(node.type, self.current_env()),
1363
false_body = Nodes.SingleAssignmentNode(
1366
rhs=false_val.coerce_to(node.type, self.current_env()),
1370
true_body, false_body = false_body, true_body
1372
cases = [Nodes.SwitchCaseNode(pos = node.pos,
1373
conditions = conditions,
1376
common_var = unwrap_node(common_var)
1377
switch_node = Nodes.SwitchStatNode(pos = node.pos,
1380
else_clause = false_body)
1381
replacement = UtilNodes.TempResultFromStatNode(result_ref, switch_node)
1384
def visit_EvalWithTempExprNode(self, node):
1385
if not self.current_directives.get('optimize.use_switch'):
1386
self.visitchildren(node)
1390
orig_expr = node.subexpression
1391
temp_ref = node.lazy_temp
1392
self.visitchildren(node)
1393
if node.subexpression is not orig_expr:
1395
if not Visitor.tree_contains(node.subexpression, temp_ref):
1396
return node.subexpression
1399
visit_Node = Visitor.VisitorTransform.recurse_to_children
1402
class FlattenInListTransform(Visitor.VisitorTransform, SkipDeclarations):
1404
This transformation flattens "x in [val1, ..., valn]" into a sequential list
1408
def visit_PrimaryCmpNode(self, node):
1409
self.visitchildren(node)
1410
if node.cascade is not None:
1412
elif node.operator == 'in':
1415
elif node.operator == 'not_in':
1421
if not isinstance(node.operand2, (ExprNodes.TupleNode,
1423
ExprNodes.SetNode)):
1427
args = node.operand2.args
1430
if lhs.try_is_simple():
1431
constant_result = node.operator == 'not_in'
1432
return ExprNodes.BoolNode(node.pos, value=constant_result, constant_result=constant_result)
1435
if any([arg.is_starred for arg in args]):
1439
lhs = UtilNodes.ResultRefNode(lhs)
1445
if not arg.try_is_simple():
1447
arg = UtilNodes.LetRefNode(arg)
1449
cond = ExprNodes.PrimaryCmpNode(
1452
operator = eq_or_neq,
1455
conds.append(ExprNodes.TypecastNode(
1458
type = PyrexTypes.c_bint_type))
1459
def concat(left, right):
1460
return ExprNodes.BoolBinopNode(
1462
operator = conjunction,
1466
condition = reduce(concat, conds)
1467
new_node = UtilNodes.EvalWithTempExprNode(lhs, condition)
1468
for temp in temps[::-1]:
1469
new_node = UtilNodes.EvalWithTempExprNode(temp, new_node)
1472
visit_Node = Visitor.VisitorTransform.recurse_to_children
1475
class DropRefcountingTransform(Visitor.VisitorTransform):
1476
"""Drop ref-counting in safe places.
1478
visit_Node = Visitor.VisitorTransform.recurse_to_children
1480
def visit_ParallelAssignmentNode(self, node):
1482
Parallel swap assignments like 'a,b = b,a' are safe.
1484
left_names, right_names = [], []
1485
left_indices, right_indices = [], []
1488
for stat in node.stats:
1489
if isinstance(stat, Nodes.SingleAssignmentNode):
1490
if not self._extract_operand(stat.lhs, left_names,
1491
left_indices, temps):
1493
if not self._extract_operand(stat.rhs, right_names,
1494
right_indices, temps):
1496
elif isinstance(stat, Nodes.CascadedAssignmentNode):
1502
if left_names or right_names:
1504
lnames = [ path for path, n in left_names ]
1505
rnames = [ path for path, n in right_names ]
1506
if set(lnames) != set(rnames):
1508
if len(set(lnames)) != len(right_names):
1511
if left_indices or right_indices:
1515
for lhs_node in left_indices:
1516
index_id = self._extract_index_id(lhs_node)
1519
lindices.append(index_id)
1521
for rhs_node in right_indices:
1522
index_id = self._extract_index_id(rhs_node)
1525
rindices.append(index_id)
1527
if set(lindices) != set(rindices):
1529
if len(set(lindices)) != len(right_indices):
1536
temp_args = [t.arg for t in temps]
1538
temp.use_managed_ref = False
1540
for _, name_node in left_names + right_names:
1541
if name_node not in temp_args:
1542
name_node.use_managed_ref = False
1544
for index_node in left_indices + right_indices:
1545
index_node.use_managed_ref = False
1549
def _extract_operand(self, node, names, indices, temps):
1550
node = unwrap_node(node)
1551
if not node.type.is_pyobject:
1553
if isinstance(node, ExprNodes.CoerceToTempNode):
1558
while obj_node.is_attribute:
1559
if obj_node.is_py_attr:
1561
name_path.append(obj_node.member)
1562
obj_node = obj_node.obj
1563
if obj_node.is_name:
1564
name_path.append(obj_node.name)
1565
names.append( ('.'.join(name_path[::-1]), node) )
1566
elif node.is_subscript:
1567
if node.base.type != Builtin.list_type:
1569
if not node.index.type.is_int:
1571
if not node.base.is_name:
1573
indices.append(node)
1578
def _extract_index_id(self, index_node):
1579
base = index_node.base
1580
index = index_node.index
1581
if isinstance(index, ExprNodes.NameNode):
1582
index_val = index.name
1583
elif isinstance(index, ExprNodes.ConstNode):
1588
return (base.name, index_val)
1591
class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
1592
"""Optimize some common calls to builtin types *before* the type
1593
analysis phase and *after* the declarations analysis phase.
1595
This transform cannot make use of any argument types, but it can
1596
restructure the tree in a way that the type analysis phase can
1599
Introducing C function calls here may not be a good idea. Move
1600
them to the OptimizeBuiltinCalls transform instead, which runs
1601
after type analysis.
1604
visit_Node = Visitor.VisitorTransform.recurse_to_children
1606
def visit_SimpleCallNode(self, node):
1607
self.visitchildren(node)
1608
function = node.function
1609
if not self._function_is_builtin_name(function):
1611
return self._dispatch_to_handler(node, function, node.args)
1613
def visit_GeneralCallNode(self, node):
1614
self.visitchildren(node)
1615
function = node.function
1616
if not self._function_is_builtin_name(function):
1618
arg_tuple = node.positional_args
1619
if not isinstance(arg_tuple, ExprNodes.TupleNode):
1621
args = arg_tuple.args
1622
return self._dispatch_to_handler(
1623
node, function, args, node.keyword_args)
1625
def _function_is_builtin_name(self, function):
1626
if not function.is_name:
1628
env = self.current_env()
1629
entry = env.lookup(function.name)
1630
if entry is not env.builtin_scope().lookup_here(function.name):
1635
def _dispatch_to_handler(self, node, function, args, kwargs=None):
1637
handler_name = '_handle_simple_function_%s' % function.name
1639
handler_name = '_handle_general_function_%s' % function.name
1640
handle_call = getattr(self, handler_name, None)
1641
if handle_call is not None:
1643
return handle_call(node, args)
1645
return handle_call(node, args, kwargs)
1648
def _inject_capi_function(self, node, cname, func_type, utility_code=None):
1649
node.function = ExprNodes.PythonCapiFunctionNode(
1650
node.function.pos, node.function.name, cname, func_type,
1651
utility_code = utility_code)
1653
def _error_wrong_arg_count(self, function_name, node, args, expected=None):
1656
elif isinstance(expected, str) or expected > 1:
1662
if expected is not None:
1663
expected_str = 'expected %s, ' % expected
1666
error(node.pos, "%s(%s) called with wrong number of args, %sfound %d" % (
1667
function_name, arg_str, expected_str, len(args)))
1671
def _handle_simple_function_float(self, node, pos_args):
1673
return ExprNodes.FloatNode(node.pos, value='0.0')
1674
if len(pos_args) > 1:
1675
self._error_wrong_arg_count('float', node, pos_args, 1)
1676
arg_type = getattr(pos_args[0], 'type', None)
1677
if arg_type in (PyrexTypes.c_double_type, Builtin.float_type):
1681
def _handle_simple_function_slice(self, node, pos_args):
1682
arg_count = len(pos_args)
1686
elif arg_count == 2:
1687
start, stop = pos_args
1688
elif arg_count == 3:
1689
start, stop, step = pos_args
1691
self._error_wrong_arg_count('slice', node, pos_args)
1693
return ExprNodes.SliceNode(
1695
start=start or ExprNodes.NoneNode(node.pos),
1697
step=step or ExprNodes.NoneNode(node.pos))
1699
def _handle_simple_function_ord(self, node, pos_args):
1702
if len(pos_args) != 1:
1705
if isinstance(arg, (ExprNodes.UnicodeNode, ExprNodes.BytesNode)):
1706
if len(arg.value) == 1:
1707
return ExprNodes.IntNode(
1708
arg.pos, type=PyrexTypes.c_long_type,
1709
value=str(ord(arg.value)),
1710
constant_result=ord(arg.value)
1716
def _handle_simple_function_all(self, node, pos_args):
1719
_result = all(p(x) for L in LL for x in L)
1730
return self._transform_any_all(node, pos_args, False)
1732
def _handle_simple_function_any(self, node, pos_args):
1735
_result = any(p(x) for L in LL for x in L)
1746
return self._transform_any_all(node, pos_args, True)
1748
def _transform_any_all(self, node, pos_args, is_any):
1749
if len(pos_args) != 1:
1751
if not isinstance(pos_args[0], ExprNodes.GeneratorExpressionNode):
1753
gen_expr_node = pos_args[0]
1754
generator_body = gen_expr_node.def_node.gbody
1755
loop_node = generator_body.body
1756
yield_expression, yield_stat_node = _find_single_yield_expression(loop_node)
1757
if yield_expression is None:
1761
condition = yield_expression
1763
condition = ExprNodes.NotNode(yield_expression.pos, operand=yield_expression)
1765
test_node = Nodes.IfStatNode(
1766
yield_expression.pos, else_clause=None, if_clauses=[
1768
yield_expression.pos,
1769
condition=condition,
1770
body=Nodes.ReturnStatNode(
1772
value=ExprNodes.BoolNode(yield_expression.pos, value=is_any, constant_result=is_any))
1775
loop_node.else_clause = Nodes.ReturnStatNode(
1777
value=ExprNodes.BoolNode(yield_expression.pos, value=not is_any, constant_result=not is_any))
1779
Visitor.recursively_replace_node(gen_expr_node, yield_stat_node, test_node)
1781
return ExprNodes.InlinedGeneratorExpressionNode(
1782
gen_expr_node.pos, gen=gen_expr_node, orig_func='any' if is_any else 'all')
1784
PySequence_List_func_type = PyrexTypes.CFuncType(
1786
[PyrexTypes.CFuncTypeArg("it", PyrexTypes.py_object_type, None)])
1788
def _handle_simple_function_sorted(self, node, pos_args):
1789
"""Transform sorted(genexpr) and sorted([listcomp]) into
1790
[listcomp].sort(). CPython just reads the iterable into a
1791
list and calls .sort() on it. Expanding the iterable in a
1792
listcomp is still faster and the result can be sorted in
1795
if len(pos_args) != 1:
1799
if isinstance(arg, ExprNodes.ComprehensionNode) and arg.type is Builtin.list_type:
1801
loop_node = list_node.loop
1803
elif isinstance(arg, ExprNodes.GeneratorExpressionNode):
1805
loop_node = gen_expr_node.loop
1806
yield_statements = _find_yield_statements(loop_node)
1807
if not yield_statements:
1810
list_node = ExprNodes.InlinedGeneratorExpressionNode(
1811
node.pos, gen_expr_node, orig_func='sorted',
1812
comprehension_type=Builtin.list_type)
1814
for yield_expression, yield_stat_node in yield_statements:
1815
append_node = ExprNodes.ComprehensionAppendNode(
1816
yield_expression.pos,
1817
expr=yield_expression,
1818
target=list_node.target)
1819
Visitor.recursively_replace_node(gen_expr_node, yield_stat_node, append_node)
1821
elif arg.is_sequence_constructor:
1824
list_node = loop_node = arg.as_list()
1829
list_node = loop_node = ExprNodes.PythonCapiCallNode(
1831
"__Pyx_PySequence_ListKeepNew"
1832
if arg.is_temp and arg.type in (PyrexTypes.py_object_type, Builtin.list_type)
1833
else "PySequence_List",
1834
self.PySequence_List_func_type,
1835
args=pos_args, is_temp=True)
1837
result_node = UtilNodes.ResultRefNode(
1838
pos=loop_node.pos, type=Builtin.list_type, may_hold_none=False)
1839
list_assign_node = Nodes.SingleAssignmentNode(
1840
node.pos, lhs=result_node, rhs=list_node, first=True)
1842
sort_method = ExprNodes.AttributeNode(
1843
node.pos, obj=result_node, attribute=EncodedString('sort'),
1845
needs_none_check=False)
1846
sort_node = Nodes.ExprStatNode(
1847
node.pos, expr=ExprNodes.SimpleCallNode(
1848
node.pos, function=sort_method, args=[]))
1850
sort_node.analyse_declarations(self.current_env())
1852
return UtilNodes.TempResultFromStatNode(
1854
Nodes.StatListNode(node.pos, stats=[list_assign_node, sort_node]))
1856
def __handle_simple_function_sum(self, node, pos_args):
1857
"""Transform sum(genexpr) into an equivalent inlined aggregation loop.
1859
if len(pos_args) not in (1,2):
1861
if not isinstance(pos_args[0], (ExprNodes.GeneratorExpressionNode,
1862
ExprNodes.ComprehensionNode)):
1864
gen_expr_node = pos_args[0]
1865
loop_node = gen_expr_node.loop
1867
if isinstance(gen_expr_node, ExprNodes.GeneratorExpressionNode):
1868
yield_expression, yield_stat_node = _find_single_yield_expression(loop_node)
1870
yield_expression = None
1871
if yield_expression is None:
1874
yield_stat_node = gen_expr_node.append
1875
yield_expression = yield_stat_node.expr
1877
if not yield_expression.is_literal or not yield_expression.type.is_int:
1879
except AttributeError:
1884
if len(pos_args) == 1:
1885
start = ExprNodes.IntNode(node.pos, value='0', constant_result=0)
1889
result_ref = UtilNodes.ResultRefNode(pos=node.pos, type=PyrexTypes.py_object_type)
1890
add_node = Nodes.SingleAssignmentNode(
1891
yield_expression.pos,
1893
rhs = ExprNodes.binop_node(node.pos, '+', result_ref, yield_expression)
1896
Visitor.recursively_replace_node(gen_expr_node, yield_stat_node, add_node)
1898
exec_code = Nodes.StatListNode(
1901
Nodes.SingleAssignmentNode(
1903
lhs = UtilNodes.ResultRefNode(pos=node.pos, expression=result_ref),
1909
return ExprNodes.InlinedGeneratorExpressionNode(
1910
gen_expr_node.pos, loop = exec_code, result_node = result_ref,
1911
expr_scope = gen_expr_node.expr_scope, orig_func = 'sum',
1912
has_local_scope = gen_expr_node.has_local_scope)
1914
def _handle_simple_function_min(self, node, pos_args):
1915
return self._optimise_min_max(node, pos_args, '<')
1917
def _handle_simple_function_max(self, node, pos_args):
1918
return self._optimise_min_max(node, pos_args, '>')
1920
def _optimise_min_max(self, node, args, operator):
1921
"""Replace min(a,b,...) and max(a,b,...) by explicit comparison code.
1924
if len(args) == 1 and args[0].is_sequence_constructor:
1930
cascaded_nodes = list(map(UtilNodes.ResultRefNode, args[1:]))
1932
last_result = args[0]
1933
for arg_node in cascaded_nodes:
1934
result_ref = UtilNodes.ResultRefNode(last_result)
1935
last_result = ExprNodes.CondExprNode(
1937
true_val = arg_node,
1938
false_val = result_ref,
1939
test = ExprNodes.PrimaryCmpNode(
1941
operand1 = arg_node,
1942
operator = operator,
1943
operand2 = result_ref,
1946
last_result = UtilNodes.EvalWithTempExprNode(result_ref, last_result)
1948
for ref_node in cascaded_nodes[::-1]:
1949
last_result = UtilNodes.EvalWithTempExprNode(ref_node, last_result)
1955
def _DISABLED_handle_simple_function_tuple(self, node, pos_args):
1957
return ExprNodes.TupleNode(node.pos, args=[], constant_result=())
1965
result = self._transform_list_set_genexpr(node, pos_args, Builtin.list_type)
1966
if result is not node:
1967
return ExprNodes.AsTupleNode(node.pos, arg=result)
1970
def _handle_simple_function_frozenset(self, node, pos_args):
1971
"""Replace frozenset([...]) by frozenset((...)) as tuples are more efficient.
1973
if len(pos_args) != 1:
1975
if pos_args[0].is_sequence_constructor and not pos_args[0].args:
1977
elif isinstance(pos_args[0], ExprNodes.ListNode):
1978
pos_args[0] = pos_args[0].as_tuple()
1981
def _handle_simple_function_list(self, node, pos_args):
1983
return ExprNodes.ListNode(node.pos, args=[], constant_result=[])
1984
return self._transform_list_set_genexpr(node, pos_args, Builtin.list_type)
1986
def _handle_simple_function_set(self, node, pos_args):
1988
return ExprNodes.SetNode(node.pos, args=[], constant_result=set())
1989
return self._transform_list_set_genexpr(node, pos_args, Builtin.set_type)
1991
def _transform_list_set_genexpr(self, node, pos_args, target_type):
1992
"""Replace set(genexpr) and list(genexpr) by an inlined comprehension.
1994
if len(pos_args) > 1:
1996
if not isinstance(pos_args[0], ExprNodes.GeneratorExpressionNode):
1998
gen_expr_node = pos_args[0]
1999
loop_node = gen_expr_node.loop
2001
yield_statements = _find_yield_statements(loop_node)
2002
if not yield_statements:
2005
result_node = ExprNodes.InlinedGeneratorExpressionNode(
2006
node.pos, gen_expr_node,
2007
orig_func='set' if target_type is Builtin.set_type else 'list',
2008
comprehension_type=target_type)
2010
for yield_expression, yield_stat_node in yield_statements:
2011
append_node = ExprNodes.ComprehensionAppendNode(
2012
yield_expression.pos,
2013
expr=yield_expression,
2014
target=result_node.target)
2015
Visitor.recursively_replace_node(gen_expr_node, yield_stat_node, append_node)
2019
def _handle_simple_function_dict(self, node, pos_args):
2020
"""Replace dict( (a,b) for ... ) by an inlined { a:b for ... }
2022
if len(pos_args) == 0:
2023
return ExprNodes.DictNode(node.pos, key_value_pairs=[], constant_result={})
2024
if len(pos_args) > 1:
2026
if not isinstance(pos_args[0], ExprNodes.GeneratorExpressionNode):
2028
gen_expr_node = pos_args[0]
2029
loop_node = gen_expr_node.loop
2031
yield_statements = _find_yield_statements(loop_node)
2032
if not yield_statements:
2035
for yield_expression, _ in yield_statements:
2036
if not isinstance(yield_expression, ExprNodes.TupleNode):
2038
if len(yield_expression.args) != 2:
2041
result_node = ExprNodes.InlinedGeneratorExpressionNode(
2042
node.pos, gen_expr_node, orig_func='dict',
2043
comprehension_type=Builtin.dict_type)
2045
for yield_expression, yield_stat_node in yield_statements:
2046
append_node = ExprNodes.DictComprehensionAppendNode(
2047
yield_expression.pos,
2048
key_expr=yield_expression.args[0],
2049
value_expr=yield_expression.args[1],
2050
target=result_node.target)
2051
Visitor.recursively_replace_node(gen_expr_node, yield_stat_node, append_node)
2057
def _handle_general_function_dict(self, node, pos_args, kwargs):
2058
"""Replace dict(a=b,c=d,...) by the underlying keyword dict
2059
construction which is done anyway.
2061
if len(pos_args) > 0:
2063
if not isinstance(kwargs, ExprNodes.DictNode):
2068
class InlineDefNodeCalls(Visitor.NodeRefCleanupMixin, Visitor.EnvTransform):
2069
visit_Node = Visitor.VisitorTransform.recurse_to_children
2071
def get_constant_value_node(self, name_node):
2072
if name_node.cf_state is None:
2074
if name_node.cf_state.cf_is_null:
2076
entry = self.current_env().lookup(name_node.name)
2077
if not entry or (not entry.cf_assignments
2078
or len(entry.cf_assignments) != 1):
2081
return entry.cf_assignments[0].rhs
2083
def visit_SimpleCallNode(self, node):
2084
self.visitchildren(node)
2085
if not self.current_directives.get('optimize.inline_defnode_calls'):
2087
function_name = node.function
2088
if not function_name.is_name:
2090
function = self.get_constant_value_node(function_name)
2091
if not isinstance(function, ExprNodes.PyCFunctionNode):
2093
inlined = ExprNodes.InlinedDefNodeCallNode(
2094
node.pos, function_name=function_name,
2095
function=function, args=node.args,
2096
generator_arg_tag=node.generator_arg_tag)
2097
if inlined.can_be_inlined():
2098
return self.replace(node, inlined)
2102
class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
2103
Visitor.MethodDispatcherTransform):
2104
"""Optimize some common methods calls and instantiation patterns
2105
for builtin types *after* the type analysis phase.
2107
Running after type analysis, this transform can only perform
2108
function replacements that do not alter the function return type
2109
in a way that was not anticipated by the type analysis.
2113
def visit_PyTypeTestNode(self, node):
2114
"""Flatten redundant type checks after tree changes.
2116
self.visitchildren(node)
2117
return node.reanalyse()
2119
def _visit_TypecastNode(self, node):
2123
Drop redundant type casts.
2125
self.visitchildren(node)
2126
if node.type == node.operand.type:
2130
def visit_ExprStatNode(self, node):
2132
Drop dead code and useless coercions.
2134
self.visitchildren(node)
2135
if isinstance(node.expr, ExprNodes.CoerceToPyTypeNode):
2136
node.expr = node.expr.arg
2138
if expr is None or expr.is_none or expr.is_literal:
2141
if expr.is_name and expr.entry and (expr.entry.is_local or expr.entry.is_arg):
2146
def visit_CoerceToBooleanNode(self, node):
2147
"""Drop redundant conversion nodes after tree changes.
2149
self.visitchildren(node)
2151
if isinstance(arg, ExprNodes.PyTypeTestNode):
2153
if isinstance(arg, ExprNodes.CoerceToPyTypeNode):
2154
if arg.type in (PyrexTypes.py_object_type, Builtin.bool_type):
2155
return arg.arg.coerce_to_boolean(self.current_env())
2158
PyNumber_Float_func_type = PyrexTypes.CFuncType(
2159
PyrexTypes.py_object_type, [
2160
PyrexTypes.CFuncTypeArg("o", PyrexTypes.py_object_type, None)
2163
def visit_CoerceToPyTypeNode(self, node):
2164
"""Drop redundant conversion nodes after tree changes."""
2165
self.visitchildren(node)
2167
if isinstance(arg, ExprNodes.CoerceFromPyTypeNode):
2169
if isinstance(arg, ExprNodes.PythonCapiCallNode):
2170
if arg.function.name == 'float' and len(arg.args) == 1:
2172
func_arg = arg.args[0]
2173
if func_arg.type is Builtin.float_type:
2174
return func_arg.as_none_safe_node("float() argument must be a string or a number, not 'NoneType'")
2175
elif func_arg.type.is_pyobject and arg.function.cname == "__Pyx_PyObject_AsDouble":
2176
return ExprNodes.PythonCapiCallNode(
2177
node.pos, '__Pyx_PyNumber_Float', self.PyNumber_Float_func_type,
2180
is_temp=node.is_temp,
2181
utility_code = UtilityCode.load_cached("pynumber_float", "TypeConversion.c"),
2182
result_is_used=node.result_is_used,
2183
).coerce_to(node.type, self.current_env())
2186
def visit_CoerceFromPyTypeNode(self, node):
2187
"""Drop redundant conversion nodes after tree changes.
2189
Also, optimise away calls to Python's builtin int() and
2190
float() if the result is going to be coerced back into a C
2193
self.visitchildren(node)
2195
if not arg.type.is_pyobject:
2197
if node.type != arg.type:
2198
arg = arg.coerce_to(node.type, self.current_env())
2200
if isinstance(arg, ExprNodes.PyTypeTestNode):
2203
if (node.type.is_int and isinstance(arg, ExprNodes.IntNode) or
2204
node.type.is_float and isinstance(arg, ExprNodes.FloatNode) or
2205
node.type.is_int and isinstance(arg, ExprNodes.BoolNode)):
2206
return arg.coerce_to(node.type, self.current_env())
2207
elif isinstance(arg, ExprNodes.CoerceToPyTypeNode):
2208
if arg.type is PyrexTypes.py_object_type:
2209
if node.type.assignable_from(arg.arg.type):
2211
return arg.arg.coerce_to(node.type, self.current_env())
2212
elif arg.type is Builtin.unicode_type:
2213
if arg.arg.type.is_unicode_char and node.type.is_unicode_char:
2214
return arg.arg.coerce_to(node.type, self.current_env())
2215
elif isinstance(arg, ExprNodes.SimpleCallNode):
2216
if node.type.is_int or node.type.is_float:
2217
return self._optimise_numeric_cast_call(node, arg)
2218
elif arg.is_subscript:
2219
index_node = arg.index
2220
if isinstance(index_node, ExprNodes.CoerceToPyTypeNode):
2221
index_node = index_node.arg
2222
if index_node.type.is_int:
2223
return self._optimise_int_indexing(node, arg, index_node)
2226
PyBytes_GetItemInt_func_type = PyrexTypes.CFuncType(
2227
PyrexTypes.c_char_type, [
2228
PyrexTypes.CFuncTypeArg("bytes", Builtin.bytes_type, None),
2229
PyrexTypes.CFuncTypeArg("index", PyrexTypes.c_py_ssize_t_type, None),
2230
PyrexTypes.CFuncTypeArg("check_bounds", PyrexTypes.c_int_type, None),
2232
exception_value = "((char)-1)",
2233
exception_check = True)
2235
def _optimise_int_indexing(self, coerce_node, arg, index_node):
2236
env = self.current_env()
2237
bound_check_bool = env.directives['boundscheck'] and 1 or 0
2238
if arg.base.type is Builtin.bytes_type:
2239
if coerce_node.type in (PyrexTypes.c_char_type, PyrexTypes.c_uchar_type):
2241
bound_check_node = ExprNodes.IntNode(
2242
coerce_node.pos, value=str(bound_check_bool),
2243
constant_result=bound_check_bool)
2244
node = ExprNodes.PythonCapiCallNode(
2245
coerce_node.pos, "__Pyx_PyBytes_GetItemInt",
2246
self.PyBytes_GetItemInt_func_type,
2248
arg.base.as_none_safe_node("'NoneType' object is not subscriptable"),
2249
index_node.coerce_to(PyrexTypes.c_py_ssize_t_type, env),
2253
utility_code=UtilityCode.load_cached(
2254
'bytes_index', 'StringTools.c'))
2255
if coerce_node.type is not PyrexTypes.c_char_type:
2256
node = node.coerce_to(coerce_node.type, env)
2260
float_float_func_types = {
2261
float_type: PyrexTypes.CFuncType(
2263
PyrexTypes.CFuncTypeArg("arg", float_type, None)
2265
for float_type in (PyrexTypes.c_float_type, PyrexTypes.c_double_type, PyrexTypes.c_longdouble_type)
2268
def _optimise_numeric_cast_call(self, node, arg):
2269
function = arg.function
2271
if isinstance(arg, ExprNodes.PythonCapiCallNode):
2273
elif isinstance(function, ExprNodes.NameNode):
2274
if function.type.is_builtin_type and isinstance(arg.arg_tuple, ExprNodes.TupleNode):
2275
args = arg.arg_tuple.args
2277
if args is None or len(args) != 1:
2280
if isinstance(func_arg, ExprNodes.CoerceToPyTypeNode):
2281
func_arg = func_arg.arg
2282
elif func_arg.type.is_pyobject:
2286
if function.name == 'int':
2287
if func_arg.type.is_int or node.type.is_int:
2288
if func_arg.type == node.type:
2290
elif func_arg.type in (PyrexTypes.c_py_ucs4_type, PyrexTypes.c_py_unicode_type):
2292
return self._pyucs4_to_number(node, function.name, func_arg)
2293
elif node.type.assignable_from(func_arg.type) or func_arg.type.is_float:
2294
return ExprNodes.TypecastNode(node.pos, operand=func_arg, type=node.type)
2295
elif func_arg.type.is_float and node.type.is_numeric:
2296
if func_arg.type.math_h_modifier == 'l':
2298
truncl = '__Pyx_truncl'
2300
truncl = 'trunc' + func_arg.type.math_h_modifier
2301
return ExprNodes.PythonCapiCallNode(
2303
func_type=self.float_float_func_types[func_arg.type],
2306
is_temp=node.is_temp,
2307
result_is_used=node.result_is_used,
2308
).coerce_to(node.type, self.current_env())
2309
elif function.name == 'float':
2310
if func_arg.type.is_float or node.type.is_float:
2311
if func_arg.type == node.type:
2313
elif func_arg.type in (PyrexTypes.c_py_ucs4_type, PyrexTypes.c_py_unicode_type):
2315
return self._pyucs4_to_number(node, function.name, func_arg)
2316
elif node.type.assignable_from(func_arg.type) or func_arg.type.is_float:
2317
return ExprNodes.TypecastNode(
2318
node.pos, operand=func_arg, type=node.type)
2321
pyucs4_int_func_type = PyrexTypes.CFuncType(
2322
PyrexTypes.c_int_type, [
2323
PyrexTypes.CFuncTypeArg("arg", PyrexTypes.c_py_ucs4_type, None)
2327
pyucs4_double_func_type = PyrexTypes.CFuncType(
2328
PyrexTypes.c_double_type, [
2329
PyrexTypes.CFuncTypeArg("arg", PyrexTypes.c_py_ucs4_type, None)
2331
exception_value=-1.0)
2333
def _pyucs4_to_number(self, node, py_type_name, func_arg):
2334
assert py_type_name in ("int", "float")
2335
return ExprNodes.PythonCapiCallNode(
2336
node.pos, "__Pyx_int_from_UCS4" if py_type_name == "int" else "__Pyx_double_from_UCS4",
2337
func_type=self.pyucs4_int_func_type if py_type_name == "int" else self.pyucs4_double_func_type,
2339
py_name=py_type_name,
2340
is_temp=node.is_temp,
2341
result_is_used=node.result_is_used,
2342
utility_code=UtilityCode.load_cached("int_pyucs4" if py_type_name == "int" else "float_pyucs4", "Builtins.c"),
2343
).coerce_to(node.type, self.current_env())
2345
def _error_wrong_arg_count(self, function_name, node, args, expected=None):
2348
elif isinstance(expected, str) or expected > 1:
2354
if expected is not None:
2355
expected_str = 'expected %s, ' % expected
2358
error(node.pos, "%s(%s) called with wrong number of args, %sfound %d" % (
2359
function_name, arg_str, expected_str, len(args)))
2363
def _handle_function(self, node, function_name, function, arg_list, kwargs):
2366
def _handle_method(self, node, type_name, attr_name, function,
2367
arg_list, is_unbound_method, kwargs):
2369
Try to inject C-API calls for unbound method calls to builtin types.
2370
While the method declarations in Builtin.py already handle this, we
2371
can additionally resolve bound and unbound methods here that were
2372
assigned to variables ahead of time.
2376
if not function or not function.is_attribute or not function.obj.is_name:
2380
type_entry = self.current_env().lookup(type_name)
2383
method = ExprNodes.AttributeNode(
2385
obj=ExprNodes.NameNode(
2389
type=type_entry.type),
2390
attribute=attr_name,
2391
is_called=True).analyse_as_type_attribute(self.current_env())
2393
return self._optimise_generic_builtin_method_call(
2394
node, attr_name, function, arg_list, is_unbound_method)
2396
if args is None and node.arg_tuple:
2397
args = node.arg_tuple.args
2398
call_node = ExprNodes.SimpleCallNode(
2402
if not is_unbound_method:
2403
call_node.self = function.obj
2404
call_node.analyse_c_function_call(self.current_env())
2405
call_node.analysed = True
2406
return call_node.coerce_to(node.type, self.current_env())
2410
def _optimise_generic_builtin_method_call(self, node, attr_name, function, arg_list, is_unbound_method):
2412
Try to inject an unbound method call for a call to a method of a known builtin type.
2413
This enables caching the underlying C function of the method at runtime.
2415
arg_count = len(arg_list)
2416
if is_unbound_method or arg_count >= 3 or not (function.is_attribute and function.is_py_attr):
2418
if not function.obj.type.is_builtin_type:
2420
if function.obj.type is Builtin.type_type:
2423
return ExprNodes.CachedBuiltinMethodCallNode(
2424
node, function.obj, attr_name, arg_list)
2426
PyObject_Unicode_func_type = PyrexTypes.CFuncType(
2427
Builtin.unicode_type, [
2428
PyrexTypes.CFuncTypeArg("obj", PyrexTypes.py_object_type, None)
2431
def _handle_simple_function_unicode(self, node, function, pos_args):
2432
"""Optimise single argument calls to unicode().
2434
if len(pos_args) != 1:
2435
if len(pos_args) == 0:
2436
return ExprNodes.UnicodeNode(node.pos, value=EncodedString())
2439
if arg.type is Builtin.unicode_type:
2440
if not arg.may_be_none():
2442
cname = "__Pyx_PyUnicode_Unicode"
2443
utility_code = UtilityCode.load_cached('PyUnicode_Unicode', 'StringTools.c')
2445
cname = "__Pyx_PyObject_Unicode"
2446
utility_code = UtilityCode.load_cached('PyObject_Unicode', 'StringTools.c')
2447
return ExprNodes.PythonCapiCallNode(
2448
node.pos, cname, self.PyObject_Unicode_func_type,
2450
is_temp=node.is_temp,
2451
utility_code=utility_code,
2454
_handle_simple_function_str = _handle_simple_function_unicode
2456
def visit_FormattedValueNode(self, node):
2457
"""Simplify or avoid plain string formatting of a unicode value.
2458
This seems misplaced here, but plain unicode formatting is essentially
2459
a call to the unicode() builtin, which is optimised right above.
2461
self.visitchildren(node)
2462
if node.value.type is Builtin.unicode_type and not node.c_format_spec and not node.format_spec:
2463
if not node.conversion_char or node.conversion_char == 's':
2465
return self._handle_simple_function_unicode(node, None, [node.value])
2468
PyDict_Copy_func_type = PyrexTypes.CFuncType(
2469
Builtin.dict_type, [
2470
PyrexTypes.CFuncTypeArg("dict", Builtin.dict_type, None)
2473
def _handle_simple_function_dict(self, node, function, pos_args):
2474
"""Replace dict(some_dict) by PyDict_Copy(some_dict).
2476
if len(pos_args) != 1:
2479
if arg.type is Builtin.dict_type:
2480
arg = arg.as_none_safe_node("'NoneType' is not iterable")
2481
return ExprNodes.PythonCapiCallNode(
2482
node.pos, "PyDict_Copy", self.PyDict_Copy_func_type,
2484
is_temp = node.is_temp
2488
PySequence_List_func_type = PyrexTypes.CFuncType(
2490
[PyrexTypes.CFuncTypeArg("it", PyrexTypes.py_object_type, None)])
2492
def _handle_simple_function_list(self, node, function, pos_args):
2493
"""Turn list(ob) into PySequence_List(ob).
2495
if len(pos_args) != 1:
2498
return ExprNodes.PythonCapiCallNode(
2500
"__Pyx_PySequence_ListKeepNew"
2501
if node.is_temp and arg.is_temp and arg.type in (PyrexTypes.py_object_type, Builtin.list_type)
2502
else "PySequence_List",
2503
self.PySequence_List_func_type,
2505
is_temp=node.is_temp,
2508
PyList_AsTuple_func_type = PyrexTypes.CFuncType(
2509
Builtin.tuple_type, [
2510
PyrexTypes.CFuncTypeArg("list", Builtin.list_type, None)
2513
def _handle_simple_function_tuple(self, node, function, pos_args):
2514
"""Replace tuple([...]) by PyList_AsTuple or PySequence_Tuple.
2516
if len(pos_args) != 1 or not node.is_temp:
2519
if arg.type is Builtin.tuple_type and not arg.may_be_none():
2521
if arg.type is Builtin.list_type:
2522
pos_args[0] = arg.as_none_safe_node(
2523
"'NoneType' object is not iterable")
2525
return ExprNodes.PythonCapiCallNode(
2526
node.pos, "PyList_AsTuple", self.PyList_AsTuple_func_type,
2527
args=pos_args, is_temp=node.is_temp)
2529
return ExprNodes.AsTupleNode(node.pos, arg=arg, type=Builtin.tuple_type)
2531
PySet_New_func_type = PyrexTypes.CFuncType(
2533
PyrexTypes.CFuncTypeArg("it", PyrexTypes.py_object_type, None)
2536
def _handle_simple_function_set(self, node, function, pos_args):
2537
if len(pos_args) != 1:
2539
if pos_args[0].is_sequence_constructor:
2547
for arg in pos_args[0].args:
2548
if not arg.is_simple():
2549
arg = UtilNodes.LetRefNode(arg)
2552
result = ExprNodes.SetNode(node.pos, is_temp=1, args=args)
2553
self.replace(node, result)
2554
for temp in temps[::-1]:
2555
result = UtilNodes.EvalWithTempExprNode(temp, result)
2559
return self.replace(node, ExprNodes.PythonCapiCallNode(
2560
node.pos, "PySet_New",
2561
self.PySet_New_func_type,
2563
is_temp=node.is_temp,
2566
PyFrozenSet_New_func_type = PyrexTypes.CFuncType(
2567
Builtin.frozenset_type, [
2568
PyrexTypes.CFuncTypeArg("it", PyrexTypes.py_object_type, None)
2571
def _handle_simple_function_frozenset(self, node, function, pos_args):
2573
pos_args = [ExprNodes.NullNode(node.pos)]
2574
elif len(pos_args) > 1:
2576
elif pos_args[0].type is Builtin.frozenset_type and not pos_args[0].may_be_none():
2579
return ExprNodes.PythonCapiCallNode(
2580
node.pos, "__Pyx_PyFrozenSet_New",
2581
self.PyFrozenSet_New_func_type,
2583
is_temp=node.is_temp,
2584
utility_code=UtilityCode.load_cached('pyfrozenset_new', 'Builtins.c'),
2585
py_name="frozenset")
2587
PyObject_AsDouble_func_type = PyrexTypes.CFuncType(
2588
PyrexTypes.c_double_type, [
2589
PyrexTypes.CFuncTypeArg("obj", PyrexTypes.py_object_type, None),
2591
exception_value = "((double)-1)",
2592
exception_check = True)
2594
def _handle_simple_function_float(self, node, function, pos_args):
2595
"""Transform float() into either a C type cast or a faster C
2600
if len(pos_args) == 0:
2601
return ExprNodes.FloatNode(
2602
node, value="0.0", constant_result=0.0
2603
).coerce_to(Builtin.float_type, self.current_env())
2604
elif len(pos_args) != 1:
2605
self._error_wrong_arg_count('float', node, pos_args, '0 or 1')
2608
func_arg = pos_args[0]
2609
if isinstance(func_arg, ExprNodes.CoerceToPyTypeNode):
2610
func_arg = func_arg.arg
2611
if func_arg.type is PyrexTypes.c_double_type:
2613
elif func_arg.type in (PyrexTypes.c_py_ucs4_type, PyrexTypes.c_py_unicode_type):
2615
return self._pyucs4_to_number(node, function.name, func_arg)
2616
elif node.type.assignable_from(func_arg.type) or func_arg.type.is_numeric:
2617
return ExprNodes.TypecastNode(
2618
node.pos, operand=func_arg, type=node.type)
2620
arg = pos_args[0].as_none_safe_node(
2621
"float() argument must be a string or a number, not 'NoneType'")
2623
if func_arg.type is Builtin.bytes_type:
2624
cfunc_name = "__Pyx_PyBytes_AsDouble"
2625
utility_code_name = 'pybytes_as_double'
2626
elif func_arg.type is Builtin.bytearray_type:
2627
cfunc_name = "__Pyx_PyByteArray_AsDouble"
2628
utility_code_name = 'pybytes_as_double'
2629
elif func_arg.type is Builtin.unicode_type:
2630
cfunc_name = "__Pyx_PyUnicode_AsDouble"
2631
utility_code_name = 'pyunicode_as_double'
2632
elif func_arg.type is Builtin.int_type:
2633
cfunc_name = "PyLong_AsDouble"
2634
utility_code_name = None
2637
cfunc_name = "__Pyx_PyObject_AsDouble"
2638
utility_code_name = 'pyobject_as_double'
2640
return ExprNodes.PythonCapiCallNode(
2641
node.pos, cfunc_name,
2642
self.PyObject_AsDouble_func_type,
2644
is_temp = node.is_temp,
2645
utility_code = load_c_utility(utility_code_name) if utility_code_name else None,
2648
PyNumber_Int_func_type = PyrexTypes.CFuncType(
2650
PyrexTypes.CFuncTypeArg("o", PyrexTypes.py_object_type, None)
2653
PyLong_FromDouble_func_type = PyrexTypes.CFuncType(
2655
PyrexTypes.CFuncTypeArg("value", PyrexTypes.c_double_type, None)
2658
def _handle_simple_function_int(self, node, function, pos_args):
2659
"""Transform int() into a faster C function call.
2661
if len(pos_args) == 0:
2662
return ExprNodes.IntNode(node.pos, value="0", constant_result=0,
2663
type=Builtin.int_type)
2664
elif len(pos_args) != 1:
2666
func_arg = pos_args[0]
2667
if isinstance(func_arg, ExprNodes.CoerceToPyTypeNode):
2668
if func_arg.arg.type.is_float:
2669
return ExprNodes.PythonCapiCallNode(
2670
node.pos, "PyLong_FromDouble", self.PyLong_FromDouble_func_type,
2671
args=[func_arg.arg], is_temp=True, py_name='int',
2675
if func_arg.type.is_pyobject and node.type.is_pyobject:
2676
return ExprNodes.PythonCapiCallNode(
2677
node.pos, "__Pyx_PyNumber_Int", self.PyNumber_Int_func_type,
2678
args=pos_args, is_temp=True, py_name='int')
2681
def _handle_simple_function_bool(self, node, function, pos_args):
2682
"""Transform bool(x) into a type coercion to a boolean.
2684
if len(pos_args) == 0:
2685
return ExprNodes.BoolNode(
2686
node.pos, value=False, constant_result=False
2687
).coerce_to(Builtin.bool_type, self.current_env())
2688
elif len(pos_args) != 1:
2689
self._error_wrong_arg_count('bool', node, pos_args, '0 or 1')
2693
operand = pos_args[0].coerce_to_boolean(self.current_env())
2694
operand = ExprNodes.NotNode(node.pos, operand = operand)
2695
operand = ExprNodes.NotNode(node.pos, operand = operand)
2697
return operand.coerce_to_pyobject(self.current_env())
2699
PyMemoryView_FromObject_func_type = PyrexTypes.CFuncType(
2700
Builtin.memoryview_type, [
2701
PyrexTypes.CFuncTypeArg("value", PyrexTypes.py_object_type, None)
2704
PyMemoryView_FromBuffer_func_type = PyrexTypes.CFuncType(
2705
Builtin.memoryview_type, [
2706
PyrexTypes.CFuncTypeArg("value", Builtin.py_buffer_type, None)
2709
def _handle_simple_function_memoryview(self, node, function, pos_args):
2710
if len(pos_args) != 1:
2711
self._error_wrong_arg_count('memoryview', node, pos_args, '1')
2714
if pos_args[0].type.is_pyobject:
2715
return ExprNodes.PythonCapiCallNode(
2716
node.pos, "PyMemoryView_FromObject",
2717
self.PyMemoryView_FromObject_func_type,
2718
args = [pos_args[0]],
2719
is_temp = node.is_temp,
2720
py_name = "memoryview")
2721
elif pos_args[0].type.is_ptr and pos_args[0].base_type is Builtin.py_buffer_type:
2724
return ExprNodes.PythonCapiCallNode(
2725
node.pos, "PyMemoryView_FromBuffer",
2726
self.PyMemoryView_FromBuffer_func_type,
2727
args = [pos_args[0]],
2728
is_temp = node.is_temp,
2729
py_name = "memoryview")
2735
Pyx_ssize_strlen_func_type = PyrexTypes.CFuncType(
2736
PyrexTypes.c_py_ssize_t_type, [
2737
PyrexTypes.CFuncTypeArg("bytes", PyrexTypes.c_const_char_ptr_type, None)
2741
Pyx_Py_UNICODE_strlen_func_type = PyrexTypes.CFuncType(
2742
PyrexTypes.c_py_ssize_t_type, [
2743
PyrexTypes.CFuncTypeArg("unicode", PyrexTypes.c_const_py_unicode_ptr_type, None)
2747
PyObject_Size_func_type = PyrexTypes.CFuncType(
2748
PyrexTypes.c_py_ssize_t_type, [
2749
PyrexTypes.CFuncTypeArg("obj", PyrexTypes.py_object_type, None)
2753
_map_to_capi_len_function = {
2754
Builtin.unicode_type: "__Pyx_PyUnicode_GET_LENGTH",
2755
Builtin.bytes_type: "__Pyx_PyBytes_GET_SIZE",
2756
Builtin.bytearray_type: '__Pyx_PyByteArray_GET_SIZE',
2757
Builtin.list_type: "__Pyx_PyList_GET_SIZE",
2758
Builtin.tuple_type: "__Pyx_PyTuple_GET_SIZE",
2759
Builtin.set_type: "__Pyx_PySet_GET_SIZE",
2760
Builtin.frozenset_type: "__Pyx_PySet_GET_SIZE",
2761
Builtin.dict_type: "PyDict_Size",
2764
_ext_types_with_pysize = {"cpython.array.array"}
2766
def _handle_simple_function_len(self, node, function, pos_args):
2767
"""Replace len(char*) by the equivalent call to strlen(),
2768
len(Py_UNICODE) by the equivalent Py_UNICODE_strlen() and
2769
len(known_builtin_type) by an equivalent C-API call.
2771
if len(pos_args) != 1:
2772
self._error_wrong_arg_count('len', node, pos_args, 1)
2775
if isinstance(arg, ExprNodes.CoerceToPyTypeNode):
2777
if arg.type.is_string:
2778
new_node = ExprNodes.PythonCapiCallNode(
2779
node.pos, "__Pyx_ssize_strlen", self.Pyx_ssize_strlen_func_type,
2781
is_temp = node.is_temp)
2782
elif arg.type.is_pyunicode_ptr:
2783
new_node = ExprNodes.PythonCapiCallNode(
2784
node.pos, "__Pyx_Py_UNICODE_ssize_strlen", self.Pyx_Py_UNICODE_strlen_func_type,
2786
is_temp = node.is_temp,
2787
utility_code = UtilityCode.load_cached("ssize_pyunicode_strlen", "StringTools.c"))
2788
elif arg.type.is_memoryviewslice:
2789
func_type = PyrexTypes.CFuncType(
2790
PyrexTypes.c_py_ssize_t_type, [
2791
PyrexTypes.CFuncTypeArg("memoryviewslice", arg.type, None)
2793
new_node = ExprNodes.PythonCapiCallNode(
2794
node.pos, "__Pyx_MemoryView_Len", func_type,
2795
args=[arg], is_temp=node.is_temp)
2796
elif arg.type.is_pyobject:
2797
cfunc_name = self._map_to_capi_len_function(arg.type)
2798
if cfunc_name is None:
2800
if ((arg_type.is_extension_type or arg_type.is_builtin_type)
2801
and arg_type.entry.qualified_name in self._ext_types_with_pysize):
2802
cfunc_name = 'Py_SIZE'
2805
arg = arg.as_none_safe_node(
2806
"object of type 'NoneType' has no len()")
2807
new_node = ExprNodes.PythonCapiCallNode(
2808
node.pos, cfunc_name, self.PyObject_Size_func_type,
2809
args=[arg], is_temp=node.is_temp)
2810
elif arg.type.is_unicode_char:
2811
return ExprNodes.IntNode(node.pos, value='1', constant_result=1,
2815
if node.type not in (PyrexTypes.c_size_t_type, PyrexTypes.c_py_ssize_t_type):
2816
new_node = new_node.coerce_to(node.type, self.current_env())
2819
Pyx_Type_func_type = PyrexTypes.CFuncType(
2820
Builtin.type_type, [
2821
PyrexTypes.CFuncTypeArg("object", PyrexTypes.py_object_type, None)
2824
def _handle_simple_function_type(self, node, function, pos_args):
2825
"""Replace type(o) by a macro call to Py_TYPE(o).
2827
if len(pos_args) != 1:
2829
node = ExprNodes.PythonCapiCallNode(
2830
node.pos, "Py_TYPE", self.Pyx_Type_func_type,
2833
return ExprNodes.CastNode(node, PyrexTypes.py_object_type)
2835
Py_type_check_func_type = PyrexTypes.CFuncType(
2836
PyrexTypes.c_bint_type, [
2837
PyrexTypes.CFuncTypeArg("arg", PyrexTypes.py_object_type, None)
2840
def _handle_simple_function_isinstance(self, node, function, pos_args):
2841
"""Replace isinstance() checks against builtin types by the
2842
corresponding C-API call.
2844
if len(pos_args) != 2:
2846
arg, types = pos_args
2848
if isinstance(types, ExprNodes.TupleNode):
2850
if len(types) == 1 and not types[0].type is Builtin.type_type:
2852
if arg.is_attribute or not arg.is_simple():
2853
arg = UtilNodes.ResultRefNode(arg)
2855
elif types.type is Builtin.type_type:
2862
env = self.current_env()
2863
for test_type_node in types:
2865
if test_type_node.is_name:
2866
if test_type_node.entry:
2867
entry = env.lookup(test_type_node.entry.name)
2868
if entry and entry.type and entry.type.is_builtin_type:
2869
builtin_type = entry.type
2870
if builtin_type is Builtin.type_type:
2872
if entry.name != 'type' or not (
2873
entry.scope and entry.scope.is_builtin_scope):
2875
if builtin_type is not None:
2876
type_check_function = entry.type.type_check_function(exact=False)
2877
if type_check_function in tests:
2879
tests.append(type_check_function)
2880
type_check_args = [arg]
2881
elif test_type_node.type is Builtin.type_type:
2882
type_check_function = '__Pyx_TypeCheck'
2883
type_check_args = [arg, test_type_node]
2885
if not test_type_node.is_literal:
2886
test_type_node = UtilNodes.ResultRefNode(test_type_node)
2887
temps.append(test_type_node)
2888
type_check_function = 'PyObject_IsInstance'
2889
type_check_args = [arg, test_type_node]
2891
ExprNodes.PythonCapiCallNode(
2892
test_type_node.pos, type_check_function, self.Py_type_check_func_type,
2893
args=type_check_args,
2897
def join_with_or(a, b, make_binop_node=ExprNodes.binop_node):
2898
or_node = make_binop_node(node.pos, 'or', a, b)
2899
or_node.type = PyrexTypes.c_bint_type
2900
or_node.wrap_operands(env)
2903
test_node = reduce(join_with_or, test_nodes).coerce_to(node.type, env)
2904
for temp in temps[::-1]:
2905
test_node = UtilNodes.EvalWithTempExprNode(temp, test_node)
2908
def _handle_simple_function_ord(self, node, function, pos_args):
2909
"""Unpack ord(Py_UNICODE) and ord('X').
2911
if len(pos_args) != 1:
2914
if isinstance(arg, ExprNodes.CoerceToPyTypeNode):
2915
if arg.arg.type.is_unicode_char:
2916
return ExprNodes.TypecastNode(
2917
arg.pos, operand=arg.arg, type=PyrexTypes.c_long_type
2918
).coerce_to(node.type, self.current_env())
2919
elif isinstance(arg, (ExprNodes.UnicodeNode, ExprNodes.BytesNode)):
2920
if len(arg.value) == 1:
2921
return ExprNodes.IntNode(
2922
arg.pos, type=PyrexTypes.c_int_type,
2923
value=str(ord(arg.value)),
2924
constant_result=ord(arg.value)
2925
).coerce_to(node.type, self.current_env())
2930
Pyx_tp_new_func_type = PyrexTypes.CFuncType(
2931
PyrexTypes.py_object_type, [
2932
PyrexTypes.CFuncTypeArg("type", PyrexTypes.py_object_type, None),
2933
PyrexTypes.CFuncTypeArg("args", Builtin.tuple_type, None),
2936
Pyx_tp_new_kwargs_func_type = PyrexTypes.CFuncType(
2937
PyrexTypes.py_object_type, [
2938
PyrexTypes.CFuncTypeArg("type", PyrexTypes.py_object_type, None),
2939
PyrexTypes.CFuncTypeArg("args", Builtin.tuple_type, None),
2940
PyrexTypes.CFuncTypeArg("kwargs", Builtin.dict_type, None),
2943
def _handle_any_slot__new__(self, node, function, args,
2944
is_unbound_method, kwargs=None):
2945
"""Replace 'exttype.__new__(exttype, ...)' by a call to exttype->tp_new()
2948
if not is_unbound_method or len(args) < 1:
2951
if not obj.is_name or not type_arg.is_name:
2953
if obj.type != Builtin.type_type or type_arg.type != Builtin.type_type:
2955
if not type_arg.type_entry or not obj.type_entry:
2956
if obj.name != type_arg.name:
2960
elif type_arg.type_entry != obj.type_entry:
2964
args_tuple = ExprNodes.TupleNode(node.pos, args=args[1:])
2965
args_tuple = args_tuple.analyse_types(
2966
self.current_env(), skip_children=True)
2968
if type_arg.type_entry:
2969
ext_type = type_arg.type_entry.type
2970
if (ext_type.is_extension_type and ext_type.typeobj_cname and
2971
ext_type.scope.global_scope() == self.current_env().global_scope()):
2973
tp_slot = TypeSlots.ConstructorSlot("tp_new", '__new__')
2974
slot_func_cname = TypeSlots.get_slot_function(ext_type.scope, tp_slot)
2976
cython_scope = self.context.cython_scope
2977
PyTypeObjectPtr = PyrexTypes.CPtrType(
2978
cython_scope.lookup('PyTypeObject').type)
2979
pyx_tp_new_kwargs_func_type = PyrexTypes.CFuncType(
2981
PyrexTypes.CFuncTypeArg("type", PyTypeObjectPtr, None),
2982
PyrexTypes.CFuncTypeArg("args", PyrexTypes.py_object_type, None),
2983
PyrexTypes.CFuncTypeArg("kwargs", PyrexTypes.py_object_type, None),
2986
type_arg = ExprNodes.CastNode(type_arg, PyTypeObjectPtr)
2988
kwargs = ExprNodes.NullNode(node.pos, type=PyrexTypes.py_object_type)
2989
return ExprNodes.PythonCapiCallNode(
2990
node.pos, slot_func_cname,
2991
pyx_tp_new_kwargs_func_type,
2992
args=[type_arg, args_tuple, kwargs],
2993
may_return_none=False,
2997
type_arg = type_arg.as_none_safe_node(
2998
"object.__new__(X): X is not a type object (NoneType)")
3000
utility_code = UtilityCode.load_cached('tp_new', 'ObjectHandling.c')
3002
return ExprNodes.PythonCapiCallNode(
3003
node.pos, "__Pyx_tp_new_kwargs", self.Pyx_tp_new_kwargs_func_type,
3004
args=[type_arg, args_tuple, kwargs],
3005
utility_code=utility_code,
3006
is_temp=node.is_temp
3009
return ExprNodes.PythonCapiCallNode(
3010
node.pos, "__Pyx_tp_new", self.Pyx_tp_new_func_type,
3011
args=[type_arg, args_tuple],
3012
utility_code=utility_code,
3013
is_temp=node.is_temp
3016
def _handle_any_slot__class__(self, node, function, args,
3017
is_unbound_method, kwargs=None):
3025
PyObject_Append_func_type = PyrexTypes.CFuncType(
3026
PyrexTypes.c_returncode_type, [
3027
PyrexTypes.CFuncTypeArg("list", PyrexTypes.py_object_type, None),
3028
PyrexTypes.CFuncTypeArg("item", PyrexTypes.py_object_type, None),
3032
def _handle_simple_method_object_append(self, node, function, args, is_unbound_method):
3033
"""Optimistic optimisation as X.append() is almost always
3034
referring to a list.
3036
if len(args) != 2 or node.result_is_used or node.function.entry:
3039
return ExprNodes.PythonCapiCallNode(
3040
node.pos, "__Pyx_PyObject_Append", self.PyObject_Append_func_type,
3042
may_return_none=False,
3043
is_temp=node.is_temp,
3044
result_is_used=False,
3045
utility_code=load_c_utility('append')
3048
def _handle_simple_method_list_extend(self, node, function, args, is_unbound_method):
3049
"""Replace list.extend([...]) for short sequence literals values by sequential appends
3050
to avoid creating an intermediate sequence argument.
3055
if not value.is_sequence_constructor:
3057
items = list(value.args)
3058
if value.mult_factor is not None or len(items) > 8:
3061
if False and isinstance(value, ExprNodes.ListNode):
3066
tuple_node = args[1].as_tuple().analyse_types(self.current_env(), skip_children=True)
3067
Visitor.recursively_replace_node(node, args[1], tuple_node)
3069
wrapped_obj = self._wrap_self_arg(obj, function, is_unbound_method, 'extend')
3072
wrapped_obj.result_is_used = node.result_is_used
3074
cloned_obj = obj = wrapped_obj
3075
if len(items) > 1 and not obj.is_simple():
3076
cloned_obj = UtilNodes.LetRefNode(obj)
3081
if not arg.is_simple():
3082
arg = UtilNodes.LetRefNode(arg)
3084
new_node = ExprNodes.PythonCapiCallNode(
3085
node.pos, "__Pyx_PyList_Append", self.PyObject_Append_func_type,
3086
args=[cloned_obj, arg],
3088
utility_code=load_c_utility("ListAppend"))
3089
for arg in items[-2::-1]:
3090
if not arg.is_simple():
3091
arg = UtilNodes.LetRefNode(arg)
3093
new_node = ExprNodes.binop_node(
3095
ExprNodes.PythonCapiCallNode(
3096
node.pos, "__Pyx_ListComp_Append", self.PyObject_Append_func_type,
3097
args=[cloned_obj, arg], py_name="extend",
3099
utility_code=load_c_utility("ListCompAppend")),
3101
type=PyrexTypes.c_returncode_type,
3103
new_node.result_is_used = node.result_is_used
3104
if cloned_obj is not obj:
3105
temps.append(cloned_obj)
3107
new_node = UtilNodes.EvalWithTempExprNode(temp, new_node)
3108
new_node.result_is_used = node.result_is_used
3111
PyByteArray_Append_func_type = PyrexTypes.CFuncType(
3112
PyrexTypes.c_returncode_type, [
3113
PyrexTypes.CFuncTypeArg("bytearray", PyrexTypes.py_object_type, None),
3114
PyrexTypes.CFuncTypeArg("value", PyrexTypes.c_int_type, None),
3118
PyByteArray_AppendObject_func_type = PyrexTypes.CFuncType(
3119
PyrexTypes.c_returncode_type, [
3120
PyrexTypes.CFuncTypeArg("bytearray", PyrexTypes.py_object_type, None),
3121
PyrexTypes.CFuncTypeArg("value", PyrexTypes.py_object_type, None),
3125
def _handle_simple_method_bytearray_append(self, node, function, args, is_unbound_method):
3128
func_name = "__Pyx_PyByteArray_Append"
3129
func_type = self.PyByteArray_Append_func_type
3131
value = unwrap_coerced_node(args[1])
3132
if value.type.is_int or isinstance(value, ExprNodes.IntNode):
3133
value = value.coerce_to(PyrexTypes.c_int_type, self.current_env())
3134
utility_code = UtilityCode.load_cached("ByteArrayAppend", "StringTools.c")
3135
elif value.is_string_literal:
3136
if not value.can_coerce_to_char_literal():
3138
value = value.coerce_to(PyrexTypes.c_char_type, self.current_env())
3139
utility_code = UtilityCode.load_cached("ByteArrayAppend", "StringTools.c")
3140
elif value.type.is_pyobject:
3141
func_name = "__Pyx_PyByteArray_AppendObject"
3142
func_type = self.PyByteArray_AppendObject_func_type
3143
utility_code = UtilityCode.load_cached("ByteArrayAppendObject", "StringTools.c")
3147
new_node = ExprNodes.PythonCapiCallNode(
3148
node.pos, func_name, func_type,
3149
args=[args[0], value],
3150
may_return_none=False,
3151
is_temp=node.is_temp,
3152
utility_code=utility_code,
3154
if node.result_is_used:
3155
new_node = new_node.coerce_to(node.type, self.current_env())
3158
PyObject_Pop_func_type = PyrexTypes.CFuncType(
3159
PyrexTypes.py_object_type, [
3160
PyrexTypes.CFuncTypeArg("list", PyrexTypes.py_object_type, None),
3163
PyObject_PopIndex_func_type = PyrexTypes.CFuncType(
3164
PyrexTypes.py_object_type, [
3165
PyrexTypes.CFuncTypeArg("list", PyrexTypes.py_object_type, None),
3166
PyrexTypes.CFuncTypeArg("py_index", PyrexTypes.py_object_type, None),
3167
PyrexTypes.CFuncTypeArg("c_index", PyrexTypes.c_py_ssize_t_type, None),
3168
PyrexTypes.CFuncTypeArg("is_signed", PyrexTypes.c_int_type, None),
3172
def _handle_simple_method_list_pop(self, node, function, args, is_unbound_method):
3173
return self._handle_simple_method_object_pop(
3174
node, function, args, is_unbound_method, is_list=True)
3176
def _handle_simple_method_object_pop(self, node, function, args, is_unbound_method, is_list=False):
3177
"""Optimistic optimisation as X.pop([n]) is almost always
3178
referring to a list.
3185
obj = obj.as_none_safe_node(
3186
"'NoneType' object has no attribute '%.30s'",
3187
error="PyExc_AttributeError",
3188
format_args=['pop'])
3190
type_name = 'Object'
3192
return ExprNodes.PythonCapiCallNode(
3193
node.pos, "__Pyx_Py%s_Pop" % type_name,
3194
self.PyObject_Pop_func_type,
3196
may_return_none=True,
3197
is_temp=node.is_temp,
3198
utility_code=load_c_utility('pop'),
3200
elif len(args) == 2:
3201
index = unwrap_coerced_node(args[1])
3202
py_index = ExprNodes.NoneNode(index.pos)
3203
orig_index_type = index.type
3204
if not index.type.is_int:
3205
if isinstance(index, ExprNodes.IntNode):
3206
py_index = index.coerce_to_pyobject(self.current_env())
3207
index = index.coerce_to(PyrexTypes.c_py_ssize_t_type, self.current_env())
3209
if index.type.is_pyobject:
3210
py_index = index.coerce_to_simple(self.current_env())
3211
index = ExprNodes.CloneNode(py_index)
3212
index = index.coerce_to(PyrexTypes.c_py_ssize_t_type, self.current_env())
3215
elif not PyrexTypes.numeric_type_fits(index.type, PyrexTypes.c_py_ssize_t_type):
3217
elif isinstance(index, ExprNodes.IntNode):
3218
py_index = index.coerce_to_pyobject(self.current_env())
3220
if not orig_index_type.is_int:
3221
orig_index_type = index.type
3222
if not orig_index_type.create_to_py_utility_code(self.current_env()):
3224
convert_func = orig_index_type.to_py_function
3225
conversion_type = PyrexTypes.CFuncType(
3226
PyrexTypes.py_object_type, [PyrexTypes.CFuncTypeArg("intval", orig_index_type, None)])
3227
return ExprNodes.PythonCapiCallNode(
3228
node.pos, "__Pyx_Py%s_PopIndex" % type_name,
3229
self.PyObject_PopIndex_func_type,
3230
args=[obj, py_index, index,
3231
ExprNodes.IntNode(index.pos, value=str(orig_index_type.signed and 1 or 0),
3232
constant_result=orig_index_type.signed and 1 or 0,
3233
type=PyrexTypes.c_int_type),
3234
ExprNodes.RawCNameExprNode(index.pos, PyrexTypes.c_void_type,
3235
orig_index_type.empty_declaration_code()),
3236
ExprNodes.RawCNameExprNode(index.pos, conversion_type, convert_func)],
3237
may_return_none=True,
3238
is_temp=node.is_temp,
3239
utility_code=load_c_utility("pop_index"),
3244
single_param_func_type = PyrexTypes.CFuncType(
3245
PyrexTypes.c_returncode_type, [
3246
PyrexTypes.CFuncTypeArg("obj", PyrexTypes.py_object_type, None),
3250
def _handle_simple_method_list_sort(self, node, function, args, is_unbound_method):
3251
"""Call PyList_Sort() instead of the 0-argument l.sort().
3255
return self._substitute_method_call(
3256
node, function, "PyList_Sort", self.single_param_func_type,
3257
'sort', is_unbound_method, args).coerce_to(node.type, self.current_env)
3259
Pyx_PyDict_GetItem_func_type = PyrexTypes.CFuncType(
3260
PyrexTypes.py_object_type, [
3261
PyrexTypes.CFuncTypeArg("dict", PyrexTypes.py_object_type, None),
3262
PyrexTypes.CFuncTypeArg("key", PyrexTypes.py_object_type, None),
3263
PyrexTypes.CFuncTypeArg("default", PyrexTypes.py_object_type, None),
3266
def _handle_simple_method_dict_get(self, node, function, args, is_unbound_method):
3267
"""Replace dict.get() by a call to PyDict_GetItem().
3270
args.append(ExprNodes.NoneNode(node.pos))
3271
elif len(args) != 3:
3272
self._error_wrong_arg_count('dict.get', node, args, "2 or 3")
3275
return self._substitute_method_call(
3277
"__Pyx_PyDict_GetItemDefault", self.Pyx_PyDict_GetItem_func_type,
3278
'get', is_unbound_method, args,
3279
may_return_none = True,
3280
utility_code = load_c_utility("dict_getitem_default"))
3282
Pyx_PyDict_SetDefault_func_type = PyrexTypes.CFuncType(
3283
PyrexTypes.py_object_type, [
3284
PyrexTypes.CFuncTypeArg("dict", PyrexTypes.py_object_type, None),
3285
PyrexTypes.CFuncTypeArg("key", PyrexTypes.py_object_type, None),
3286
PyrexTypes.CFuncTypeArg("default", PyrexTypes.py_object_type, None),
3287
PyrexTypes.CFuncTypeArg("is_safe_type", PyrexTypes.c_int_type, None),
3290
def _handle_simple_method_dict_setdefault(self, node, function, args, is_unbound_method):
3291
"""Replace dict.setdefault() by calls to PyDict_GetItem() and PyDict_SetItem().
3294
args.append(ExprNodes.NoneNode(node.pos))
3295
elif len(args) != 3:
3296
self._error_wrong_arg_count('dict.setdefault', node, args, "2 or 3")
3298
key_type = args[1].type
3299
if key_type.is_builtin_type:
3300
is_safe_type = int(key_type.name in
3301
'str bytes unicode float int long bool')
3302
elif key_type is PyrexTypes.py_object_type:
3306
args.append(ExprNodes.IntNode(
3307
node.pos, value=str(is_safe_type), constant_result=is_safe_type))
3309
return self._substitute_method_call(
3311
"__Pyx_PyDict_SetDefault", self.Pyx_PyDict_SetDefault_func_type,
3312
'setdefault', is_unbound_method, args,
3313
may_return_none=True,
3314
utility_code=load_c_utility('dict_setdefault'))
3316
PyDict_Pop_func_type = PyrexTypes.CFuncType(
3317
PyrexTypes.py_object_type, [
3318
PyrexTypes.CFuncTypeArg("dict", PyrexTypes.py_object_type, None),
3319
PyrexTypes.CFuncTypeArg("key", PyrexTypes.py_object_type, None),
3320
PyrexTypes.CFuncTypeArg("default", PyrexTypes.py_object_type, None),
3323
PyDict_Pop_ignore_func_type = PyrexTypes.CFuncType(
3324
PyrexTypes.c_int_type, [
3325
PyrexTypes.CFuncTypeArg("dict", PyrexTypes.py_object_type, None),
3326
PyrexTypes.CFuncTypeArg("key", PyrexTypes.py_object_type, None),
3327
PyrexTypes.CFuncTypeArg("default", PyrexTypes.py_object_type, None),
3329
exception_value=PyrexTypes.c_int_type.exception_value,
3332
def _handle_simple_method_dict_pop(self, node, function, args, is_unbound_method):
3333
"""Replace dict.pop() by a call to _PyDict_Pop().
3335
capi_func = "__Pyx_PyDict_Pop"
3336
utility_code_name = 'py_dict_pop'
3337
func_type = self.PyDict_Pop_func_type
3340
args.append(ExprNodes.NullNode(node.pos))
3341
elif len(args) == 3:
3342
if not node.result_is_used:
3344
capi_func = "__Pyx_PyDict_Pop_ignore"
3345
utility_code_name = 'py_dict_pop_ignore'
3346
func_type = self.PyDict_Pop_ignore_func_type
3348
self._error_wrong_arg_count('dict.pop', node, args, "2 or 3")
3351
return self._substitute_method_call(
3353
capi_func, func_type,
3354
'pop', is_unbound_method, args,
3355
may_return_none=True,
3356
utility_code=load_c_utility(utility_code_name))
3358
Pyx_BinopInt_func_types = {
3359
(ctype, ret_type): PyrexTypes.CFuncType(
3361
PyrexTypes.CFuncTypeArg("op1", PyrexTypes.py_object_type, None),
3362
PyrexTypes.CFuncTypeArg("op2", PyrexTypes.py_object_type, None),
3363
PyrexTypes.CFuncTypeArg("cval", ctype, None),
3364
PyrexTypes.CFuncTypeArg("inplace", PyrexTypes.c_bint_type, None),
3365
PyrexTypes.CFuncTypeArg("zerodiv_check", PyrexTypes.c_bint_type, None),
3366
], exception_value=None if ret_type.is_pyobject else ret_type.exception_value)
3367
for ctype in (PyrexTypes.c_long_type, PyrexTypes.c_double_type)
3368
for ret_type in (PyrexTypes.py_object_type, PyrexTypes.c_bint_type)
3371
def _handle_simple_method_object___add__(self, node, function, args, is_unbound_method):
3372
return self._optimise_num_binop('Add', node, function, args, is_unbound_method)
3374
def _handle_simple_method_object___sub__(self, node, function, args, is_unbound_method):
3375
return self._optimise_num_binop('Subtract', node, function, args, is_unbound_method)
3377
def _handle_simple_method_object___mul__(self, node, function, args, is_unbound_method):
3378
return self._optimise_num_binop('Multiply', node, function, args, is_unbound_method)
3380
def _handle_simple_method_object___eq__(self, node, function, args, is_unbound_method):
3381
return self._optimise_num_binop('Eq', node, function, args, is_unbound_method)
3383
def _handle_simple_method_object___ne__(self, node, function, args, is_unbound_method):
3384
return self._optimise_num_binop('Ne', node, function, args, is_unbound_method)
3386
def _handle_simple_method_object___and__(self, node, function, args, is_unbound_method):
3387
return self._optimise_num_binop('And', node, function, args, is_unbound_method)
3389
def _handle_simple_method_object___or__(self, node, function, args, is_unbound_method):
3390
return self._optimise_num_binop('Or', node, function, args, is_unbound_method)
3392
def _handle_simple_method_object___xor__(self, node, function, args, is_unbound_method):
3393
return self._optimise_num_binop('Xor', node, function, args, is_unbound_method)
3395
def _handle_simple_method_object___rshift__(self, node, function, args, is_unbound_method):
3396
if len(args) != 2 or not isinstance(args[1], ExprNodes.IntNode):
3398
if not args[1].has_constant_result() or not (1 <= args[1].constant_result <= 63):
3400
return self._optimise_num_binop('Rshift', node, function, args, is_unbound_method)
3402
def _handle_simple_method_object___lshift__(self, node, function, args, is_unbound_method):
3403
if len(args) != 2 or not isinstance(args[1], ExprNodes.IntNode):
3405
if not args[1].has_constant_result() or not (1 <= args[1].constant_result <= 63):
3407
return self._optimise_num_binop('Lshift', node, function, args, is_unbound_method)
3409
def _handle_simple_method_object___mod__(self, node, function, args, is_unbound_method):
3410
return self._optimise_num_div('Remainder', node, function, args, is_unbound_method)
3412
def _handle_simple_method_object___floordiv__(self, node, function, args, is_unbound_method):
3413
return self._optimise_num_div('FloorDivide', node, function, args, is_unbound_method)
3415
def _handle_simple_method_object___truediv__(self, node, function, args, is_unbound_method):
3416
return self._optimise_num_div('TrueDivide', node, function, args, is_unbound_method)
3418
def _handle_simple_method_object___div__(self, node, function, args, is_unbound_method):
3419
return self._optimise_num_div('Divide', node, function, args, is_unbound_method)
3421
_handle_simple_method_int___add__ = _handle_simple_method_object___add__
3422
_handle_simple_method_int___sub__ = _handle_simple_method_object___sub__
3423
_handle_simple_method_int___mul__ = _handle_simple_method_object___mul__
3424
_handle_simple_method_int___eq__ = _handle_simple_method_object___eq__
3425
_handle_simple_method_int___ne__ = _handle_simple_method_object___ne__
3426
_handle_simple_method_int___and__ = _handle_simple_method_object___and__
3427
_handle_simple_method_int___or__ = _handle_simple_method_object___or__
3428
_handle_simple_method_int___xor__ = _handle_simple_method_object___xor__
3429
_handle_simple_method_int___rshift__ = _handle_simple_method_object___rshift__
3430
_handle_simple_method_int___lshift__ = _handle_simple_method_object___lshift__
3431
_handle_simple_method_int___mod__ = _handle_simple_method_object___mod__
3432
_handle_simple_method_int___floordiv__ = _handle_simple_method_object___floordiv__
3433
_handle_simple_method_int___truediv__ = _handle_simple_method_object___truediv__
3435
def _optimise_num_div(self, operator, node, function, args, is_unbound_method):
3436
if len(args) != 2 or not args[1].has_constant_result() or args[1].constant_result == 0:
3438
if isinstance(args[1], ExprNodes.IntNode):
3439
if not (-2**30 <= args[1].constant_result <= 2**30):
3441
elif isinstance(args[1], ExprNodes.FloatNode):
3442
if not (-2**53 <= args[1].constant_result <= 2**53):
3446
return self._optimise_num_binop(operator, node, function, args, is_unbound_method)
3448
def _handle_simple_method_float___add__(self, node, function, args, is_unbound_method):
3449
return self._optimise_num_binop('Add', node, function, args, is_unbound_method)
3451
def _handle_simple_method_float___sub__(self, node, function, args, is_unbound_method):
3452
return self._optimise_num_binop('Subtract', node, function, args, is_unbound_method)
3454
def _handle_simple_method_float___truediv__(self, node, function, args, is_unbound_method):
3455
return self._optimise_num_binop('TrueDivide', node, function, args, is_unbound_method)
3457
def _handle_simple_method_float___div__(self, node, function, args, is_unbound_method):
3458
return self._optimise_num_binop('Divide', node, function, args, is_unbound_method)
3460
def _handle_simple_method_float___mod__(self, node, function, args, is_unbound_method):
3461
return self._optimise_num_binop('Remainder', node, function, args, is_unbound_method)
3463
def _handle_simple_method_float___eq__(self, node, function, args, is_unbound_method):
3464
return self._optimise_num_binop('Eq', node, function, args, is_unbound_method)
3466
def _handle_simple_method_float___ne__(self, node, function, args, is_unbound_method):
3467
return self._optimise_num_binop('Ne', node, function, args, is_unbound_method)
3469
def _optimise_num_binop(self, operator, node, function, args, is_unbound_method):
3471
Optimise math operators for (likely) float or small integer operations.
3473
if getattr(node, "special_bool_cmp_function", None):
3479
if node.type.is_pyobject:
3480
ret_type = PyrexTypes.py_object_type
3481
elif node.type is PyrexTypes.c_bint_type and operator in ('Eq', 'Ne'):
3482
ret_type = PyrexTypes.c_bint_type
3486
result = optimise_numeric_binop(operator, node, ret_type, args[0], args[1])
3489
func_cname, utility_code, extra_args, num_type = result
3490
assert all([arg.type.is_pyobject for arg in args])
3491
args = list(args) + extra_args
3493
call_node = self._substitute_method_call(
3496
self.Pyx_BinopInt_func_types[(num_type, ret_type)],
3497
'__%s__' % operator[:3].lower(), is_unbound_method, args,
3498
may_return_none=True,
3499
with_none_check=False,
3500
utility_code=utility_code)
3502
if node.type.is_pyobject and not ret_type.is_pyobject:
3503
call_node = ExprNodes.CoerceToPyTypeNode(call_node, self.current_env(), node.type)
3508
PyUnicode_uchar_predicate_func_type = PyrexTypes.CFuncType(
3509
PyrexTypes.c_bint_type, [
3510
PyrexTypes.CFuncTypeArg("uchar", PyrexTypes.c_py_ucs4_type, None),
3513
def _inject_unicode_predicate(self, node, function, args, is_unbound_method):
3514
if is_unbound_method or len(args) != 1:
3517
if not isinstance(ustring, ExprNodes.CoerceToPyTypeNode) or \
3518
not ustring.arg.type.is_unicode_char:
3521
method_name = function.attribute
3522
if method_name in ('istitle', 'isprintable'):
3525
utility_code = UtilityCode.load_cached(
3526
"py_unicode_%s" % method_name, "StringTools.c")
3527
function_name = '__Pyx_Py_UNICODE_%s' % method_name.upper()
3530
function_name = 'Py_UNICODE_%s' % method_name.upper()
3531
func_call = self._substitute_method_call(
3533
function_name, self.PyUnicode_uchar_predicate_func_type,
3534
method_name, is_unbound_method, [uchar],
3535
utility_code = utility_code)
3536
if node.type.is_pyobject:
3537
func_call = func_call.coerce_to_pyobject(self.current_env)
3540
_handle_simple_method_unicode_isalnum = _inject_unicode_predicate
3541
_handle_simple_method_unicode_isalpha = _inject_unicode_predicate
3542
_handle_simple_method_unicode_isdecimal = _inject_unicode_predicate
3543
_handle_simple_method_unicode_isdigit = _inject_unicode_predicate
3544
_handle_simple_method_unicode_islower = _inject_unicode_predicate
3545
_handle_simple_method_unicode_isnumeric = _inject_unicode_predicate
3546
_handle_simple_method_unicode_isspace = _inject_unicode_predicate
3547
_handle_simple_method_unicode_istitle = _inject_unicode_predicate
3548
_handle_simple_method_unicode_isupper = _inject_unicode_predicate
3549
_handle_simple_method_unicode_isprintable = _inject_unicode_predicate
3551
PyUnicode_uchar_conversion_func_type = PyrexTypes.CFuncType(
3552
PyrexTypes.c_py_ucs4_type, [
3553
PyrexTypes.CFuncTypeArg("uchar", PyrexTypes.c_py_ucs4_type, None),
3558
def _inject_unicode_character_conversion(self, node, function, args, is_unbound_method):
3559
if is_unbound_method or len(args) != 1:
3562
if not isinstance(ustring, ExprNodes.CoerceToPyTypeNode) or \
3563
not ustring.arg.type.is_unicode_char:
3566
method_name = function.attribute
3567
function_name = 'Py_UNICODE_TO%s' % method_name.upper()
3568
func_call = self._substitute_method_call(
3570
function_name, self.PyUnicode_uchar_conversion_func_type,
3571
method_name, is_unbound_method, [uchar])
3572
if node.type.is_pyobject:
3573
func_call = func_call.coerce_to_pyobject(self.current_env)
3576
#_handle_simple_method_unicode_lower = _inject_unicode_character_conversion
3577
#_handle_simple_method_unicode_upper = _inject_unicode_character_conversion
3578
#_handle_simple_method_unicode_title = _inject_unicode_character_conversion
3581
PyUnicode_Splitlines_func_type = PyrexTypes.CFuncType(
3582
Builtin.list_type, [
3583
PyrexTypes.CFuncTypeArg("str", Builtin.unicode_type, None),
3584
PyrexTypes.CFuncTypeArg("keepends", PyrexTypes.c_bint_type, None),
3587
def _handle_simple_method_unicode_splitlines(self, node, function, args, is_unbound_method):
3588
"""Replace unicode.splitlines(...) by a direct call to the
3589
corresponding C-API function.
3591
if len(args) not in (1,2):
3592
self._error_wrong_arg_count('unicode.splitlines', node, args, "1 or 2")
3594
self._inject_bint_default_argument(node, args, 1, False)
3596
return self._substitute_method_call(
3598
"PyUnicode_Splitlines", self.PyUnicode_Splitlines_func_type,
3599
'splitlines', is_unbound_method, args)
3601
PyUnicode_Split_func_type = PyrexTypes.CFuncType(
3602
Builtin.list_type, [
3603
PyrexTypes.CFuncTypeArg("str", Builtin.unicode_type, None),
3604
PyrexTypes.CFuncTypeArg("sep", PyrexTypes.py_object_type, None),
3605
PyrexTypes.CFuncTypeArg("maxsplit", PyrexTypes.c_py_ssize_t_type, None),
3609
def _handle_simple_method_unicode_split(self, node, function, args, is_unbound_method):
3610
"""Replace unicode.split(...) by a direct call to the
3611
corresponding C-API function.
3613
if len(args) not in (1,2,3):
3614
self._error_wrong_arg_count('unicode.split', node, args, "1-3")
3617
args.append(ExprNodes.NullNode(node.pos))
3619
self._inject_null_for_none(args, 1)
3620
self._inject_int_default_argument(
3621
node, args, 2, PyrexTypes.c_py_ssize_t_type, "-1")
3623
return self._substitute_method_call(
3625
"PyUnicode_Split", self.PyUnicode_Split_func_type,
3626
'split', is_unbound_method, args)
3628
PyUnicode_Join_func_type = PyrexTypes.CFuncType(
3629
Builtin.unicode_type, [
3630
PyrexTypes.CFuncTypeArg("str", Builtin.unicode_type, None),
3631
PyrexTypes.CFuncTypeArg("seq", PyrexTypes.py_object_type, None),
3634
def _handle_simple_method_unicode_join(self, node, function, args, is_unbound_method):
3636
unicode.join() builds a list first => see if we can do this more efficiently
3639
self._error_wrong_arg_count('unicode.join', node, args, "2")
3641
if isinstance(args[1], ExprNodes.GeneratorExpressionNode):
3642
gen_expr_node = args[1]
3643
loop_node = gen_expr_node.loop
3645
yield_statements = _find_yield_statements(loop_node)
3646
if yield_statements:
3647
inlined_genexpr = ExprNodes.InlinedGeneratorExpressionNode(
3648
node.pos, gen_expr_node, orig_func='list',
3649
comprehension_type=Builtin.list_type)
3651
for yield_expression, yield_stat_node in yield_statements:
3652
append_node = ExprNodes.ComprehensionAppendNode(
3653
yield_expression.pos,
3654
expr=yield_expression,
3655
target=inlined_genexpr.target)
3657
Visitor.recursively_replace_node(gen_expr_node, yield_stat_node, append_node)
3659
args[1] = inlined_genexpr
3661
return self._substitute_method_call(
3663
"PyUnicode_Join", self.PyUnicode_Join_func_type,
3664
'join', is_unbound_method, args)
3666
PyString_Tailmatch_func_type = PyrexTypes.CFuncType(
3667
PyrexTypes.c_bint_type, [
3668
PyrexTypes.CFuncTypeArg("str", PyrexTypes.py_object_type, None),
3669
PyrexTypes.CFuncTypeArg("substring", PyrexTypes.py_object_type, None),
3670
PyrexTypes.CFuncTypeArg("start", PyrexTypes.c_py_ssize_t_type, None),
3671
PyrexTypes.CFuncTypeArg("end", PyrexTypes.c_py_ssize_t_type, None),
3672
PyrexTypes.CFuncTypeArg("direction", PyrexTypes.c_int_type, None),
3676
def _handle_simple_method_unicode_endswith(self, node, function, args, is_unbound_method):
3677
return self._inject_tailmatch(
3678
node, function, args, is_unbound_method, 'str', 'endswith',
3679
unicode_tailmatch_utility_code, +1)
3681
def _handle_simple_method_unicode_startswith(self, node, function, args, is_unbound_method):
3682
return self._inject_tailmatch(
3683
node, function, args, is_unbound_method, 'str', 'startswith',
3684
unicode_tailmatch_utility_code, -1)
3686
def _inject_tailmatch(self, node, function, args, is_unbound_method, type_name,
3687
method_name, utility_code, direction):
3688
"""Replace unicode.startswith(...) and unicode.endswith(...)
3689
by a direct call to the corresponding C-API function.
3691
if len(args) not in (2,3,4):
3692
self._error_wrong_arg_count(f"{type_name}.{method_name}", node, args, "2-4")
3694
self._inject_int_default_argument(
3695
node, args, 2, PyrexTypes.c_py_ssize_t_type, "0")
3696
self._inject_int_default_argument(
3697
node, args, 3, PyrexTypes.c_py_ssize_t_type, "PY_SSIZE_T_MAX")
3698
args.append(ExprNodes.IntNode(
3699
node.pos, value=str(direction), type=PyrexTypes.c_int_type))
3701
if type_name == 'str':
3702
func_name = "__Pyx_PyUnicode_Tailmatch"
3704
func_name = f"__Pyx_Py{type_name.capitalize()}_Tailmatch"
3706
method_call = self._substitute_method_call(
3708
func_name, self.PyString_Tailmatch_func_type,
3709
method_name, is_unbound_method, args,
3710
utility_code = utility_code)
3711
return method_call.coerce_to(Builtin.bool_type, self.current_env())
3713
PyUnicode_Find_func_type = PyrexTypes.CFuncType(
3714
PyrexTypes.c_py_ssize_t_type, [
3715
PyrexTypes.CFuncTypeArg("str", Builtin.unicode_type, None),
3716
PyrexTypes.CFuncTypeArg("substring", PyrexTypes.py_object_type, None),
3717
PyrexTypes.CFuncTypeArg("start", PyrexTypes.c_py_ssize_t_type, None),
3718
PyrexTypes.CFuncTypeArg("end", PyrexTypes.c_py_ssize_t_type, None),
3719
PyrexTypes.CFuncTypeArg("direction", PyrexTypes.c_int_type, None),
3723
def _handle_simple_method_unicode_find(self, node, function, args, is_unbound_method):
3724
return self._inject_unicode_find(
3725
node, function, args, is_unbound_method, 'find', +1)
3727
def _handle_simple_method_unicode_rfind(self, node, function, args, is_unbound_method):
3728
return self._inject_unicode_find(
3729
node, function, args, is_unbound_method, 'rfind', -1)
3731
def _inject_unicode_find(self, node, function, args, is_unbound_method,
3732
method_name, direction):
3733
"""Replace unicode.find(...) and unicode.rfind(...) by a
3734
direct call to the corresponding C-API function.
3736
if len(args) not in (2,3,4):
3737
self._error_wrong_arg_count('unicode.%s' % method_name, node, args, "2-4")
3739
self._inject_int_default_argument(
3740
node, args, 2, PyrexTypes.c_py_ssize_t_type, "0")
3741
self._inject_int_default_argument(
3742
node, args, 3, PyrexTypes.c_py_ssize_t_type, "PY_SSIZE_T_MAX")
3743
args.append(ExprNodes.IntNode(
3744
node.pos, value=str(direction), type=PyrexTypes.c_int_type))
3746
method_call = self._substitute_method_call(
3747
node, function, "PyUnicode_Find", self.PyUnicode_Find_func_type,
3748
method_name, is_unbound_method, args)
3749
return method_call.coerce_to_pyobject(self.current_env())
3751
PyUnicode_Count_func_type = PyrexTypes.CFuncType(
3752
PyrexTypes.c_py_ssize_t_type, [
3753
PyrexTypes.CFuncTypeArg("str", Builtin.unicode_type, None),
3754
PyrexTypes.CFuncTypeArg("substring", PyrexTypes.py_object_type, None),
3755
PyrexTypes.CFuncTypeArg("start", PyrexTypes.c_py_ssize_t_type, None),
3756
PyrexTypes.CFuncTypeArg("end", PyrexTypes.c_py_ssize_t_type, None),
3760
def _handle_simple_method_unicode_count(self, node, function, args, is_unbound_method):
3761
"""Replace unicode.count(...) by a direct call to the
3762
corresponding C-API function.
3764
if len(args) not in (2,3,4):
3765
self._error_wrong_arg_count('unicode.count', node, args, "2-4")
3767
self._inject_int_default_argument(
3768
node, args, 2, PyrexTypes.c_py_ssize_t_type, "0")
3769
self._inject_int_default_argument(
3770
node, args, 3, PyrexTypes.c_py_ssize_t_type, "PY_SSIZE_T_MAX")
3772
method_call = self._substitute_method_call(
3773
node, function, "PyUnicode_Count", self.PyUnicode_Count_func_type,
3774
'count', is_unbound_method, args)
3775
return method_call.coerce_to_pyobject(self.current_env())
3777
PyUnicode_Replace_func_type = PyrexTypes.CFuncType(
3778
Builtin.unicode_type, [
3779
PyrexTypes.CFuncTypeArg("str", Builtin.unicode_type, None),
3780
PyrexTypes.CFuncTypeArg("substring", PyrexTypes.py_object_type, None),
3781
PyrexTypes.CFuncTypeArg("replstr", PyrexTypes.py_object_type, None),
3782
PyrexTypes.CFuncTypeArg("maxcount", PyrexTypes.c_py_ssize_t_type, None),
3785
def _handle_simple_method_unicode_replace(self, node, function, args, is_unbound_method):
3786
"""Replace unicode.replace(...) by a direct call to the
3787
corresponding C-API function.
3789
if len(args) not in (3,4):
3790
self._error_wrong_arg_count('unicode.replace', node, args, "3-4")
3792
self._inject_int_default_argument(
3793
node, args, 3, PyrexTypes.c_py_ssize_t_type, "-1")
3795
return self._substitute_method_call(
3796
node, function, "PyUnicode_Replace", self.PyUnicode_Replace_func_type,
3797
'replace', is_unbound_method, args)
3799
PyUnicode_AsEncodedString_func_type = PyrexTypes.CFuncType(
3800
Builtin.bytes_type, [
3801
PyrexTypes.CFuncTypeArg("obj", Builtin.unicode_type, None),
3802
PyrexTypes.CFuncTypeArg("encoding", PyrexTypes.c_const_char_ptr_type, None),
3803
PyrexTypes.CFuncTypeArg("errors", PyrexTypes.c_const_char_ptr_type, None),
3806
PyUnicode_AsXyzString_func_type = PyrexTypes.CFuncType(
3807
Builtin.bytes_type, [
3808
PyrexTypes.CFuncTypeArg("obj", Builtin.unicode_type, None),
3811
_special_encodings = ['UTF8', 'UTF16', 'UTF-16LE', 'UTF-16BE', 'Latin1', 'ASCII',
3812
'unicode_escape', 'raw_unicode_escape']
3814
_special_codecs = [ (name, codecs.getencoder(name))
3815
for name in _special_encodings ]
3817
def _handle_simple_method_unicode_encode(self, node, function, args, is_unbound_method):
3818
"""Replace unicode.encode(...) by a direct C-API call to the
3819
corresponding codec.
3821
if len(args) < 1 or len(args) > 3:
3822
self._error_wrong_arg_count('unicode.encode', node, args, '1-3')
3825
string_node = args[0]
3827
parameters = self._unpack_encoding_and_error_mode(node.pos, args)
3828
if parameters is None:
3830
encoding, encoding_node, error_handling, error_handling_node = parameters
3832
if string_node.has_constant_result():
3835
value = string_node.constant_result.encode(encoding, error_handling)
3840
value = bytes_literal(value, encoding or 'UTF-8')
3841
return ExprNodes.BytesNode(string_node.pos, value=value, type=Builtin.bytes_type)
3844
null_node = ExprNodes.NullNode(node.pos)
3845
return self._substitute_method_call(
3846
node, function, "PyUnicode_AsEncodedString",
3847
self.PyUnicode_AsEncodedString_func_type,
3848
'encode', is_unbound_method, [string_node, null_node, null_node])
3850
if encoding and error_handling == 'strict':
3852
codec_name = self._find_special_codec_name(encoding)
3853
if codec_name is not None and '-' not in codec_name:
3854
encode_function = "PyUnicode_As%sString" % codec_name
3855
return self._substitute_method_call(
3856
node, function, encode_function,
3857
self.PyUnicode_AsXyzString_func_type,
3858
'encode', is_unbound_method, [string_node])
3860
return self._substitute_method_call(
3861
node, function, "PyUnicode_AsEncodedString",
3862
self.PyUnicode_AsEncodedString_func_type,
3863
'encode', is_unbound_method,
3864
[string_node, encoding_node, error_handling_node])
3866
PyUnicode_DecodeXyz_func_ptr_type = PyrexTypes.CPtrType(PyrexTypes.CFuncType(
3867
Builtin.unicode_type, [
3868
PyrexTypes.CFuncTypeArg("string", PyrexTypes.c_const_char_ptr_type, None),
3869
PyrexTypes.CFuncTypeArg("size", PyrexTypes.c_py_ssize_t_type, None),
3870
PyrexTypes.CFuncTypeArg("errors", PyrexTypes.c_const_char_ptr_type, None),
3873
_decode_c_string_func_type = PyrexTypes.CFuncType(
3874
Builtin.unicode_type, [
3875
PyrexTypes.CFuncTypeArg("string", PyrexTypes.c_const_char_ptr_type, None),
3876
PyrexTypes.CFuncTypeArg("start", PyrexTypes.c_py_ssize_t_type, None),
3877
PyrexTypes.CFuncTypeArg("stop", PyrexTypes.c_py_ssize_t_type, None),
3878
PyrexTypes.CFuncTypeArg("encoding", PyrexTypes.c_const_char_ptr_type, None),
3879
PyrexTypes.CFuncTypeArg("errors", PyrexTypes.c_const_char_ptr_type, None),
3880
PyrexTypes.CFuncTypeArg("decode_func", PyUnicode_DecodeXyz_func_ptr_type, None),
3883
_decode_bytes_func_type = PyrexTypes.CFuncType(
3884
Builtin.unicode_type, [
3885
PyrexTypes.CFuncTypeArg("string", PyrexTypes.py_object_type, None),
3886
PyrexTypes.CFuncTypeArg("start", PyrexTypes.c_py_ssize_t_type, None),
3887
PyrexTypes.CFuncTypeArg("stop", PyrexTypes.c_py_ssize_t_type, None),
3888
PyrexTypes.CFuncTypeArg("encoding", PyrexTypes.c_const_char_ptr_type, None),
3889
PyrexTypes.CFuncTypeArg("errors", PyrexTypes.c_const_char_ptr_type, None),
3890
PyrexTypes.CFuncTypeArg("decode_func", PyUnicode_DecodeXyz_func_ptr_type, None),
3893
_decode_cpp_string_func_type = None
3895
def _handle_simple_method_bytes_decode(self, node, function, args, is_unbound_method):
3896
"""Replace char*.decode() by a direct C-API call to the
3897
corresponding codec, possibly resolving a slice on the char*.
3899
if not (1 <= len(args) <= 3):
3900
self._error_wrong_arg_count('bytes.decode', node, args, '1-3')
3904
string_node = args[0]
3905
parameters = self._unpack_encoding_and_error_mode(node.pos, args)
3906
if parameters is None:
3908
encoding, encoding_node, error_handling, error_handling_node = parameters
3910
if string_node.has_constant_result():
3912
constant_result = string_node.constant_result.decode(encoding, error_handling)
3913
except (AttributeError, ValueError, UnicodeDecodeError):
3918
value=EncodedString(constant_result),
3919
bytes_value=string_node.constant_result,
3924
if isinstance(string_node, ExprNodes.SliceIndexNode):
3925
index_node = string_node
3926
string_node = index_node.base
3927
start, stop = index_node.start, index_node.stop
3928
if not start or start.constant_result == 0:
3930
if isinstance(string_node, ExprNodes.CoerceToPyTypeNode):
3931
string_node = string_node.arg
3933
string_type = string_node.type
3934
if string_type in (Builtin.bytes_type, Builtin.bytearray_type):
3935
if is_unbound_method:
3936
string_node = string_node.as_none_safe_node(
3937
"descriptor '%s' requires a '%s' object but received a 'NoneType'",
3938
format_args=['decode', string_type.name])
3940
string_node = string_node.as_none_safe_node(
3941
"'NoneType' object has no attribute '%.30s'",
3942
error="PyExc_AttributeError",
3943
format_args=['decode'])
3944
elif not string_type.is_string and not string_type.is_cpp_string:
3949
start = ExprNodes.IntNode(node.pos, value='0', constant_result=0)
3950
elif not start.type.is_int:
3951
start = start.coerce_to(PyrexTypes.c_py_ssize_t_type, self.current_env())
3952
if stop and not stop.type.is_int:
3953
stop = stop.coerce_to(PyrexTypes.c_py_ssize_t_type, self.current_env())
3957
if encoding is not None:
3958
codec_name = self._find_special_codec_name(encoding)
3959
if codec_name is not None:
3960
if codec_name in ('UTF16', 'UTF-16LE', 'UTF-16BE'):
3961
codec_cname = "__Pyx_PyUnicode_Decode%s" % codec_name.replace('-', '')
3963
codec_cname = "PyUnicode_Decode%s" % codec_name
3964
decode_function = ExprNodes.RawCNameExprNode(
3965
node.pos, type=self.PyUnicode_DecodeXyz_func_ptr_type, cname=codec_cname)
3966
encoding_node = ExprNodes.NullNode(node.pos)
3968
decode_function = ExprNodes.NullNode(node.pos)
3972
if string_type.is_string:
3976
if not string_node.is_name:
3977
string_node = UtilNodes.LetRefNode(string_node)
3978
temps.append(string_node)
3979
stop = ExprNodes.PythonCapiCallNode(
3980
string_node.pos, "__Pyx_ssize_strlen", self.Pyx_ssize_strlen_func_type,
3984
helper_func_type = self._decode_c_string_func_type
3985
utility_code_name = 'decode_c_string'
3986
elif string_type.is_cpp_string:
3989
stop = ExprNodes.IntNode(node.pos, value='PY_SSIZE_T_MAX',
3990
constant_result=ExprNodes.not_a_constant)
3991
if self._decode_cpp_string_func_type is None:
3993
self._decode_cpp_string_func_type = PyrexTypes.CFuncType(
3994
Builtin.unicode_type, [
3995
PyrexTypes.CFuncTypeArg("string", string_type, None),
3996
PyrexTypes.CFuncTypeArg("start", PyrexTypes.c_py_ssize_t_type, None),
3997
PyrexTypes.CFuncTypeArg("stop", PyrexTypes.c_py_ssize_t_type, None),
3998
PyrexTypes.CFuncTypeArg("encoding", PyrexTypes.c_const_char_ptr_type, None),
3999
PyrexTypes.CFuncTypeArg("errors", PyrexTypes.c_const_char_ptr_type, None),
4000
PyrexTypes.CFuncTypeArg("decode_func", self.PyUnicode_DecodeXyz_func_ptr_type, None),
4002
helper_func_type = self._decode_cpp_string_func_type
4003
utility_code_name = 'decode_cpp_string'
4007
stop = ExprNodes.IntNode(node.pos, value='PY_SSIZE_T_MAX',
4008
constant_result=ExprNodes.not_a_constant)
4009
helper_func_type = self._decode_bytes_func_type
4010
if string_type is Builtin.bytes_type:
4011
utility_code_name = 'decode_bytes'
4013
utility_code_name = 'decode_bytearray'
4015
node = ExprNodes.PythonCapiCallNode(
4016
node.pos, '__Pyx_%s' % utility_code_name, helper_func_type,
4017
args=[string_node, start, stop, encoding_node, error_handling_node, decode_function],
4018
is_temp=node.is_temp,
4019
utility_code=UtilityCode.load_cached(utility_code_name, 'StringTools.c'),
4022
for temp in temps[::-1]:
4023
node = UtilNodes.EvalWithTempExprNode(temp, node)
4026
_handle_simple_method_bytearray_decode = _handle_simple_method_bytes_decode
4028
def _find_special_codec_name(self, encoding):
4030
requested_codec = codecs.getencoder(encoding)
4033
for name, codec in self._special_codecs:
4034
if codec == requested_codec:
4036
name = ''.join([s.capitalize()
4037
for s in name.split('_')])
4041
def _unpack_encoding_and_error_mode(self, pos, args):
4042
null_node = ExprNodes.NullNode(pos)
4045
encoding, encoding_node = self._unpack_string_and_cstring_node(args[1])
4046
if encoding_node is None:
4050
encoding_node = null_node
4053
error_handling, error_handling_node = self._unpack_string_and_cstring_node(args[2])
4054
if error_handling_node is None:
4056
if error_handling == 'strict':
4057
error_handling_node = null_node
4059
error_handling = 'strict'
4060
error_handling_node = null_node
4062
return (encoding, encoding_node, error_handling, error_handling_node)
4064
def _unpack_string_and_cstring_node(self, node):
4065
if isinstance(node, ExprNodes.CoerceToPyTypeNode):
4067
if isinstance(node, ExprNodes.UnicodeNode):
4068
encoding = node.value
4069
node = ExprNodes.BytesNode(
4070
node.pos, value=encoding.as_utf8_string(), type=PyrexTypes.c_const_char_ptr_type)
4071
elif isinstance(node, ExprNodes.BytesNode):
4072
encoding = node.value.decode('ISO-8859-1')
4073
node = ExprNodes.BytesNode(
4074
node.pos, value=node.value, type=PyrexTypes.c_const_char_ptr_type)
4075
elif node.type is Builtin.bytes_type:
4077
node = node.coerce_to(PyrexTypes.c_const_char_ptr_type, self.current_env())
4078
elif node.type.is_string:
4081
encoding = node = None
4082
return encoding, node
4084
def _handle_simple_method_bytes_endswith(self, node, function, args, is_unbound_method):
4085
return self._inject_tailmatch(
4086
node, function, args, is_unbound_method, 'bytes', 'endswith',
4087
bytes_tailmatch_utility_code, +1)
4089
def _handle_simple_method_bytes_startswith(self, node, function, args, is_unbound_method):
4090
return self._inject_tailmatch(
4091
node, function, args, is_unbound_method, 'bytes', 'startswith',
4092
bytes_tailmatch_utility_code, -1)
4094
''' # disabled for now, enable when we consider it worth it (see StringTools.c)
4095
def _handle_simple_method_bytearray_endswith(self, node, function, args, is_unbound_method):
4096
return self._inject_tailmatch(
4097
node, function, args, is_unbound_method, 'bytearray', 'endswith',
4098
bytes_tailmatch_utility_code, +1)
4100
def _handle_simple_method_bytearray_startswith(self, node, function, args, is_unbound_method):
4101
return self._inject_tailmatch(
4102
node, function, args, is_unbound_method, 'bytearray', 'startswith',
4103
bytes_tailmatch_utility_code, -1)
4108
def _substitute_method_call(self, node, function, name, func_type,
4109
attr_name, is_unbound_method, args=(),
4110
utility_code=None, is_temp=None,
4111
may_return_none=ExprNodes.PythonCapiCallNode.may_return_none,
4112
with_none_check=True):
4114
if with_none_check and args:
4115
args[0] = self._wrap_self_arg(args[0], function, is_unbound_method, attr_name)
4117
is_temp = node.is_temp
4118
return ExprNodes.PythonCapiCallNode(
4119
node.pos, name, func_type,
4122
utility_code = utility_code,
4123
may_return_none = may_return_none,
4124
result_is_used = node.result_is_used,
4127
def _wrap_self_arg(self, self_arg, function, is_unbound_method, attr_name):
4128
if self_arg.is_literal:
4130
if is_unbound_method:
4131
self_arg = self_arg.as_none_safe_node(
4132
"descriptor '%s' requires a '%s' object but received a 'NoneType'",
4133
format_args=[attr_name, self_arg.type.name])
4135
self_arg = self_arg.as_none_safe_node(
4136
"'NoneType' object has no attribute '%{}s'".format('.30' if len(attr_name) <= 30 else ''),
4137
error="PyExc_AttributeError",
4138
format_args=[attr_name])
4141
obj_to_obj_func_type = PyrexTypes.CFuncType(
4142
PyrexTypes.py_object_type, [
4143
PyrexTypes.CFuncTypeArg("obj", PyrexTypes.py_object_type, None)
4146
def _inject_null_for_none(self, args, index):
4147
if len(args) <= index:
4150
args[index] = ExprNodes.NullNode(arg.pos) if arg.is_none else ExprNodes.PythonCapiCallNode(
4151
arg.pos, "__Pyx_NoneAsNull",
4152
self.obj_to_obj_func_type,
4153
args=[arg.coerce_to_simple(self.current_env())],
4157
def _inject_int_default_argument(self, node, args, arg_index, type, default_value):
4160
assert len(args) >= arg_index
4161
if len(args) == arg_index or args[arg_index].is_none:
4162
args.append(ExprNodes.IntNode(node.pos, value=str(default_value),
4163
type=type, constant_result=default_value))
4165
arg = args[arg_index].coerce_to(type, self.current_env())
4166
if isinstance(arg, ExprNodes.CoerceFromPyTypeNode):
4168
arg.special_none_cvalue = str(default_value)
4169
args[arg_index] = arg
4171
def _inject_bint_default_argument(self, node, args, arg_index, default_value):
4172
assert len(args) >= arg_index
4173
if len(args) == arg_index:
4174
default_value = bool(default_value)
4175
args.append(ExprNodes.BoolNode(node.pos, value=default_value,
4176
constant_result=default_value))
4178
args[arg_index] = args[arg_index].coerce_to_boolean(self.current_env())
4181
def optimise_numeric_binop(operator, node, ret_type, arg0, arg1):
4183
Optimise math operators for (likely) float or small integer operations.
4187
num_nodes = (ExprNodes.IntNode, ExprNodes.FloatNode)
4188
if isinstance(arg1, num_nodes):
4189
if arg0.type is not PyrexTypes.py_object_type and arg0.type is not Builtin.int_type:
4193
elif isinstance(arg0, num_nodes):
4194
if arg1.type is not PyrexTypes.py_object_type and arg1.type is not Builtin.int_type:
4201
if not numval.has_constant_result():
4206
is_float = isinstance(numval, ExprNodes.FloatNode)
4207
num_type = PyrexTypes.c_double_type if is_float else PyrexTypes.c_long_type
4209
if operator not in ('Add', 'Subtract', 'Remainder', 'TrueDivide', 'Divide', 'Eq', 'Ne'):
4211
elif operator == 'Divide':
4214
elif abs(numval.constant_result) > 2**30:
4218
if operator in ('TrueDivide', 'FloorDivide', 'Divide', 'Remainder'):
4219
if arg1.constant_result == 0:
4225
extra_args.append((ExprNodes.FloatNode if is_float else ExprNodes.IntNode)(
4226
numval.pos, value=numval.value, constant_result=numval.constant_result,
4228
inplace = node.inplace if isinstance(node, ExprNodes.NumBinopNode) else False
4229
extra_args.append(ExprNodes.BoolNode(node.pos, value=inplace, constant_result=inplace))
4230
if is_float or operator not in ('Eq', 'Ne'):
4232
zerodivision_check = arg_order == 'CObj' and (
4233
not node.cdivision if isinstance(node, ExprNodes.DivNode) else False)
4234
extra_args.append(ExprNodes.BoolNode(node.pos, value=zerodivision_check, constant_result=zerodivision_check))
4236
utility_code = TempitaUtilityCode.load_cached(
4237
"PyFloatBinop" if is_float else "PyLongCompare" if operator in ('Eq', 'Ne') else "PyLongBinop",
4239
context=dict(op=operator, order=arg_order, ret_type=ret_type))
4241
func_cname = "__Pyx_Py%s_%s%s%s" % (
4242
'Float' if is_float else 'Long',
4243
'' if ret_type.is_pyobject else 'Bool',
4247
return func_cname, utility_code, extra_args, num_type
4250
unicode_tailmatch_utility_code = UtilityCode.load_cached('unicode_tailmatch', 'StringTools.c')
4251
bytes_tailmatch_utility_code = UtilityCode.load_cached('bytes_tailmatch', 'StringTools.c')
4254
class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
4255
"""Calculate the result of constant expressions to store it in
4256
``expr_node.constant_result``, and replace trivial cases by their
4261
- We calculate float constants to make them available to the
4262
compiler, but we do not aggregate them into a single literal
4263
node to prevent any loss of precision.
4265
- We recursively calculate constants from non-literal nodes to
4266
make them available to the compiler, but we only aggregate
4267
literal nodes at each step. Non-literal nodes are never merged
4271
def __init__(self, reevaluate=False):
4273
The reevaluate argument specifies whether constant values that were
4274
previously computed should be recomputed.
4277
self.reevaluate = reevaluate
4279
def _calculate_const(self, node):
4280
if (not self.reevaluate and
4281
node.constant_result is not ExprNodes.constant_value_not_set):
4285
not_a_constant = ExprNodes.not_a_constant
4286
node.constant_result = not_a_constant
4289
children = self.visitchildren(node)
4290
for child_result in children.values():
4291
if type(child_result) is list:
4292
for child in child_result:
4293
if getattr(child, 'constant_result', not_a_constant) is not_a_constant:
4295
elif getattr(child_result, 'constant_result', not_a_constant) is not_a_constant:
4300
node.calculate_constant_result()
4303
except (ValueError, TypeError, KeyError, IndexError, AttributeError, ArithmeticError):
4308
import traceback, sys
4309
traceback.print_exc(file=sys.stdout)
4311
NODE_TYPE_ORDER = [ExprNodes.BoolNode, ExprNodes.CharNode,
4312
ExprNodes.IntNode, ExprNodes.FloatNode]
4314
def _widest_node_class(self, *nodes):
4316
return self.NODE_TYPE_ORDER[
4317
max(map(self.NODE_TYPE_ORDER.index, map(type, nodes)))]
4321
def _bool_node(self, node, value):
4323
return ExprNodes.BoolNode(node.pos, value=value, constant_result=value)
4325
def visit_ExprNode(self, node):
4326
self._calculate_const(node)
4329
def visit_UnopNode(self, node):
4330
self._calculate_const(node)
4331
if not node.has_constant_result():
4332
if node.operator == '!':
4333
return self._handle_NotNode(node)
4335
if not node.operand.is_literal:
4337
if node.operator == '!':
4338
return self._bool_node(node, node.constant_result)
4339
elif isinstance(node.operand, ExprNodes.BoolNode):
4340
return ExprNodes.IntNode(node.pos, value=str(int(node.constant_result)),
4341
type=PyrexTypes.c_int_type,
4342
constant_result=int(node.constant_result))
4343
elif node.operator == '+':
4344
return self._handle_UnaryPlusNode(node)
4345
elif node.operator == '-':
4346
return self._handle_UnaryMinusNode(node)
4349
_negate_operator = {
4356
def _handle_NotNode(self, node):
4357
operand = node.operand
4358
if isinstance(operand, ExprNodes.PrimaryCmpNode):
4359
operator = self._negate_operator(operand.operator)
4361
node = copy.copy(operand)
4362
node.operator = operator
4363
node = self.visit_PrimaryCmpNode(node)
4366
def _handle_UnaryMinusNode(self, node):
4368
if value.startswith('-'):
4374
node_type = node.operand.type
4375
if isinstance(node.operand, ExprNodes.FloatNode):
4377
return ExprNodes.FloatNode(node.pos, value=_negate(node.operand.value),
4379
constant_result=node.constant_result)
4380
if node_type.is_int and node_type.signed or \
4381
isinstance(node.operand, ExprNodes.IntNode) and node_type.is_pyobject:
4382
return ExprNodes.IntNode(node.pos, value=_negate(node.operand.value),
4384
longness=node.operand.longness,
4385
constant_result=node.constant_result)
4388
def _handle_UnaryPlusNode(self, node):
4389
if (node.operand.has_constant_result() and
4390
node.constant_result == node.operand.constant_result):
4394
def visit_BoolBinopNode(self, node):
4395
self._calculate_const(node)
4396
if not node.operand1.has_constant_result():
4398
if node.operand1.constant_result:
4399
if node.operator == 'and':
4400
return node.operand2
4402
return node.operand1
4404
if node.operator == 'and':
4405
return node.operand1
4407
return node.operand2
4409
def visit_BinopNode(self, node):
4410
self._calculate_const(node)
4411
if node.constant_result is ExprNodes.not_a_constant:
4413
if isinstance(node.constant_result, float):
4415
operand1, operand2 = node.operand1, node.operand2
4416
if not operand1.is_literal or not operand2.is_literal:
4421
type1, type2 = operand1.type, operand2.type
4422
if type1 is None or type2 is None:
4424
except AttributeError:
4427
if type1.is_numeric and type2.is_numeric:
4428
widest_type = PyrexTypes.widest_numeric_type(type1, type2)
4430
widest_type = PyrexTypes.py_object_type
4432
target_class = self._widest_node_class(operand1, operand2)
4433
if target_class is None:
4435
elif target_class is ExprNodes.BoolNode and node.operator in '+-//<<%**>>':
4437
target_class = ExprNodes.IntNode
4438
elif target_class is ExprNodes.CharNode and node.operator in '+-//<<%**>>&|^':
4440
target_class = ExprNodes.IntNode
4442
if target_class is ExprNodes.IntNode:
4443
unsigned = getattr(operand1, 'unsigned', '') and \
4444
getattr(operand2, 'unsigned', '')
4445
longness = "LL"[:max(len(getattr(operand1, 'longness', '')),
4446
len(getattr(operand2, 'longness', '')))]
4447
value = hex(int(node.constant_result))
4448
value = Utils.strip_py2_long_suffix(value)
4449
new_node = ExprNodes.IntNode(pos=node.pos,
4450
unsigned=unsigned, longness=longness,
4452
constant_result=int(node.constant_result))
4455
if widest_type.is_pyobject or new_node.type.is_pyobject:
4456
new_node.type = PyrexTypes.py_object_type
4458
new_node.type = PyrexTypes.widest_numeric_type(widest_type, new_node.type)
4460
if target_class is ExprNodes.BoolNode:
4461
node_value = node.constant_result
4463
node_value = str(node.constant_result)
4464
new_node = target_class(pos=node.pos, type = widest_type,
4466
constant_result = node.constant_result)
4469
def visit_AddNode(self, node):
4470
self._calculate_const(node)
4471
if node.constant_result is ExprNodes.not_a_constant:
4473
if node.operand1.is_string_literal and node.operand2.is_string_literal:
4475
str1, str2 = node.operand1, node.operand2
4476
if isinstance(str1, ExprNodes.UnicodeNode) and isinstance(str2, ExprNodes.UnicodeNode):
4478
if str1.bytes_value is not None and str2.bytes_value is not None:
4479
if str1.bytes_value.encoding == str2.bytes_value.encoding:
4480
bytes_value = bytes_literal(
4481
str1.bytes_value + str2.bytes_value,
4482
str1.bytes_value.encoding)
4483
string_value = EncodedString(node.constant_result)
4484
return ExprNodes.UnicodeNode(str1.pos, value=string_value, bytes_value=bytes_value)
4485
elif isinstance(str1, ExprNodes.BytesNode) and isinstance(str2, ExprNodes.BytesNode):
4486
if str1.value.encoding == str2.value.encoding:
4487
bytes_value = bytes_literal(node.constant_result, str1.value.encoding)
4488
return ExprNodes.BytesNode(str1.pos, value=bytes_value, constant_result=node.constant_result)
4491
return self.visit_BinopNode(node)
4493
def visit_MulNode(self, node):
4494
self._calculate_const(node)
4495
if node.operand1.is_sequence_constructor:
4496
return self._calculate_constant_seq(node, node.operand1, node.operand2)
4497
if isinstance(node.operand1, ExprNodes.IntNode) and \
4498
node.operand2.is_sequence_constructor:
4499
return self._calculate_constant_seq(node, node.operand2, node.operand1)
4500
if node.operand1.is_string_literal:
4501
return self._multiply_string(node, node.operand1, node.operand2)
4502
elif node.operand2.is_string_literal:
4503
return self._multiply_string(node, node.operand2, node.operand1)
4504
return self.visit_BinopNode(node)
4506
def _multiply_string(self, node, string_node, multiplier_node):
4507
multiplier = multiplier_node.constant_result
4508
if not isinstance(multiplier, int):
4510
if not (node.has_constant_result() and isinstance(node.constant_result, _py_string_types)):
4512
if len(node.constant_result) > 256:
4516
if isinstance(string_node, ExprNodes.BytesNode):
4517
build_string = bytes_literal
4518
elif isinstance(string_node, ExprNodes.UnicodeNode):
4519
build_string = encoded_string
4520
if string_node.bytes_value is not None:
4521
string_node.bytes_value = bytes_literal(
4522
string_node.bytes_value * multiplier,
4523
string_node.bytes_value.encoding)
4525
assert False, "unknown string node type: %s" % type(string_node)
4526
string_node.value = build_string(
4527
string_node.value * multiplier,
4528
string_node.value.encoding)
4530
string_node.constant_result = string_node.value
4533
def _calculate_constant_seq(self, node, sequence_node, factor):
4534
if factor.constant_result != 1 and sequence_node.args:
4535
if isinstance(factor.constant_result, int) and factor.constant_result <= 0:
4536
del sequence_node.args[:]
4537
sequence_node.mult_factor = None
4538
elif sequence_node.mult_factor is not None:
4539
if (isinstance(factor.constant_result, int) and
4540
isinstance(sequence_node.mult_factor.constant_result, int)):
4541
value = sequence_node.mult_factor.constant_result * factor.constant_result
4542
sequence_node.mult_factor = ExprNodes.IntNode(
4543
sequence_node.mult_factor.pos,
4544
value=str(value), constant_result=value)
4547
return self.visit_BinopNode(node)
4549
sequence_node.mult_factor = factor
4550
return sequence_node
4552
def visit_ModNode(self, node):
4553
self.visitchildren(node)
4554
if isinstance(node.operand1, ExprNodes.UnicodeNode) and isinstance(node.operand2, ExprNodes.TupleNode):
4555
if not node.operand2.mult_factor:
4556
fstring = self._build_fstring(node.operand1.pos, node.operand1.value, node.operand2.args)
4557
if fstring is not None:
4559
return self.visit_BinopNode(node)
4561
_parse_string_format_regex = (
4568
def _build_fstring(self, pos, ustring, format_args):
4570
args = iter(format_args)
4572
can_be_optimised = True
4573
for s in re.split(self._parse_string_format_regex, ustring):
4577
substrings.append(ExprNodes.UnicodeNode(pos, value=EncodedString('%')))
4581
warning(pos, f"Incomplete format: '...{s[-3:]}'", level=1)
4582
can_be_optimised = False
4583
substrings.append(ExprNodes.UnicodeNode(pos, value=EncodedString(s)))
4588
except StopIteration:
4589
warning(pos, "Too few arguments for format placeholders", level=1)
4590
can_be_optimised = False
4593
can_be_optimised = False
4595
if format_type in 'asrfdoxX':
4597
conversion_char = None
4598
if format_type in 'doxX' and '.' in format_spec:
4600
can_be_optimised = False
4601
elif format_type in 'ars':
4602
format_spec = format_spec[:-1]
4603
conversion_char = format_type
4604
if format_spec.startswith('0'):
4605
format_spec = '>' + format_spec[1:]
4606
elif format_type == 'd':
4608
conversion_char = 'd'
4610
if format_spec.startswith('-'):
4611
format_spec = '<' + format_spec[1:]
4613
substrings.append(ExprNodes.FormattedValueNode(
4615
conversion_char=conversion_char,
4616
format_spec=ExprNodes.UnicodeNode(pos, value=EncodedString(format_spec))
4617
if format_spec else None,
4621
can_be_optimised = False
4624
if not can_be_optimised:
4630
except StopIteration: pass
4632
warning(pos, "Too many arguments for format placeholders", level=1)
4635
node = ExprNodes.JoinedStrNode(pos, values=substrings)
4636
return self.visit_JoinedStrNode(node)
4638
def visit_FormattedValueNode(self, node):
4639
self.visitchildren(node)
4640
conversion_char = node.conversion_char or 's'
4641
if node.format_spec is not None and node.format_spec.is_string_literal and not node.format_spec.value:
4642
node.format_spec = None
4643
if node.format_spec is None and isinstance(node.value, ExprNodes.IntNode):
4644
value = EncodedString(node.value.value)
4646
return ExprNodes.UnicodeNode(node.value.pos, value=value)
4647
if node.format_spec is None and conversion_char == 's':
4648
if node.value.is_string_literal:
4652
def visit_JoinedStrNode(self, node):
4654
Clean up after the parser by discarding empty Unicode strings and merging
4655
substring sequences. Empty or single-value join lists are not uncommon
4656
because f-string format specs are always parsed into JoinedStrNodes.
4658
self.visitchildren(node)
4661
for is_unode_group, substrings in itertools.groupby(node.values, key=attrgetter('is_string_literal')):
4663
substrings = list(substrings)
4664
unode = substrings[0]
4665
if len(substrings) > 1:
4666
value = EncodedString(''.join(value.value for value in substrings))
4667
unode = ExprNodes.UnicodeNode(unode.pos, value=value)
4670
values.append(unode)
4672
values.extend(substrings)
4675
node = ExprNodes.UnicodeNode(node.pos, value=EncodedString(''))
4676
elif len(values) == 1:
4678
elif len(values) == 2:
4680
node = ExprNodes.binop_node(node.pos, '+', *values)
4682
node.values = values
4685
def visit_MergedDictNode(self, node):
4686
"""Unpack **args in place if we can."""
4687
self.visitchildren(node)
4691
def add(parent, arg):
4692
if arg.is_dict_literal:
4693
if items and items[-1].reject_duplicates == arg.reject_duplicates:
4694
items[-1].key_value_pairs.extend(arg.key_value_pairs)
4697
elif isinstance(arg, ExprNodes.MergedDictNode) and parent.reject_duplicates == arg.reject_duplicates:
4698
for child_arg in arg.keyword_args:
4706
for arg in node.keyword_args:
4713
if arg.is_dict_literal or isinstance(arg, ExprNodes.MergedDictNode):
4715
node.keyword_args[:] = args
4716
self._calculate_const(node)
4719
def visit_MergedSequenceNode(self, node):
4720
"""Unpack *args in place if we can."""
4721
self.visitchildren(node)
4723
is_set = node.type is Builtin.set_type
4728
if (is_set and arg.is_set_literal) or (arg.is_sequence_constructor and not arg.mult_factor):
4730
values[0].args.extend(arg.args)
4733
elif isinstance(arg, ExprNodes.MergedSequenceNode):
4734
for child_arg in arg.args:
4738
args.append(values[0])
4742
for arg in node.args:
4745
args.append(values[0])
4749
if ((is_set and arg.is_set_literal) or
4750
(arg.is_sequence_constructor and arg.type is node.type) or
4751
isinstance(arg, ExprNodes.MergedSequenceNode)):
4754
self._calculate_const(node)
4757
def visit_SequenceNode(self, node):
4758
"""Unpack *args in place if we can."""
4759
self.visitchildren(node)
4761
for arg in node.args:
4762
if not arg.is_starred:
4764
elif arg.target.is_sequence_constructor and not arg.target.mult_factor:
4765
args.extend(arg.target.args)
4769
self._calculate_const(node)
4772
def visit_PrimaryCmpNode(self, node):
4774
self.visitchildren(node, ['operand1'])
4775
left_node = node.operand1
4777
while cmp_node is not None:
4778
self.visitchildren(cmp_node, ['operand2'])
4779
right_node = cmp_node.operand2
4780
cmp_node.constant_result = not_a_constant
4781
if left_node.has_constant_result() and right_node.has_constant_result():
4783
cmp_node.calculate_cascaded_constant_result(left_node.constant_result)
4784
except (ValueError, TypeError, KeyError, IndexError, AttributeError, ArithmeticError):
4786
left_node = right_node
4787
cmp_node = cmp_node.cascade
4789
if not node.cascade:
4790
if node.has_constant_result():
4791
return self._bool_node(node, node.constant_result)
4795
cascades = [[node.operand1]]
4796
final_false_result = []
4799
while cmp_node is not None:
4800
if cmp_node.has_constant_result():
4801
if not cmp_node.constant_result:
4803
final_false_result.append(self._bool_node(cmp_node, False))
4807
cascades.append([cmp_node.operand2])
4810
cascades[-1].append(cmp_node)
4811
cmp_node = cmp_node.cascade
4814
for cascade in cascades:
4815
if len(cascade) < 2:
4817
cmp_node = cascade[1]
4818
pcmp_node = ExprNodes.PrimaryCmpNode(
4820
operand1=cascade[0],
4821
operator=cmp_node.operator,
4822
operand2=cmp_node.operand2,
4823
constant_result=not_a_constant)
4824
cmp_nodes.append(pcmp_node)
4826
last_cmp_node = pcmp_node
4827
for cmp_node in cascade[2:]:
4828
last_cmp_node.cascade = cmp_node
4829
last_cmp_node = cmp_node
4830
last_cmp_node.cascade = None
4832
if final_false_result:
4834
cmp_nodes.append(final_false_result[0])
4837
return self._bool_node(node, True)
4839
if len(cmp_nodes) == 1:
4840
if node.has_constant_result():
4841
return self._bool_node(node, node.constant_result)
4843
for cmp_node in cmp_nodes[1:]:
4844
node = ExprNodes.BoolBinopNode(
4849
constant_result=not_a_constant)
4852
def visit_CondExprNode(self, node):
4853
self._calculate_const(node)
4854
if not node.test.has_constant_result():
4856
if node.test.constant_result:
4857
return node.true_val
4859
return node.false_val
4861
def visit_IfStatNode(self, node):
4862
self.visitchildren(node)
4865
for if_clause in node.if_clauses:
4866
condition = if_clause.condition
4867
if condition.has_constant_result():
4868
if condition.constant_result:
4870
node.else_clause = if_clause.body
4875
if_clauses.append(if_clause)
4877
node.if_clauses = if_clauses
4879
elif node.else_clause:
4880
return node.else_clause
4882
return Nodes.StatListNode(node.pos, stats=[])
4884
def visit_SliceIndexNode(self, node):
4885
self._calculate_const(node)
4887
if node.start is None or node.start.constant_result is None:
4888
start = node.start = None
4890
start = node.start.constant_result
4891
if node.stop is None or node.stop.constant_result is None:
4892
stop = node.stop = None
4894
stop = node.stop.constant_result
4896
if node.constant_result is not not_a_constant:
4898
if base.is_sequence_constructor and base.mult_factor is None:
4899
base.args = base.args[start:stop]
4901
elif base.is_string_literal:
4902
base = base.as_sliced_node(start, stop)
4903
if base is not None:
4907
def visit_ComprehensionNode(self, node):
4908
self.visitchildren(node)
4909
if isinstance(node.loop, Nodes.StatListNode) and not node.loop.stats:
4911
if node.type is Builtin.list_type:
4912
return ExprNodes.ListNode(
4913
node.pos, args=[], constant_result=[])
4914
elif node.type is Builtin.set_type:
4915
return ExprNodes.SetNode(
4916
node.pos, args=[], constant_result=set())
4917
elif node.type is Builtin.dict_type:
4918
return ExprNodes.DictNode(
4919
node.pos, key_value_pairs=[], constant_result={})
4922
def visit_ForInStatNode(self, node):
4923
self.visitchildren(node)
4924
sequence = node.iterator.sequence
4925
if isinstance(sequence, ExprNodes.SequenceNode):
4926
if not sequence.args:
4927
if node.else_clause:
4928
return node.else_clause
4931
return Nodes.StatListNode(node.pos, stats=[])
4933
if isinstance(sequence, ExprNodes.ListNode):
4934
node.iterator.sequence = sequence.as_tuple()
4937
def visit_WhileStatNode(self, node):
4938
self.visitchildren(node)
4939
if node.condition and node.condition.has_constant_result():
4940
if node.condition.constant_result:
4941
node.condition = None
4942
node.else_clause = None
4944
return node.else_clause
4947
def visit_ExprStatNode(self, node):
4948
self.visitchildren(node)
4949
if not isinstance(node.expr, ExprNodes.ExprNode):
4953
if node.expr.has_constant_result():
4957
def visit_GILStatNode(self, node):
4958
self.visitchildren(node)
4959
if node.condition is None:
4962
if node.condition.has_constant_result():
4965
if node.condition.constant_result:
4966
node.condition = None
4984
visit_Node = Visitor.VisitorTransform.recurse_to_children
4987
class FinalOptimizePhase(Visitor.EnvTransform, Visitor.NodeRefCleanupMixin):
4989
This visitor handles several commuting optimizations, and is run
4990
just before the C code generation phase.
4992
The optimizations currently implemented in this class are:
4993
- eliminate None assignment and refcounting for first assignment.
4994
- isinstance -> typecheck for cdef types
4995
- eliminate checks for None and/or types that became redundant after tree changes
4996
- eliminate useless string formatting steps
4997
- inject branch hints for unlikely if-cases that only raise exceptions
4998
- replace Python function calls that look like method calls by a faster PyMethodCallNode
5002
def visit_SingleAssignmentNode(self, node):
5003
"""Avoid redundant initialisation of local variables before their
5006
self.visitchildren(node)
5009
lhs.lhs_of_first_assignment = True
5012
def _check_optimize_method_calls(self, node):
5013
function = node.function
5014
env = self.current_env()
5016
env.is_module_scope or
5017
env.is_c_class_scope or
5018
(env.is_py_class_scope and env.outer_scope.is_module_scope)
5020
return (node.is_temp and function.type.is_pyobject and self.current_directives.get(
5021
"optimize.unpack_method_calls_in_pyinit"
5022
if not self.in_loop and in_global_scope
5023
else "optimize.unpack_method_calls"))
5025
def visit_SimpleCallNode(self, node):
5027
Replace generic calls to isinstance(x, type) by a more efficient type check.
5028
Replace likely Python method calls by a specialised PyMethodCallNode.
5030
self.visitchildren(node)
5031
function = node.function
5032
if function.type.is_cfunction and function.is_name:
5033
if function.name == 'isinstance' and len(node.args) == 2:
5034
type_arg = node.args[1]
5035
if type_arg.type.is_builtin_type and type_arg.type.name == 'type':
5036
cython_scope = self.context.cython_scope
5037
function.entry = cython_scope.lookup('PyObject_TypeCheck')
5038
function.type = function.entry.type
5039
PyTypeObjectPtr = PyrexTypes.CPtrType(cython_scope.lookup('PyTypeObject').type)
5040
node.args[1] = ExprNodes.CastNode(node.args[1], PyTypeObjectPtr)
5043
if ExprNodes.PyMethodCallNode.can_be_used_for_posargs(node.arg_tuple, has_kwargs=False):
5045
if ExprNodes.PyMethodCallNode.can_be_used_for_function(function):
5046
if (node.self and function.is_attribute and
5047
isinstance(function.obj, ExprNodes.CloneNode) and function.obj.arg is node.self):
5049
function.obj = function.obj.arg
5050
node = self.replace(node, ExprNodes.PyMethodCallNode.from_node(
5051
node, function=function, arg_tuple=node.arg_tuple, type=node.type,
5052
unpack=self._check_optimize_method_calls(node)))
5055
def visit_GeneralCallNode(self, node):
5057
Replace likely Python method calls by a specialised PyMethodCallNode.
5059
self.visitchildren(node)
5060
has_kwargs = bool(node.keyword_args)
5061
kwds_is_dict_node = isinstance(node.keyword_args, ExprNodes.DictNode)
5062
if not ExprNodes.PyMethodCallNode.can_be_used_for_posargs(
5063
node.positional_args, has_kwargs=has_kwargs, kwds_is_dict_node=kwds_is_dict_node):
5065
function = node.function
5066
if not ExprNodes.PyMethodCallNode.can_be_used_for_function(function):
5069
node = self.replace(node, ExprNodes.PyMethodCallNode.from_node(
5070
node, function=function, arg_tuple=node.positional_args, kwdict=node.keyword_args,
5071
type=node.type, unpack=self._check_optimize_method_calls(node)))
5074
def visit_NumPyMethodCallNode(self, node):
5076
self.visitchildren(node)
5079
def visit_PyTypeTestNode(self, node):
5080
"""Remove tests for alternatively allowed None values from
5081
type tests when we know that the argument cannot be None
5084
self.visitchildren(node)
5085
if not node.notnone:
5086
if not node.arg.may_be_none():
5090
def visit_NoneCheckNode(self, node):
5091
"""Remove None checks from expressions that definitely do not
5094
self.visitchildren(node)
5095
if not node.arg.may_be_none():
5099
def visit_LoopNode(self, node):
5100
"""Remember when we enter a loop as some expensive optimisations might still be worth it there.
5102
old_val = self.in_loop
5104
self.visitchildren(node)
5105
self.in_loop = old_val
5108
def visit_IfStatNode(self, node):
5109
"""Assign 'unlikely' branch hints to if-clauses that only raise exceptions.
5111
self.visitchildren(node)
5112
last_non_unlikely_clause = None
5113
for i, if_clause in enumerate(node.if_clauses):
5114
self._set_ifclause_branch_hint(if_clause, if_clause.body)
5115
if not if_clause.branch_hint:
5116
last_non_unlikely_clause = if_clause
5117
if node.else_clause and last_non_unlikely_clause:
5119
self._set_ifclause_branch_hint(last_non_unlikely_clause, node.else_clause, inverse=True)
5122
def _set_ifclause_branch_hint(self, clause, statements_node, inverse=False):
5123
"""Inject a branch hint if the if-clause unconditionally leads to a 'raise' statement.
5125
if not statements_node.is_terminator:
5128
non_branch_nodes = (
5130
Nodes.AssignmentNode,
5131
Nodes.AssertStatNode,
5136
statements = [statements_node]
5137
for next_node_pos, node in enumerate(statements, 1):
5138
if isinstance(node, Nodes.GILStatNode):
5139
statements.insert(next_node_pos, node.body)
5141
if isinstance(node, Nodes.StatListNode):
5142
statements[next_node_pos:next_node_pos] = node.stats
5144
if not isinstance(node, non_branch_nodes):
5145
if next_node_pos == len(statements) and isinstance(node, (Nodes.RaiseStatNode, Nodes.ReraiseStatNode)):
5147
clause.branch_hint = 'likely' if inverse else 'unlikely'
5151
class ConsolidateOverflowCheck(Visitor.CythonTransform):
5153
This class facilitates the sharing of overflow checking among all nodes
5154
of a nested arithmetic expression. For example, given the expression
5155
a*b + c, where a, b, and x are all possibly overflowing ints, the entire
5156
sequence will be evaluated and the overflow bit checked only at the end.
5158
overflow_bit_node = None
5160
def visit_Node(self, node):
5161
if self.overflow_bit_node is not None:
5162
saved = self.overflow_bit_node
5163
self.overflow_bit_node = None
5164
self.visitchildren(node)
5165
self.overflow_bit_node = saved
5167
self.visitchildren(node)
5170
def visit_NumBinopNode(self, node):
5171
if node.overflow_check and node.overflow_fold:
5172
top_level_overflow = self.overflow_bit_node is None
5173
if top_level_overflow:
5174
self.overflow_bit_node = node
5176
node.overflow_bit_node = self.overflow_bit_node
5177
node.overflow_check = False
5178
self.visitchildren(node)
5179
if top_level_overflow:
5180
self.overflow_bit_node = None
5182
self.visitchildren(node)