cython

Форк
0
3222 строки · 117.2 Кб
1
#
2
#   Code output module
3
#
4

5

6
import cython
7
cython.declare(os=object, re=object, operator=object, textwrap=object,
8
               Template=object, Naming=object, Options=object, StringEncoding=object,
9
               Utils=object, SourceDescriptor=object, StringIOTree=object,
10
               DebugFlags=object, defaultdict=object,
11
               closing=object, partial=object, wraps=object)
12

13
import hashlib
14
import operator
15
import os
16
import re
17
import shutil
18
import textwrap
19
from string import Template
20
from functools import partial, wraps
21
from contextlib import closing, contextmanager
22
from collections import defaultdict
23

24
from . import Naming
25
from . import Options
26
from . import DebugFlags
27
from . import StringEncoding
28
from .. import Utils
29
from .Scanning import SourceDescriptor
30
from ..StringIOTree import StringIOTree
31

32

33
renamed_py2_builtins_map = {
34
    # builtins that had different names in Py2 code
35
    'unicode'    : 'str',
36
    'basestring' : 'str',
37
    'xrange'     : 'range',
38
    'raw_input'  : 'input',
39
}
40

41
ctypedef_builtins_map = {
42
    # types of builtins in "ctypedef class" statements which we don't
43
    # import either because the names conflict with C types or because
44
    # the type simply is not exposed.
45
    'py_int'             : '&PyLong_Type',
46
    'py_long'            : '&PyLong_Type',
47
    'py_float'           : '&PyFloat_Type',
48
    'wrapper_descriptor' : '&PyWrapperDescr_Type',
49
}
50

51
basicsize_builtins_map = {
52
    # builtins whose type has a different tp_basicsize than sizeof(...)
53
    'PyTypeObject': 'PyHeapTypeObject',
54
}
55

56
# Builtins as of Python version ...
57
KNOWN_PYTHON_BUILTINS_VERSION = (3, 13, 0, 'alpha', 5)
58
KNOWN_PYTHON_BUILTINS = frozenset([
59
    'ArithmeticError',
60
    'AssertionError',
61
    'AttributeError',
62
    'BaseException',
63
    'BaseExceptionGroup',
64
    'BlockingIOError',
65
    'BrokenPipeError',
66
    'BufferError',
67
    'BytesWarning',
68
    'ChildProcessError',
69
    'ConnectionAbortedError',
70
    'ConnectionError',
71
    'ConnectionRefusedError',
72
    'ConnectionResetError',
73
    'DeprecationWarning',
74
    'EOFError',
75
    'Ellipsis',
76
    'EncodingWarning',
77
    'EnvironmentError',
78
    'Exception',
79
    'ExceptionGroup',
80
    'False',
81
    'FileExistsError',
82
    'FileNotFoundError',
83
    'FloatingPointError',
84
    'FutureWarning',
85
    'GeneratorExit',
86
    'IOError',
87
    'ImportError',
88
    'ImportWarning',
89
    '_IncompleteInputError',
90
    'IndentationError',
91
    'IndexError',
92
    'InterruptedError',
93
    'IsADirectoryError',
94
    'KeyError',
95
    'KeyboardInterrupt',
96
    'LookupError',
97
    'MemoryError',
98
    'ModuleNotFoundError',
99
    'NameError',
100
    'None',
101
    'NotADirectoryError',
102
    'NotImplemented',
103
    'NotImplementedError',
104
    'OSError',
105
    'OverflowError',
106
    'PendingDeprecationWarning',
107
    'PermissionError',
108
    'ProcessLookupError',
109
    'PythonFinalizationError',
110
    'RecursionError',
111
    'ReferenceError',
112
    'ResourceWarning',
113
    'RuntimeError',
114
    'RuntimeWarning',
115
    'StopAsyncIteration',
116
    'StopIteration',
117
    'SyntaxError',
118
    'SyntaxWarning',
119
    'SystemError',
120
    'SystemExit',
121
    'TabError',
122
    'TimeoutError',
123
    'True',
124
    'TypeError',
125
    'UnboundLocalError',
126
    'UnicodeDecodeError',
127
    'UnicodeEncodeError',
128
    'UnicodeError',
129
    'UnicodeTranslateError',
130
    'UnicodeWarning',
131
    'UserWarning',
132
    'ValueError',
133
    'Warning',
134
    'WindowsError',
135
    'ZeroDivisionError',
136
    '__build_class__',
137
    '__debug__',
138
    '__import__',
139
    'abs',
140
    'aiter',
141
    'all',
142
    'anext',
143
    'any',
144
    'ascii',
145
    'bin',
146
    'bool',
147
    'breakpoint',
148
    'bytearray',
149
    'bytes',
150
    'callable',
151
    'chr',
152
    'classmethod',
153
    'compile',
154
    'complex',
155
    'copyright',
156
    'credits',
157
    'delattr',
158
    'dict',
159
    'dir',
160
    'divmod',
161
    'enumerate',
162
    'eval',
163
    'exec',
164
    'exit',
165
    'filter',
166
    'float',
167
    'format',
168
    'frozenset',
169
    'getattr',
170
    'globals',
171
    'hasattr',
172
    'hash',
173
    'help',
174
    'hex',
175
    'id',
176
    'input',
177
    'int',
178
    'isinstance',
179
    'issubclass',
180
    'iter',
181
    'len',
182
    'license',
183
    'list',
184
    'locals',
185
    'map',
186
    'max',
187
    'memoryview',
188
    'min',
189
    'next',
190
    'object',
191
    'oct',
192
    'open',
193
    'ord',
194
    'pow',
195
    'print',
196
    'property',
197
    'quit',
198
    'range',
199
    'repr',
200
    'reversed',
201
    'round',
202
    'set',
203
    'setattr',
204
    'slice',
205
    'sorted',
206
    'staticmethod',
207
    'str',
208
    'sum',
209
    'super',
210
    'tuple',
211
    'type',
212
    'vars',
213
    'zip',
214
])
215

216
uncachable_builtins = [
217
    # Global/builtin names that cannot be cached because they may or may not
218
    # be available at import time, for various reasons:
219
    ## Python 3.13+
220
    '_IncompleteInputError',
221
    'PythonFinalizationError',
222
    ## Python 3.11+
223
    'BaseExceptionGroup',
224
    'ExceptionGroup',
225
    ## - Py3.10+
226
    'aiter',
227
    'anext',
228
    'EncodingWarning',
229
    ## - Py3.7+
230
    'breakpoint',  # might deserve an implementation in Cython
231
    ## - platform specific
232
    'WindowsError',
233
    ## - others
234
    '_',  # e.g. used by gettext
235
]
236

237
special_py_methods = cython.declare(frozenset, frozenset((
238
    '__cinit__', '__dealloc__', '__richcmp__', '__next__',
239
    '__await__', '__aiter__', '__anext__',
240
    '__getbuffer__', '__releasebuffer__',
241
)))
242

243
modifier_output_mapper = {
244
    'inline': 'CYTHON_INLINE'
245
}.get
246

247
cleanup_level_for_type_prefix = cython.declare(object, {
248
    'ustring': None,
249
    'tuple': 2,
250
    'slice': 2,
251
}.get)
252

253

254
class IncludeCode:
255
    """
256
    An include file and/or verbatim C code to be included in the
257
    generated sources.
258
    """
259
    # attributes:
260
    #
261
    #  pieces    {order: unicode}: pieces of C code to be generated.
262
    #            For the included file, the key "order" is zero.
263
    #            For verbatim include code, the "order" is the "order"
264
    #            attribute of the original IncludeCode where this piece
265
    #            of C code was first added. This is needed to prevent
266
    #            duplication if the same include code is found through
267
    #            multiple cimports.
268
    #  location  int: where to put this include in the C sources, one
269
    #            of the constants INITIAL, EARLY, LATE
270
    #  order     int: sorting order (automatically set by increasing counter)
271

272
    # Constants for location. If the same include occurs with different
273
    # locations, the earliest one takes precedense.
274
    INITIAL = 0
275
    EARLY = 1
276
    LATE = 2
277

278
    counter = 1   # Counter for "order"
279

280
    def __init__(self, include=None, verbatim=None, late=True, initial=False):
281
        self.order = self.counter
282
        type(self).counter += 1
283
        self.pieces = {}
284

285
        if include:
286
            if include[0] == '<' and include[-1] == '>':
287
                self.pieces[0] = '#include {}'.format(include)
288
                late = False  # system include is never late
289
            else:
290
                self.pieces[0] = '#include "{}"'.format(include)
291

292
        if verbatim:
293
            self.pieces[self.order] = verbatim
294

295
        if initial:
296
            self.location = self.INITIAL
297
        elif late:
298
            self.location = self.LATE
299
        else:
300
            self.location = self.EARLY
301

302
    def dict_update(self, d, key):
303
        """
304
        Insert `self` in dict `d` with key `key`. If that key already
305
        exists, update the attributes of the existing value with `self`.
306
        """
307
        if key in d:
308
            other = d[key]
309
            other.location = min(self.location, other.location)
310
            other.pieces.update(self.pieces)
311
        else:
312
            d[key] = self
313

314
    def sortkey(self):
315
        return self.order
316

317
    def mainpiece(self):
318
        """
319
        Return the main piece of C code, corresponding to the include
320
        file. If there was no include file, return None.
321
        """
322
        return self.pieces.get(0)
323

324
    def write(self, code):
325
        # Write values of self.pieces dict, sorted by the keys
326
        for k in sorted(self.pieces):
327
            code.putln(self.pieces[k])
328

329

330
def get_utility_dir():
331
    # make this a function and not global variables:
332
    # http://trac.cython.org/cython_trac/ticket/475
333
    Cython_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
334
    return os.path.join(Cython_dir, "Utility")
335

336
read_utilities_hook = None
337
"""
338
Override the hook for reading a utilities file that contains code fragments used
339
by the codegen.
340

341
The hook functions takes the path of the utilities file, and returns a list
342
of strings, one per line.
343

344
The default behavior is to open a file relative to get_utility_dir().
345
"""
346

347
def read_utilities_from_utility_dir(path):
348
    """
349
    Read all lines of the file at the provided path from a path relative
350
    to get_utility_dir().
351
    """
352
    filename = os.path.join(get_utility_dir(), path)
353
    with closing(Utils.open_source_file(filename, encoding='UTF-8')) as f:
354
        return f.readlines()
355

356
# by default, read utilities from the utility directory.
357
read_utilities_hook = read_utilities_from_utility_dir
358

359
class UtilityCodeBase:
360
    """
361
    Support for loading utility code from a file.
362

363
    Code sections in the file can be specified as follows:
364

365
        ##### MyUtility.proto #####
366

367
        [proto declarations]
368

369
        ##### MyUtility.init #####
370

371
        [code run at module initialization]
372

373
        ##### MyUtility #####
374
        #@requires: MyOtherUtility
375
        #@substitute: naming
376

377
        [definitions]
378

379
        ##### MyUtility #####
380
        #@substitute: tempita
381

382
        [requires tempita substitution
383
         - context can't be specified here though so only
384
           tempita utility that requires no external context
385
           will benefit from this tag
386
         - only necessary when @required from non-tempita code]
387

388
    for prototypes and implementation respectively.  For non-python or
389
    -cython files backslashes should be used instead.  5 to 30 comment
390
    characters may be used on either side.
391

392
    If the @cname decorator is not used and this is a CythonUtilityCode,
393
    one should pass in the 'name' keyword argument to be used for name
394
    mangling of such entries.
395
    """
396

397
    is_cython_utility = False
398
    _utility_cache = {}
399

400
    @classmethod
401
    def _add_utility(cls, utility, name, type, lines, begin_lineno, tags=None):
402
        if utility is None:
403
            return
404

405
        code = '\n'.join(lines)
406
        if tags and 'substitute' in tags and 'naming' in tags['substitute']:
407
            try:
408
                new_code = Template(code).substitute(vars(Naming))
409
            except (KeyError, ValueError) as e:
410
                raise RuntimeError(
411
                    f"Error parsing templated utility code '{name}.{type}' at line {begin_lineno:d}: {e}")
412
            if new_code == code:
413
                raise RuntimeError(
414
                    f"Found useless 'substitute: naming' declaration without replacements. ({name}.{type}:{begin_lineno:d})")
415
            code = new_code
416

417
        # remember correct line numbers at least until after templating
418
        code = '\n' * begin_lineno + code
419

420
        if type == 'proto':
421
            utility[0] = code
422
        elif type == 'impl':
423
            utility[1] = code
424
        else:
425
            all_tags = utility[2]
426
            all_tags[type] = code
427

428
        if tags:
429
            all_tags = utility[2]
430
            for name, values in tags.items():
431
                all_tags.setdefault(name, set()).update(values)
432

433
    @classmethod
434
    def load_utilities_from_file(cls, path):
435
        utilities = cls._utility_cache.get(path)
436
        if utilities:
437
            return utilities
438

439
        _, ext = os.path.splitext(path)
440
        if ext in ('.pyx', '.py', '.pxd', '.pxi'):
441
            comment = '#'
442
            strip_comments = partial(re.compile(r'^\s*#(?!\s*cython\s*:).*').sub, '')
443
            rstrip = str.rstrip
444
        else:
445
            comment = '/'
446
            strip_comments = partial(re.compile(r'^\s*//.*|/\*[^*]*\*/').sub, '')
447
            rstrip = partial(re.compile(r'\s+(\\?)$').sub, r'\1')
448
        match_special = re.compile(
449
            (r'^%(C)s{5,30}\s*(?P<name>(?:\w|\.)+)\s*%(C)s{5,30}|'
450
             r'^%(C)s+@(?P<tag>\w+)\s*:\s*(?P<value>(?:\w|[.:])+)') %
451
            {'C': comment}).match
452
        match_type = re.compile(r'(.+)[.](proto(?:[.]\S+)?|impl|init|cleanup)$').match
453

454
        all_lines = read_utilities_hook(path)
455

456
        utilities = defaultdict(lambda: [None, None, {}])
457
        lines = []
458
        tags = defaultdict(set)
459
        utility = name = type = None
460
        begin_lineno = 0
461

462
        for lineno, line in enumerate(all_lines):
463
            m = match_special(line)
464
            if m:
465
                if m.group('name'):
466
                    cls._add_utility(utility, name, type, lines, begin_lineno, tags)
467

468
                    begin_lineno = lineno + 1
469
                    del lines[:]
470
                    tags.clear()
471

472
                    name = m.group('name')
473
                    mtype = match_type(name)
474
                    if mtype:
475
                        name, type = mtype.groups()
476
                    else:
477
                        type = 'impl'
478
                    utility = utilities[name]
479
                else:
480
                    tags[m.group('tag')].add(m.group('value'))
481
                    lines.append('')  # keep line number correct
482
            else:
483
                lines.append(rstrip(strip_comments(line)))
484

485
        if utility is None:
486
            raise ValueError("Empty utility code file")
487

488
        # Don't forget to add the last utility code
489
        cls._add_utility(utility, name, type, lines, begin_lineno, tags)
490

491
        utilities = dict(utilities)  # un-defaultdict-ify
492
        cls._utility_cache[path] = utilities
493
        return utilities
494

495
    @classmethod
496
    def load(cls, util_code_name, from_file, **kwargs):
497
        """
498
        Load utility code from a file specified by from_file (relative to
499
        Cython/Utility) and name util_code_name.
500
        """
501

502
        if '::' in util_code_name:
503
            from_file, util_code_name = util_code_name.rsplit('::', 1)
504
        assert from_file
505
        utilities = cls.load_utilities_from_file(from_file)
506
        proto, impl, tags = utilities[util_code_name]
507

508
        if tags:
509
            if "substitute" in tags and "tempita" in tags["substitute"]:
510
                if not issubclass(cls, TempitaUtilityCode):
511
                    return TempitaUtilityCode.load(util_code_name, from_file, **kwargs)
512
            orig_kwargs = kwargs.copy()
513
            for name, values in tags.items():
514
                if name in kwargs:
515
                    continue
516
                # only pass lists when we have to: most argument expect one value or None
517
                if name == 'requires':
518
                    if orig_kwargs:
519
                        values = [cls.load(dep, from_file, **orig_kwargs)
520
                                  for dep in sorted(values)]
521
                    else:
522
                        # dependencies are rarely unique, so use load_cached() when we can
523
                        values = [cls.load_cached(dep, from_file)
524
                                  for dep in sorted(values)]
525
                elif name == 'substitute':
526
                    # don't want to pass "naming" or "tempita" to the constructor
527
                    # since these will have been handled
528
                    values = values - {'naming', 'tempita'}
529
                    if not values:
530
                        continue
531
                elif not values:
532
                    values = None
533
                elif len(values) == 1:
534
                    values = list(values)[0]
535
                kwargs[name] = values
536

537
        if proto is not None:
538
            kwargs['proto'] = proto
539
        if impl is not None:
540
            kwargs['impl'] = impl
541

542
        if 'name' not in kwargs:
543
            kwargs['name'] = util_code_name
544

545
        if 'file' not in kwargs and from_file:
546
            kwargs['file'] = from_file
547
        return cls(**kwargs)
548

549
    @classmethod
550
    def load_cached(cls, utility_code_name, from_file, __cache={}):
551
        """
552
        Calls .load(), but using a per-type cache based on utility name and file name.
553
        """
554
        key = (utility_code_name, from_file, cls)
555
        try:
556
            return __cache[key]
557
        except KeyError:
558
            pass
559
        code = __cache[key] = cls.load(utility_code_name, from_file)
560
        return code
561

562
    @classmethod
563
    def load_as_string(cls, util_code_name, from_file, include_requires=False, **kwargs):
564
        """
565
        Load a utility code as a string. Returns (proto, implementation).
566

567
        If 'include_requires=True', concatenates all requirements before the actually
568
        requested utility code, separately for proto and impl part.
569
        """
570
        util = cls.load(util_code_name, from_file, **kwargs)
571

572
        if not include_requires:
573
            return util.format_code(util.proto), util.format_code(util.impl)
574

575
        protos, impls = [], []
576
        def prepend(util_code):
577
            if util_code.requires:
578
                for dep in util_code.requires:
579
                    prepend(dep)
580
            if util_code.proto:
581
                protos.append(util_code.format_code(util_code.proto))
582
            if util_code.impl:
583
                impls.append(util_code.format_code(util_code.impl))
584

585
        prepend(util)
586
        return "".join(protos), "".join(impls)
587

588
    def format_code(self, code_string, replace_empty_lines=re.compile(r'\n\n+').sub):
589
        """
590
        Format a code section for output.
591
        """
592
        if code_string:
593
            code_string = replace_empty_lines('\n', code_string.strip()) + '\n\n'
594
        return code_string
595

596
    def __repr__(self):
597
        return "<%s(%s)>" % (type(self).__name__, self.name)
598

599
    def get_tree(self, **kwargs):
600
        return None
601

602
    def __deepcopy__(self, memodict=None):
603
        # No need to deep-copy utility code since it's essentially immutable.
604
        return self
605

606

607
class UtilityCode(UtilityCodeBase):
608
    """
609
    Stores utility code to add during code generation.
610

611
    See GlobalState.put_utility_code.
612

613
    hashes/equals by instance
614

615
    proto           C prototypes
616
    impl            implementation code
617
    init            code to call on module initialization
618
    requires        utility code dependencies
619
    proto_block     the place in the resulting file where the prototype should
620
                    end up
621
    name            name of the utility code (or None)
622
    file            filename of the utility code file this utility was loaded
623
                    from (or None)
624
    """
625

626
    def __init__(self, proto=None, impl=None, init=None, cleanup=None, requires=None,
627
                 proto_block='utility_code_proto', name=None, file=None):
628
        # proto_block: Which code block to dump prototype in. See GlobalState.
629
        self.proto = proto
630
        self.impl = impl
631
        self.init = init
632
        self.cleanup = cleanup
633
        self.requires = requires
634
        self._cache = {}
635
        self.specialize_list = []
636
        self.proto_block = proto_block
637
        self.name = name
638
        self.file = file
639

640
    def __hash__(self):
641
        return hash((self.proto, self.impl))
642

643
    def __eq__(self, other):
644
        if self is other:
645
            return True
646
        self_type, other_type = type(self), type(other)
647
        if self_type is not other_type and not (isinstance(other, self_type) or isinstance(self, other_type)):
648
            return False
649

650
        self_init = getattr(self, 'init', None)
651
        other_init = getattr(other, 'init', None)
652
        self_proto = getattr(self, 'proto', None)
653
        other_proto = getattr(other, 'proto', None)
654
        return (self_init, self_proto, self.impl) == (other_init, other_proto, other.impl)
655

656
    def none_or_sub(self, s, context):
657
        """
658
        Format a string in this utility code with context. If None, do nothing.
659
        """
660
        if s is None:
661
            return None
662
        return s % context
663

664
    def specialize(self, pyrex_type=None, **data):
665
        name = self.name
666
        if pyrex_type is not None:
667
            data['type'] = pyrex_type.empty_declaration_code()
668
            data['type_name'] = pyrex_type.specialization_name()
669
            name = "%s[%s]" % (name, data['type_name'])
670
        # Dicts aren't hashable...
671
        key = tuple(sorted(data.items()))
672
        try:
673
            return self._cache[key]
674
        except KeyError:
675
            if self.requires is None:
676
                requires = None
677
            else:
678
                requires = [r.specialize(data) for r in self.requires]
679

680
            s = self._cache[key] = UtilityCode(
681
                self.none_or_sub(self.proto, data),
682
                self.none_or_sub(self.impl, data),
683
                self.none_or_sub(self.init, data),
684
                self.none_or_sub(self.cleanup, data),
685
                requires,
686
                self.proto_block,
687
                name,
688
            )
689

690
            self.specialize_list.append(s)
691
            return s
692

693
    def put_code(self, output):
694
        if self.requires:
695
            for dependency in self.requires:
696
                output.use_utility_code(dependency)
697
        if self.proto:
698
            writer = output[self.proto_block]
699
            writer.putln(f"/* {self.name}.proto */")
700
            proto, result_is_module_specific = process_utility_ccode(self, output, self.proto)
701
            if result_is_module_specific:
702
                writer.put(proto)
703
            else:
704
                # can be reused across modules
705
                writer.put_or_include(proto, f'{self.name}_proto')
706
        if self.impl:
707
            impl, result_is_module_specific = process_utility_ccode(self, output, self.impl)
708
            writer = output['utility_code_def']
709
            writer.putln(f"/* {self.name} */")
710
            if result_is_module_specific:
711
                writer.put(impl)
712
            else:
713
                # can be reused across modules
714
                writer.put_or_include(impl, f'{self.name}_impl')
715
        if self.init:
716
            writer = output['init_globals']
717
            writer.putln(f"/* {self.name}.init */")
718
            if isinstance(self.init, str):
719
                writer.put(self.format_code(self.init))
720
            else:
721
                self.init(writer, output.module_pos)
722
            # 'init' code can end with an 'if' statement for an error condition like:
723
            # if (check_ok()) ; else
724
            writer.putln(writer.error_goto_if_PyErr(output.module_pos))
725
            writer.putln()
726
        if self.cleanup and Options.generate_cleanup_code:
727
            writer = output['cleanup_globals']
728
            writer.putln(f"/* {self.name}.cleanup */")
729
            if isinstance(self.cleanup, str):
730
                writer.put_or_include(
731
                    self.format_code(self.cleanup),
732
                    f'{self.name}_cleanup')
733
            else:
734
                self.cleanup(writer, output.module_pos)
735

736

737
def add_macro_processor(*macro_names, regex=None, is_module_specific=False, _last_macro_processor = [None]):
738
    """Decorator to chain the code macro processors below.
739
    """
740
    last_processor = _last_macro_processor[0]
741

742
    def build_processor(func):
743
        @wraps(func)
744
        def process(utility_code: UtilityCode, output, code_string: str):
745
            # First, call the processing chain in FIFO function definition order.
746
            result_is_module_specific = False
747
            if last_processor is not None:
748
                code_string, result_is_module_specific = last_processor(utility_code, output, code_string)
749

750
            # Detect if we need to do something.
751
            if macro_names:
752
                for macro in macro_names:
753
                    if macro in code_string:
754
                        break
755
                else:
756
                    return code_string, result_is_module_specific
757

758
            # Process the code.
759
            if regex is None:
760
                code_string = func(utility_code, output, code_string)
761
            else:
762
                code_string = re.sub(regex, partial(func, output), code_string)
763

764
            # Make sure we found and replaced all macro occurrences.
765
            for macro in macro_names:
766
                if macro in code_string:
767
                    raise RuntimeError(f"Left-over utility code macro '{macro}()' found in '{utility_code.name}'")
768

769
            result_is_module_specific |= is_module_specific
770
            return code_string, result_is_module_specific
771

772
        _last_macro_processor[0] = process
773
        return process
774

775
    return build_processor
776

777

778
@add_macro_processor(
779
    'CSTRING',
780
    regex=r'CSTRING\(\s*"""([^"]*(?:"[^"]+)*)"""\s*\)',
781
)
782
def _wrap_c_string(_, matchobj):
783
    """Replace CSTRING('''xyz''') by a C compatible string, taking care of line breaks.
784
    """
785
    content = matchobj.group(1).replace('"', '\042')
786
    return ''.join(
787
        f'"{line}\\n"\n' if not line.endswith('\\') or line.endswith('\\\\') else f'"{line[:-1]}"\n'
788
        for line in content.splitlines())
789

790

791
@add_macro_processor()
792
def _format_impl_code(utility_code: UtilityCode, _, impl):
793
    return utility_code.format_code(impl)
794

795

796
@add_macro_processor(
797
    'CALL_UNBOUND_METHOD',
798
    is_module_specific=True,
799
    regex=(
800
        r'CALL_UNBOUND_METHOD\('
801
        r'([a-zA-Z_]+),'      # type cname
802
        r'\s*"([^"]+)",'      # method name
803
        r'\s*([^),]+)'        # object cname
804
        r'((?:,[^),]+)*)'     # args*
805
        r'\)'
806
    ),
807
)
808
def _inject_unbound_method(output, matchobj):
809
    """Replace 'UNBOUND_METHOD(type, "name")' by a constant Python identifier cname.
810
    """
811
    type_cname, method_name, obj_cname, args = matchobj.groups()
812
    type_cname = '&%s' % type_cname
813
    args = [arg.strip() for arg in args[1:].split(',')] if args else []
814
    assert len(args) < 3, f"CALL_UNBOUND_METHOD() does not support {len(args):d} call arguments"
815
    return output.cached_unbound_method_call_code(obj_cname, type_cname, method_name, args)
816

817

818
@add_macro_processor(
819
    'PYIDENT', 'PYUNICODE',
820
    is_module_specific=True,
821
    regex=r'PY(IDENT|UNICODE)\("([^"]+)"\)',
822
)
823
def _inject_string_constant(output, matchobj):
824
    """Replace 'PYIDENT("xyz")' by a constant Python identifier cname.
825
    """
826
    str_type, name = matchobj.groups()
827
    return output.get_py_string_const(
828
            StringEncoding.EncodedString(name), identifier=str_type == 'IDENT').cname
829

830

831
@add_macro_processor(
832
    'EMPTY',
833
    # As long as we use the same C access macros for these names, they are not module specific.
834
    # is_module_specific=True,
835
    regex=r'EMPTY\((bytes|unicode|tuple)\)',
836
)
837
def _inject_empty_collection_constant(output, matchobj):
838
    """Replace 'EMPTY(bytes|tuple|...)' by a constant Python identifier cname.
839
    """
840
    type_name = matchobj.group(1)
841
    return getattr(Naming, f'empty_{type_name}')
842

843

844
@add_macro_processor()
845
def process_utility_ccode(utility_code, _, code_string):
846
    """Entry point for code processors, must be defined last.
847
    """
848
    return code_string
849

850

851
def sub_tempita(s, context, file=None, name=None, __cache={}):
852
    "Run tempita on string s with given context."
853
    if not s:
854
        return None
855

856
    if file:
857
        name = f"{file}:{name}"
858
    if name:
859
        context['__name'] = name
860

861
    try:
862
        template = __cache[s]
863
    except KeyError:
864
        from ..Tempita import Template
865
        template = __cache[s] = Template(s, name=name)
866

867
    return template.substitute(context)
868

869

870
class TempitaUtilityCode(UtilityCode):
871
    def __init__(self, name=None, proto=None, impl=None, init=None, file=None, context=None, **kwargs):
872
        if context is None:
873
            context = {}
874
        proto = sub_tempita(proto, context, file, name)
875
        impl = sub_tempita(impl, context, file, name)
876
        init = sub_tempita(init, context, file, name)
877
        super().__init__(
878
            proto, impl, init=init, name=name, file=file, **kwargs)
879

880
    @classmethod
881
    def load_cached(cls, utility_code_name, from_file=None, context=None, __cache={}):
882
        context_key = tuple(sorted(context.items())) if context else None
883
        assert hash(context_key) is not None  # raise TypeError if not hashable
884
        key = (cls, from_file, utility_code_name, context_key)
885
        try:
886
            return __cache[key]
887
        except KeyError:
888
            pass
889
        code = __cache[key] = cls.load(utility_code_name, from_file, context=context)
890
        return code
891

892
    def none_or_sub(self, s, context):
893
        """
894
        Format a string in this utility code with context. If None, do nothing.
895
        """
896
        if s is None:
897
            return None
898
        return sub_tempita(s, context, self.file, self.name)
899

900

901
class LazyUtilityCode(UtilityCodeBase):
902
    """
903
    Utility code that calls a callback with the root code writer when
904
    available. Useful when you only have 'env' but not 'code'.
905
    """
906
    __name__ = '<lazy>'
907
    requires = None
908

909
    def __init__(self, callback):
910
        self.callback = callback
911

912
    def put_code(self, globalstate):
913
        utility = self.callback(globalstate.rootwriter)
914
        globalstate.use_utility_code(utility)
915

916

917
class FunctionState:
918
    # return_label     string          function return point label
919
    # error_label      string          error catch point label
920
    # error_without_exception  boolean Can go to the error label without an exception (e.g. __next__ can return NULL)
921
    # continue_label   string          loop continue point label
922
    # break_label      string          loop break point label
923
    # return_from_error_cleanup_label string
924
    # label_counter    integer         counter for naming labels
925
    # in_try_finally   boolean         inside try of try...finally
926
    # exc_vars         (string * 3)    exception variables for reraise, or None
927
    # can_trace        boolean         line tracing is supported in the current context
928
    # scope            Scope           the scope object of the current function
929

930
    # Not used for now, perhaps later
931
    def __init__(self, owner, names_taken=set(), scope=None):
932
        self.names_taken = names_taken
933
        self.owner = owner
934
        self.scope = scope
935

936
        self.error_label = None
937
        self.label_counter = 0
938
        self.labels_used = set()
939
        self.return_label = self.new_label()
940
        self.new_error_label()
941
        self.continue_label = None
942
        self.break_label = None
943
        self.yield_labels = []
944

945
        self.in_try_finally = 0
946
        self.exc_vars = None
947
        self.current_except = None
948
        self.can_trace = False
949
        self.gil_owned = True
950

951
        self.temps_allocated = []  # of (name, type, manage_ref, static)
952
        self.temps_free = {}  # (type, manage_ref) -> list of free vars with same type/managed status
953
        self.temps_used_type = {}  # name -> (type, manage_ref)
954
        self.zombie_temps = set()  # temps that must not be reused after release
955
        self.temp_counter = 0
956
        self.closure_temps = None
957

958
        # This is used to collect temporaries, useful to find out which temps
959
        # need to be privatized in parallel sections
960
        self.collect_temps_stack = []
961

962
        # This is used for the error indicator, which needs to be local to the
963
        # function. It used to be global, which relies on the GIL being held.
964
        # However, exceptions may need to be propagated through 'nogil'
965
        # sections, in which case we introduce a race condition.
966
        self.should_declare_error_indicator = False
967
        self.uses_error_indicator = False
968

969
        self.error_without_exception = False
970

971
        self.needs_refnanny = False
972

973
    # safety checks
974

975
    def validate_exit(self):
976
        # validate that all allocated temps have been freed
977
        if self.temps_allocated:
978
            leftovers = self.temps_in_use()
979
            if leftovers:
980
                msg = "TEMPGUARD: Temps left over at end of '%s': %s" % (self.scope.name, ', '.join([
981
                    '%s [%s]' % (name, ctype)
982
                    for name, ctype, is_pytemp in sorted(leftovers)]),
983
                )
984
                #print(msg)
985
                raise RuntimeError(msg)
986

987
    # labels
988

989
    def new_label(self, name=None):
990
        n: cython.size_t = self.label_counter
991
        self.label_counter = n + 1
992
        label = "%s%d" % (Naming.label_prefix, n)
993
        if name is not None:
994
            label += '_' + name
995
        return label
996

997
    def new_yield_label(self, expr_type='yield'):
998
        label = self.new_label('resume_from_%s' % expr_type)
999
        num_and_label = (len(self.yield_labels) + 1, label)
1000
        self.yield_labels.append(num_and_label)
1001
        return num_and_label
1002

1003
    def new_error_label(self, prefix=""):
1004
        old_err_lbl = self.error_label
1005
        self.error_label = self.new_label(prefix + 'error')
1006
        return old_err_lbl
1007

1008
    def get_loop_labels(self):
1009
        return (
1010
            self.continue_label,
1011
            self.break_label)
1012

1013
    def set_loop_labels(self, labels):
1014
        (self.continue_label,
1015
         self.break_label) = labels
1016

1017
    def new_loop_labels(self, prefix=""):
1018
        old_labels = self.get_loop_labels()
1019
        self.set_loop_labels(
1020
            (self.new_label(prefix + "continue"),
1021
             self.new_label(prefix + "break")))
1022
        return old_labels
1023

1024
    def get_all_labels(self):
1025
        return (
1026
            self.continue_label,
1027
            self.break_label,
1028
            self.return_label,
1029
            self.error_label)
1030

1031
    def set_all_labels(self, labels):
1032
        (self.continue_label,
1033
         self.break_label,
1034
         self.return_label,
1035
         self.error_label) = labels
1036

1037
    def all_new_labels(self):
1038
        old_labels = self.get_all_labels()
1039
        new_labels = []
1040
        for old_label, name in zip(old_labels, ['continue', 'break', 'return', 'error']):
1041
            if old_label:
1042
                new_labels.append(self.new_label(name))
1043
            else:
1044
                new_labels.append(old_label)
1045
        self.set_all_labels(new_labels)
1046
        return old_labels
1047

1048
    def use_label(self, lbl):
1049
        self.labels_used.add(lbl)
1050

1051
    def label_used(self, lbl):
1052
        return lbl in self.labels_used
1053

1054
    # temp handling
1055

1056
    def allocate_temp(self, type, manage_ref, static=False, reusable=True):
1057
        """
1058
        Allocates a temporary (which may create a new one or get a previously
1059
        allocated and released one of the same type). Type is simply registered
1060
        and handed back, but will usually be a PyrexType.
1061

1062
        If type.needs_refcounting, manage_ref comes into play. If manage_ref is set to
1063
        True, the temp will be decref-ed on return statements and in exception
1064
        handling clauses. Otherwise the caller has to deal with any reference
1065
        counting of the variable.
1066

1067
        If not type.needs_refcounting, then manage_ref will be ignored, but it
1068
        still has to be passed. It is recommended to pass False by convention
1069
        if it is known that type will never be a reference counted type.
1070

1071
        static=True marks the temporary declaration with "static".
1072
        This is only used when allocating backing store for a module-level
1073
        C array literals.
1074

1075
        if reusable=False, the temp will not be reused after release.
1076

1077
        A C string referring to the variable is returned.
1078
        """
1079
        if type.is_cv_qualified and not type.is_reference:
1080
            type = type.cv_base_type
1081
        elif type.is_reference and not type.is_fake_reference:
1082
            type = type.ref_base_type
1083
        elif type.is_cfunction:
1084
            from . import PyrexTypes
1085
            type = PyrexTypes.c_ptr_type(type)  # A function itself isn't an l-value
1086
        elif type.is_cpp_class and not type.is_fake_reference and self.scope.directives['cpp_locals']:
1087
            self.scope.use_utility_code(UtilityCode.load_cached("OptionalLocals", "CppSupport.cpp"))
1088
        if not type.needs_refcounting:
1089
            # Make manage_ref canonical, so that manage_ref will always mean
1090
            # a decref is needed.
1091
            manage_ref = False
1092

1093
        freelist = self.temps_free.get((type, manage_ref))
1094
        if reusable and freelist is not None and freelist[0]:
1095
            result = freelist[0].pop()
1096
            freelist[1].remove(result)
1097
        else:
1098
            while True:
1099
                self.temp_counter += 1
1100
                result = "%s%d" % (Naming.codewriter_temp_prefix, self.temp_counter)
1101
                if result not in self.names_taken: break
1102
            self.temps_allocated.append((result, type, manage_ref, static))
1103
            if not reusable:
1104
                self.zombie_temps.add(result)
1105
        self.temps_used_type[result] = (type, manage_ref)
1106
        if DebugFlags.debug_temp_code_comments:
1107
            self.owner.putln("/* %s allocated (%s)%s */" % (result, type, "" if reusable else " - zombie"))
1108

1109
        if self.collect_temps_stack:
1110
            self.collect_temps_stack[-1].add((result, type))
1111

1112
        return result
1113

1114
    def release_temp(self, name):
1115
        """
1116
        Releases a temporary so that it can be reused by other code needing
1117
        a temp of the same type.
1118
        """
1119
        type, manage_ref = self.temps_used_type[name]
1120
        freelist = self.temps_free.get((type, manage_ref))
1121
        if freelist is None:
1122
            freelist = ([], set())  # keep order in list and make lookups in set fast
1123
            self.temps_free[(type, manage_ref)] = freelist
1124
        if name in freelist[1]:
1125
            raise RuntimeError("Temp %s freed twice!" % name)
1126
        if name not in self.zombie_temps:
1127
            freelist[0].append(name)
1128
        freelist[1].add(name)
1129
        if DebugFlags.debug_temp_code_comments:
1130
            self.owner.putln("/* %s released %s*/" % (
1131
                name, " - zombie" if name in self.zombie_temps else ""))
1132

1133
    def temps_in_use(self):
1134
        """Return a list of (cname,type,manage_ref) tuples of temp names and their type
1135
        that are currently in use.
1136
        """
1137
        used = []
1138
        for name, type, manage_ref, static in self.temps_allocated:
1139
            freelist = self.temps_free.get((type, manage_ref))
1140
            if freelist is None or name not in freelist[1]:
1141
                used.append((name, type, manage_ref and type.needs_refcounting))
1142
        return used
1143

1144
    def temps_holding_reference(self):
1145
        """Return a list of (cname,type) tuples of temp names and their type
1146
        that are currently in use. This includes only temps
1147
        with a reference counted type which owns its reference.
1148
        """
1149
        return [(name, type)
1150
                for name, type, manage_ref in self.temps_in_use()
1151
                if manage_ref and type.needs_refcounting]
1152

1153
    def all_managed_temps(self):
1154
        """Return a list of (cname, type) tuples of refcount-managed Python objects.
1155
        """
1156
        return [(cname, type)
1157
                for cname, type, manage_ref, static in self.temps_allocated
1158
                if manage_ref]
1159

1160
    def all_free_managed_temps(self):
1161
        """Return a list of (cname, type) tuples of refcount-managed Python
1162
        objects that are not currently in use.  This is used by
1163
        try-except and try-finally blocks to clean up temps in the
1164
        error case.
1165
        """
1166
        return sorted([  # Enforce deterministic order.
1167
            (cname, type)
1168
            for (type, manage_ref), freelist in self.temps_free.items() if manage_ref
1169
            for cname in freelist[0]
1170
        ])
1171

1172
    def start_collecting_temps(self):
1173
        """
1174
        Useful to find out which temps were used in a code block
1175
        """
1176
        self.collect_temps_stack.append(set())
1177

1178
    def stop_collecting_temps(self):
1179
        return self.collect_temps_stack.pop()
1180

1181
    def init_closure_temps(self, scope):
1182
        self.closure_temps = ClosureTempAllocator(scope)
1183

1184

1185
class NumConst:
1186
    """Global info about a Python number constant held by GlobalState.
1187

1188
    cname       string
1189
    value       string
1190
    py_type     string     int, long, float
1191
    value_code  string     evaluation code if different from value
1192
    """
1193

1194
    def __init__(self, cname, value, py_type, value_code=None):
1195
        self.cname = cname
1196
        self.value = value
1197
        self.py_type = py_type
1198
        self.value_code = value_code or value
1199

1200

1201
class PyObjectConst:
1202
    """Global info about a generic constant held by GlobalState.
1203
    """
1204
    # cname       string
1205
    # type        PyrexType
1206

1207
    def __init__(self, cname, type):
1208
        self.cname = cname
1209
        self.type = type
1210

1211

1212
cython.declare(possible_unicode_identifier=object, possible_bytes_identifier=object,
1213
               replace_identifier=object, find_alphanums=object)
1214
possible_unicode_identifier = re.compile(r"(?![0-9])\w+$", re.U).match
1215
possible_bytes_identifier = re.compile(br"(?![0-9])\w+$").match
1216
replace_identifier = re.compile(r'[^a-zA-Z0-9_]+').sub
1217
find_alphanums = re.compile('([a-zA-Z0-9]+)').findall
1218

1219
class StringConst:
1220
    """Global info about a C string constant held by GlobalState.
1221
    """
1222
    # cname            string
1223
    # text             EncodedString or BytesLiteral
1224
    # py_strings       {(identifier, encoding) : PyStringConst}
1225

1226
    def __init__(self, cname, text, byte_string):
1227
        self.cname = cname
1228
        self.text = text
1229
        self.escaped_value = StringEncoding.escape_byte_string(byte_string)
1230
        self.py_strings = None
1231

1232
    def get_py_string_const(self, encoding, identifier=None):
1233
        text = self.text
1234
        intern: cython.bint
1235
        is_unicode: cython.bint
1236

1237
        if identifier or encoding is None:
1238
            # unicode string
1239
            encoding = encoding_key = None
1240
            is_unicode = True
1241
        else:
1242
            # bytes
1243
            is_unicode = False
1244
            encoding = encoding.lower()
1245
            if encoding in ('utf8', 'utf-8', 'ascii', 'usascii', 'us-ascii'):
1246
                encoding = None
1247
                encoding_key = None
1248
            else:
1249
                encoding_key = ''.join(find_alphanums(encoding))
1250

1251
        if identifier:
1252
            intern = True
1253
        elif identifier is None:
1254
            if isinstance(text, bytes):
1255
                intern = bool(possible_bytes_identifier(text))
1256
            else:
1257
                intern = bool(possible_unicode_identifier(text))
1258
        else:
1259
            intern = False
1260

1261
        key = (intern, is_unicode, encoding_key)
1262
        if self.py_strings is None:
1263
            self.py_strings = {}
1264
        else:
1265
            try:
1266
                return self.py_strings[key]
1267
            except KeyError:
1268
                pass
1269

1270
        pystring_cname = (
1271
            f"{Naming.interned_prefixes['str'] if intern else Naming.py_const_prefix}"
1272
            f"{'u' if is_unicode else 'b'}"
1273
            f"{'_' + encoding_key if encoding_key else ''}"
1274
            f"_{self.cname[len(Naming.const_prefix):]}"
1275
        )
1276

1277
        py_string = PyStringConst(pystring_cname, encoding, intern, is_unicode)
1278
        self.py_strings[key] = py_string
1279
        return py_string
1280

1281

1282
class PyStringConst:
1283
    """Global info about a Python string constant held by GlobalState.
1284
    """
1285
    # cname       string
1286
    # encoding    string
1287
    # intern      boolean
1288
    # is_unicode  boolean
1289

1290
    def __init__(self, cname, encoding, intern=False, is_unicode=False):
1291
        self.cname = cname
1292
        self.encoding = encoding
1293
        self.is_unicode = is_unicode
1294
        self.intern = intern
1295

1296
    def __lt__(self, other):
1297
        return self.cname < other.cname
1298

1299

1300
class GlobalState:
1301
    # filename_table   {string : int}  for finding filename table indexes
1302
    # filename_list    [string]        filenames in filename table order
1303
    # input_file_contents dict         contents (=list of lines) of any file that was used as input
1304
    #                                  to create this output C code.  This is
1305
    #                                  used to annotate the comments.
1306
    #
1307
    # utility_codes   set                IDs of used utility code (to avoid reinsertion)
1308
    #
1309
    # declared_cnames  {string:Entry}  used in a transition phase to merge pxd-declared
1310
    #                                  constants etc. into the pyx-declared ones (i.e,
1311
    #                                  check if constants are already added).
1312
    #                                  In time, hopefully the literals etc. will be
1313
    #                                  supplied directly instead.
1314
    #
1315
    # const_cnames_used  dict          global counter for unique constant identifiers
1316
    #
1317

1318
    # parts            {string:CCodeWriter}
1319

1320

1321
    # interned_strings
1322
    # consts
1323
    # interned_nums
1324

1325
    # directives       set             Temporary variable used to track
1326
    #                                  the current set of directives in the code generation
1327
    #                                  process.
1328

1329
    directives = {}
1330

1331
    code_layout = [
1332
        'h_code',
1333
        'filename_table',
1334
        'utility_code_proto_before_types',
1335
        'numeric_typedefs',           # Let these detailed individual parts stay!,
1336
        'complex_type_declarations',  # as the proper solution is to make a full DAG...
1337
        'type_declarations',          # More coarse-grained blocks would simply hide
1338
        'utility_code_proto',         # the ugliness, not fix it
1339
        'module_declarations',
1340
        'typeinfo',
1341
        'before_global_var',
1342
        'global_var',
1343
        'string_decls',
1344
        'decls',
1345
        'late_includes',
1346
        'module_state',
1347
        'module_state_clear',
1348
        'module_state_traverse',
1349
        'module_state_defines',  # redefines names used in module_state/_clear/_traverse
1350
        'module_code',  # user code goes here
1351
        'module_exttypes',
1352
        'initfunc_declarations',
1353
        'init_module',
1354
        'pystring_table',
1355
        'cached_builtins',
1356
        'cached_constants',
1357
        'init_constants',
1358
        'init_codeobjects',
1359
        'init_globals',  # (utility code called at init-time)
1360
        'cleanup_globals',
1361
        'cleanup_module',
1362
        'main_method',
1363
        'utility_code_pragmas',  # silence some irrelevant warnings in utility code
1364
        'utility_code_def',
1365
        'utility_code_pragmas_end',  # clean-up the utility_code_pragmas
1366
        'end'
1367
    ]
1368

1369
    # h files can only have a much smaller list of sections
1370
    h_code_layout = [
1371
        'h_code',
1372
        'utility_code_proto_before_types',
1373
        'type_declarations',
1374
        'utility_code_proto',
1375
        'end'
1376
    ]
1377

1378
    def __init__(self, writer, module_node, code_config, common_utility_include_dir=None):
1379
        self.filename_table = {}
1380
        self.filename_list = []
1381
        self.input_file_contents = {}
1382
        self.utility_codes = set()
1383
        self.declared_cnames = {}
1384
        self.in_utility_code_generation = False
1385
        self.code_config = code_config
1386
        self.common_utility_include_dir = common_utility_include_dir
1387
        self.parts = {}
1388
        self.module_node = module_node  # because some utility code generation needs it
1389
                                        # (generating backwards-compatible Get/ReleaseBuffer
1390

1391
        self.const_cnames_used = {}
1392
        self.string_const_index = {}
1393
        self.dedup_const_index = {}
1394
        self.pyunicode_ptr_const_index = {}
1395
        self.codeobject_constants = []
1396
        self.num_const_index = {}
1397
        self.arg_default_constants = []
1398
        self.const_array_counters = {}  # counts of differently prefixed arrays of constants
1399
        self.cached_cmethods = {}
1400
        self.initialised_constants = set()
1401

1402
        writer.set_global_state(self)
1403
        self.rootwriter = writer
1404

1405
    def initialize_main_c_code(self):
1406
        rootwriter = self.rootwriter
1407
        for i, part in enumerate(self.code_layout):
1408
            w = self.parts[part] = rootwriter.insertion_point()
1409
            if i > 0:
1410
                w.putln("/* #### Code section: %s ### */" % part)
1411

1412
        if not Options.cache_builtins:
1413
            del self.parts['cached_builtins']
1414
        else:
1415
            w = self.parts['cached_builtins']
1416
            w.start_initcfunc("int __Pyx_InitCachedBuiltins(void)")
1417

1418
        w = self.parts['cached_constants']
1419
        w.start_initcfunc("int __Pyx_InitCachedConstants(void)", refnanny=True)
1420
        w.put_setup_refcount_context(StringEncoding.EncodedString("__Pyx_InitCachedConstants"))
1421

1422
        w = self.parts['init_globals']
1423
        w.start_initcfunc("int __Pyx_InitGlobals(void)")
1424

1425
        w = self.parts['init_constants']
1426
        w.start_initcfunc("int __Pyx_InitConstants(void)")
1427

1428
        if not Options.generate_cleanup_code:
1429
            del self.parts['cleanup_globals']
1430
        else:
1431
            w = self.parts['cleanup_globals']
1432
            w.start_initcfunc("void __Pyx_CleanupGlobals(void)")
1433

1434
        code = self.parts['utility_code_proto']
1435
        code.putln("")
1436
        code.putln("/* --- Runtime support code (head) --- */")
1437

1438
        code = self.parts['utility_code_def']
1439
        if self.code_config.emit_linenums:
1440
            code.write('\n#line 1 "cython_utility"\n')
1441
        code.putln("")
1442
        code.putln("/* --- Runtime support code --- */")
1443

1444
    def initialize_main_h_code(self):
1445
        rootwriter = self.rootwriter
1446
        for part in self.h_code_layout:
1447
            self.parts[part] = rootwriter.insertion_point()
1448

1449
    def finalize_main_c_code(self):
1450
        self.close_global_decls()
1451

1452
        #
1453
        # utility_code_def
1454
        #
1455
        code = self.parts['utility_code_def']
1456
        util = TempitaUtilityCode.load_cached("TypeConversions", "TypeConversion.c")
1457
        code.put(util.format_code(util.impl))
1458
        code.putln("")
1459

1460
        #
1461
        # utility code pragmas
1462
        #
1463
        code = self.parts['utility_code_pragmas']
1464
        util = UtilityCode.load_cached("UtilityCodePragmas", "ModuleSetupCode.c")
1465
        code.putln(util.format_code(util.impl))
1466
        code.putln("")
1467
        code = self.parts['utility_code_pragmas_end']
1468
        util = UtilityCode.load_cached("UtilityCodePragmasEnd", "ModuleSetupCode.c")
1469
        code.putln(util.format_code(util.impl))
1470
        code.putln("")
1471

1472
    def __getitem__(self, key):
1473
        return self.parts[key]
1474

1475
    #
1476
    # Global constants, interned objects, etc.
1477
    #
1478
    def close_global_decls(self):
1479
        # This is called when it is known that no more global declarations will
1480
        # declared.
1481
        self.generate_const_declarations()
1482
        if Options.cache_builtins:
1483
            w = self.parts['cached_builtins']
1484
            w.putln("return 0;")
1485
            if w.label_used(w.error_label):
1486
                w.put_label(w.error_label)
1487
                w.putln("return -1;")
1488
            w.putln("}")
1489
            w.exit_cfunc_scope()
1490

1491
        w = self.parts['cached_constants']
1492
        w.put_finish_refcount_context()
1493
        w.putln("return 0;")
1494
        if w.label_used(w.error_label):
1495
            w.put_label(w.error_label)
1496
            w.put_finish_refcount_context()
1497
            w.putln("return -1;")
1498
        w.putln("}")
1499
        w.exit_cfunc_scope()
1500

1501
        for part in ['init_globals', 'init_constants']:
1502
            w = self.parts[part]
1503
            w.putln("return 0;")
1504
            if w.label_used(w.error_label):
1505
                w.put_label(w.error_label)
1506
                w.putln("return -1;")
1507
            w.putln("}")
1508
            w.exit_cfunc_scope()
1509

1510
        if Options.generate_cleanup_code:
1511
            w = self.parts['cleanup_globals']
1512
            w.putln("}")
1513
            w.exit_cfunc_scope()
1514

1515
        if Options.generate_cleanup_code:
1516
            w = self.parts['cleanup_module']
1517
            w.putln("}")
1518
            w.exit_cfunc_scope()
1519

1520
    def put_pyobject_decl(self, entry):
1521
        self['global_var'].putln("static PyObject *%s;" % entry.cname)
1522

1523
    # constant handling at code generation time
1524

1525
    def get_cached_constants_writer(self, target=None):
1526
        if target is not None:
1527
            if target in self.initialised_constants:
1528
                # Return None on second/later calls to prevent duplicate creation code.
1529
                return None
1530
            self.initialised_constants.add(target)
1531
        return self.parts['cached_constants']
1532

1533
    def get_int_const(self, str_value, longness=False):
1534
        py_type = longness and 'long' or 'int'
1535
        try:
1536
            c = self.num_const_index[(str_value, py_type)]
1537
        except KeyError:
1538
            c = self.new_num_const(str_value, py_type)
1539
        return c
1540

1541
    def get_float_const(self, str_value, value_code):
1542
        try:
1543
            c = self.num_const_index[(str_value, 'float')]
1544
        except KeyError:
1545
            c = self.new_num_const(str_value, 'float', value_code)
1546
        return c
1547

1548
    def get_py_const(self, prefix, dedup_key=None):
1549
        if dedup_key is not None:
1550
            const = self.dedup_const_index.get(dedup_key)
1551
            if const is not None:
1552
                return const
1553
        const = self.new_array_const_cname(prefix)
1554
        if dedup_key is not None:
1555
            self.dedup_const_index[dedup_key] = const
1556
        return const
1557

1558
    def get_argument_default_const(self, type):
1559
        cname = self.new_const_cname('')
1560
        c = PyObjectConst(cname, type)
1561
        self.arg_default_constants.append(c)
1562
        # Argument default constants aren't currently cleaned up.
1563
        # If that changes, it needs to account for the fact that they
1564
        # aren't just Python objects
1565
        return c
1566

1567
    def get_string_const(self, text):
1568
        # return a C string constant, creating a new one if necessary
1569
        if text.is_unicode:
1570
            byte_string = text.utf8encode()
1571
        else:
1572
            byte_string = text.byteencode()
1573
        try:
1574
            c = self.string_const_index[byte_string]
1575
        except KeyError:
1576
            c = self.new_string_const(text, byte_string)
1577
        return c
1578

1579
    def get_pyunicode_ptr_const(self, text):
1580
        # return a Py_UNICODE[] constant, creating a new one if necessary
1581
        assert text.is_unicode
1582
        try:
1583
            c = self.pyunicode_ptr_const_index[text]
1584
        except KeyError:
1585
            c = self.pyunicode_ptr_const_index[text] = self.new_const_cname()
1586
        return c
1587

1588
    def get_py_string_const(self, text, identifier=None):
1589
        # return a Python string constant, creating a new one if necessary
1590
        c_string = self.get_string_const(text)
1591
        py_string = c_string.get_py_string_const(text.encoding, identifier)
1592
        return py_string
1593

1594
    def get_py_codeobj_const(self, node):
1595
        idx = len(self.codeobject_constants)
1596
        name = f"{Naming.codeobjtab_cname}[{idx}]"
1597
        self.codeobject_constants.append(node)
1598
        return name
1599

1600
    def get_interned_identifier(self, text):
1601
        return self.get_py_string_const(text, identifier=True)
1602

1603
    def new_string_const(self, text, byte_string):
1604
        cname = self.new_string_const_cname(byte_string)
1605
        c = StringConst(cname, text, byte_string)
1606
        self.string_const_index[byte_string] = c
1607
        return c
1608

1609
    def new_num_const(self, value, py_type, value_code=None):
1610
        cname = self.new_num_const_cname(value, py_type)
1611
        c = NumConst(cname, value, py_type, value_code)
1612
        self.num_const_index[(value, py_type)] = c
1613
        return c
1614

1615
    def new_string_const_cname(self, bytes_value):
1616
        # Create a new globally-unique nice name for a C string constant.
1617
        value = bytes_value.decode('ASCII', 'ignore')
1618
        return self.new_const_cname(value=value)
1619

1620
    def unique_const_cname(self, format_str):  # type: (str) -> str
1621
        used = self.const_cnames_used
1622
        cname = value = format_str.format(sep='', counter='')
1623
        while cname in used:
1624
            counter = used[value] = used[value] + 1
1625
            cname = format_str.format(sep='_', counter=counter)
1626
        used[cname] = 1
1627
        return cname
1628

1629
    def new_num_const_cname(self, value, py_type):  # type: (str, str) -> str
1630
        if py_type == 'long':
1631
            value += 'L'
1632
            py_type = 'int'
1633
        prefix = Naming.interned_prefixes[py_type]
1634

1635
        value = value.replace('.', '_').replace('+', '_').replace('-', 'neg_')
1636
        if len(value) > 42:
1637
            # update tests/run/large_integer_T5290.py in case the amount is changed
1638
            cname = self.unique_const_cname(
1639
                prefix + "large{counter}_" + value[:18] + "_xxx_" + value[-18:])
1640
        else:
1641
            cname = "%s%s" % (prefix, value)
1642
        return cname
1643

1644
    def new_const_cname(self, prefix='', value=''):
1645
        value = replace_identifier('_', value)[:32].strip('_')
1646
        name_suffix = self.unique_const_cname(value + "{sep}{counter}")
1647
        if prefix:
1648
            prefix = Naming.interned_prefixes[prefix]
1649
        else:
1650
            prefix = Naming.const_prefix
1651
        return "%s%s" % (prefix, name_suffix)
1652

1653
    def new_array_const_cname(self, prefix: str):
1654
        count = self.const_array_counters.get(prefix, 0)
1655
        self.const_array_counters[prefix] = count+1
1656
        return f"{Naming.pyrex_prefix}{prefix}[{count}]"
1657

1658
    def get_cached_unbound_method(self, type_cname, method_name):
1659
        key = (type_cname, method_name)
1660
        try:
1661
            cname = self.cached_cmethods[key]
1662
        except KeyError:
1663
            cname = self.cached_cmethods[key] = self.new_const_cname(
1664
                'umethod', '%s_%s' % (type_cname, method_name))
1665
        return cname
1666

1667
    def cached_unbound_method_call_code(self, obj_cname, type_cname, method_name, arg_cnames):
1668
        # admittedly, not the best place to put this method, but it is reused by UtilityCode and ExprNodes ...
1669
        utility_code_name = "CallUnboundCMethod%d" % len(arg_cnames)
1670
        self.use_utility_code(UtilityCode.load_cached(utility_code_name, "ObjectHandling.c"))
1671
        cache_cname = self.get_cached_unbound_method(type_cname, method_name)
1672
        args = [obj_cname] + arg_cnames
1673
        return "__Pyx_%s(&%s, %s)" % (
1674
            utility_code_name,
1675
            cache_cname,
1676
            ', '.join(args),
1677
        )
1678

1679
    def add_cached_builtin_decl(self, entry):
1680
        if entry.is_builtin and entry.is_const:
1681
            if self.should_declare(entry.cname, entry):
1682
                self.put_pyobject_decl(entry)
1683
                name = entry.name
1684
                if name in renamed_py2_builtins_map:
1685
                    name = renamed_py2_builtins_map[name]
1686
                self.put_cached_builtin_init(
1687
                    entry.pos, StringEncoding.EncodedString(name),
1688
                    entry.cname)
1689

1690
    def put_cached_builtin_init(self, pos, name, cname):
1691
        w = self.parts['cached_builtins']
1692
        interned_cname = self.get_interned_identifier(name).cname
1693
        self.use_utility_code(
1694
            UtilityCode.load_cached("GetBuiltinName", "ObjectHandling.c"))
1695
        w.putln('%s = __Pyx_GetBuiltinName(%s); if (!%s) %s' % (
1696
            cname,
1697
            interned_cname,
1698
            cname,
1699
            w.error_goto(pos)))
1700

1701
    def generate_const_declarations(self):
1702
        self.generate_cached_methods_decls()
1703
        self.generate_object_constant_decls()
1704
        self.generate_codeobject_constants()
1705
        # generate code for string and numeric constants as late as possible
1706
        # to allow new constants be to created by the earlier stages.
1707
        # (although the constants themselves are written early)
1708
        self.generate_string_constants()
1709
        self.generate_num_constants()
1710

1711
    def _generate_module_array_traverse_and_clear(self, struct_attr_cname, count, may_have_refcycles=True):
1712
        counter_type = 'int' if count < 2**15 else 'Py_ssize_t'
1713
        visit_call = "Py_VISIT" if may_have_refcycles else "__Pyx_VISIT_CONST"
1714

1715
        writer = self.parts['module_state_traverse']
1716
        writer.putln(f"for ({counter_type} i=0; i<{count}; ++i) {{ {visit_call}(traverse_module_state->{struct_attr_cname}[i]); }}")
1717

1718
        writer = self.parts['module_state_clear']
1719
        writer.putln(f"for ({counter_type} i=0; i<{count}; ++i) {{ Py_CLEAR(clear_module_state->{struct_attr_cname}[i]); }}")
1720

1721
    def generate_object_constant_decls(self):
1722
        consts = [(len(c.cname), c.cname, c)
1723
                  for c in self.arg_default_constants]
1724
        consts.sort()
1725
        for _, cname, c in consts:
1726
            self.parts['module_state'].putln("%s;" % c.type.declaration_code(cname))
1727
            self.parts['module_state_defines'].putln(
1728
                "#define %s %s->%s" % (cname, Naming.modulestateglobal_cname, cname))
1729
            if not c.type.needs_refcounting:
1730
                # Note that py_constants is used for all argument defaults
1731
                # which aren't necessarily PyObjects, so aren't appropriate
1732
                # to clear.
1733
                continue
1734

1735
            self.parts['module_state_clear'].put_xdecref_clear(
1736
                f"clear_module_state->{cname}",
1737
                c.type,
1738
                clear_before_decref=True,
1739
                nanny=False,
1740
            )
1741

1742
            if c.type.is_memoryviewslice:
1743
                # TODO: Implement specific to type like CodeWriter.put_xdecref_clear()
1744
                cname += "->memview"
1745

1746
            self.parts['module_state_traverse'].putln(
1747
                f"Py_VISIT(traverse_module_state->{cname});")
1748

1749
        for prefix, count in sorted(self.const_array_counters.items()):
1750
            # name the struct attribute and the global "define" slightly differently
1751
            # to avoid the global define getting in the way
1752
            struct_attr_cname = f"{Naming.pyrex_prefix}_{prefix}"
1753
            global_cname = f"{Naming.pyrex_prefix}{prefix}"
1754
            self.parts['module_state'].putln(f"PyObject *{struct_attr_cname}[{count}];")
1755
            self.parts['module_state_defines'].putln(
1756
                f"#define {global_cname} {Naming.modulestateglobal_cname}->{struct_attr_cname}")
1757

1758
            # The constant tuples/slices that we create can never participate in reference cycles.
1759
            self._generate_module_array_traverse_and_clear(struct_attr_cname, count, may_have_refcycles=False)
1760

1761
            cleanup_level = cleanup_level_for_type_prefix(prefix)
1762
            if cleanup_level is not None and cleanup_level <= Options.generate_cleanup_code:
1763
                part_writer = self.parts['cleanup_globals']
1764
                part_writer.putln(f"for (Py_ssize_t i=0; i<{count}; ++i) {{ Py_CLEAR({global_cname}[i]); }}")
1765

1766
    def generate_cached_methods_decls(self):
1767
        if not self.cached_cmethods:
1768
            return
1769

1770
        decl = self.parts['decls']
1771
        init = self.parts['init_constants']
1772
        cnames = []
1773
        for (type_cname, method_name), cname in sorted(self.cached_cmethods.items()):
1774
            cnames.append(cname)
1775
            method_name_cname = self.get_interned_identifier(StringEncoding.EncodedString(method_name)).cname
1776
            decl.putln('static __Pyx_CachedCFunction %s = {0, 0, 0, 0, 0};' % (
1777
                cname))
1778
            # split type reference storage as it might not be static
1779
            init.putln('%s.type = (PyObject*)%s;' % (
1780
                cname, type_cname))
1781
            # method name string isn't static in limited api
1782
            init.putln('%s.method_name = &%s;' % (
1783
                cname, method_name_cname))
1784

1785
        if Options.generate_cleanup_code:
1786
            cleanup = self.parts['cleanup_globals']
1787
            for cname in cnames:
1788
                cleanup.putln("Py_CLEAR(%s.method);" % cname)
1789

1790
    def generate_string_constants(self):
1791
        c_consts = [(len(c.cname), c.cname, c) for c in self.string_const_index.values()]
1792
        c_consts.sort()
1793
        py_strings = []
1794
        longest_pystring = 0
1795
        encodings = set()
1796

1797
        def normalise_encoding_name(py_string):
1798
            if py_string.encoding and py_string.encoding not in (
1799
                    'ASCII', 'USASCII', 'US-ASCII', 'UTF8', 'UTF-8'):
1800
                return f'"{py_string.encoding.lower()}"'
1801
            else:
1802
                return '0'
1803

1804
        decls_writer = self.parts['string_decls']
1805
        for _, cname, c in c_consts:
1806
            cliteral = StringEncoding.split_string_literal(c.escaped_value)
1807
            decls_writer.putln(
1808
                f'static const char {cname}[] = "{cliteral}";',
1809
                safe=True)  # Braces in user strings are not for indentation.
1810
            if c.py_strings is not None:
1811
                if len(c.escaped_value) > longest_pystring:
1812
                    # This is not an accurate count since it adds up C escape characters,
1813
                    # but it's probably good enough for an upper bound.
1814
                    longest_pystring = len(c.escaped_value)
1815
                for py_string in c.py_strings.values():
1816
                    encodings.add(normalise_encoding_name(py_string))
1817
                    py_strings.append((c.cname, len(py_string.cname), py_string))
1818

1819
        for c, cname in sorted(self.pyunicode_ptr_const_index.items()):
1820
            utf16_array, utf32_array = StringEncoding.encode_pyunicode_string(c)
1821
            if utf16_array:
1822
                # Narrow and wide representations differ
1823
                decls_writer.putln("#ifdef Py_UNICODE_WIDE")
1824
            decls_writer.putln("static Py_UNICODE %s[] = { %s };" % (cname, utf32_array))
1825
            if utf16_array:
1826
                decls_writer.putln("#else")
1827
                decls_writer.putln("static Py_UNICODE %s[] = { %s };" % (cname, utf16_array))
1828
                decls_writer.putln("#endif")
1829

1830
        if not py_strings:
1831
            return
1832

1833
        py_strings.sort()
1834

1835
        w = self.parts['pystring_table']
1836
        w.putln("")
1837

1838
        # We use only type size macros from "pyport.h" here.
1839
        w.put(textwrap.dedent("""\
1840
        typedef struct {
1841
            const char *s;
1842
        #if %(max_length)d <= 65535
1843
            const unsigned short n;
1844
        #elif %(max_length)d / 2 < INT_MAX
1845
            const unsigned int n;
1846
        #elif %(max_length)d / 2 < LONG_MAX
1847
            const unsigned long n;
1848
        #else
1849
            const Py_ssize_t n;
1850
        #endif
1851
        #if %(num_encodings)d <= 31
1852
            const unsigned int encoding : 5;
1853
        #elif %(num_encodings)d <= 255
1854
            const unsigned char encoding;
1855
        #elif %(num_encodings)d <= 65535
1856
            const unsigned short encoding;
1857
        #else
1858
            const Py_ssize_t encoding;
1859
        #endif
1860
            const unsigned int is_unicode : 1;
1861
            const unsigned int intern : 1;
1862
        } __Pyx_StringTabEntry;
1863
        """ % dict(
1864
            max_length=longest_pystring,
1865
            num_encodings=len(encodings),
1866
        )))
1867

1868
        py_string_count = len(py_strings)
1869
        self.parts['module_state'].putln(f"PyObject *{Naming.stringtab_cname}[{py_string_count}];")
1870
        self._generate_module_array_traverse_and_clear(Naming.stringtab_cname, py_string_count, may_have_refcycles=False)
1871

1872
        encodings = sorted(encodings)
1873
        encodings.sort(key=len)  # stable sort to make sure '0' comes first, index 0
1874
        assert not encodings or '0' not in encodings or encodings[0] == '0', encodings
1875
        encodings_map = {encoding: i for i, encoding in enumerate(encodings)}
1876
        w.putln("static const char * const %s[] = { %s };" % (
1877
            Naming.stringtab_encodings_cname,
1878
            ', '.join(encodings),
1879
        ))
1880

1881
        w.putln("static const __Pyx_StringTabEntry %s[] = {" % Naming.stringtab_cname)
1882
        for n, (c_cname, _, py_string) in enumerate(py_strings):
1883
            encodings_index = encodings_map[normalise_encoding_name(py_string)]
1884
            is_unicode = py_string.is_unicode
1885

1886
            self.parts['module_state_defines'].putln("#define %s %s->%s[%s]" % (
1887
                py_string.cname,
1888
                Naming.modulestateglobal_cname,
1889
                Naming.stringtab_cname,
1890
                n))
1891

1892
            w.putln("{%s, sizeof(%s), %d, %d, %d}, /* PyObject cname: %s */" % (
1893
                c_cname,
1894
                c_cname,
1895
                encodings_index,
1896
                is_unicode,
1897
                py_string.intern,
1898
                py_string.cname
1899
                ))
1900
        w.putln("{0, 0, 0, 0, 0}")
1901
        w.putln("};")
1902

1903
        self.use_utility_code(UtilityCode.load_cached("InitStrings", "StringTools.c"))
1904

1905
        init_constants = self.parts['init_constants']
1906
        init_constants.putln(
1907
            "if (__Pyx_InitStrings(%s, %s->%s, %s) < 0) %s;" % (
1908
                Naming.stringtab_cname,
1909
                Naming.modulestateglobal_cname,
1910
                Naming.stringtab_cname,
1911
                Naming.stringtab_encodings_cname,
1912
                init_constants.error_goto(self.module_pos)))
1913

1914
    def generate_codeobject_constants(self):
1915
        w = self.parts['init_codeobjects']
1916
        init_function = "int __Pyx_CreateCodeObjects(void)"
1917

1918
        if not self.codeobject_constants:
1919
            w.start_initcfunc(init_function)
1920
            w.putln("return 0;")
1921
            w.exit_cfunc_scope()
1922
            w.putln("}")
1923
            return
1924

1925
        # Create a downsized config struct and build code objects from it.
1926
        max_flags = 0x3ff  # to be adapted when we start using new flags
1927
        max_func_args = 1
1928
        max_kwonly_args = 1
1929
        max_posonly_args = 1
1930
        max_vars = 1
1931
        max_line = 1
1932
        max_positions = 1
1933
        for node in self.codeobject_constants:
1934
            def_node = node.def_node
1935
            if not def_node.is_generator_expression:
1936
                max_func_args = max(max_func_args, len(def_node.args) - def_node.num_kwonly_args)
1937
                max_kwonly_args = max(max_kwonly_args, def_node.num_kwonly_args)
1938
                max_posonly_args = max(max_posonly_args, def_node.num_posonly_args)
1939
            max_vars = max(max_vars, len(node.varnames))
1940
            max_line = max(max_line, def_node.pos[1])
1941
            max_positions = max(max_positions, len(def_node.node_positions))
1942

1943
        # Even for full 64-bit line/column values, one entry in the line table can never be larger than 45 bytes.
1944
        max_linetable_len = max_positions * 47
1945

1946
        w.put(textwrap.dedent(f"""\
1947
        typedef struct {{
1948
            unsigned int argcount : {max_func_args.bit_length()};
1949
            unsigned int num_posonly_args : {max_posonly_args.bit_length()};
1950
            unsigned int num_kwonly_args : {max_kwonly_args.bit_length()};
1951
            unsigned int nlocals : {max_vars.bit_length()};
1952
            unsigned int flags : {max_flags.bit_length()};
1953
            unsigned int first_line : {max_line.bit_length()};
1954
            unsigned int line_table_length : {max_linetable_len.bit_length()};
1955
        }} __Pyx_PyCode_New_function_description;
1956
        """))
1957

1958
        self.use_utility_code(UtilityCode.load_cached("NewCodeObj", "ModuleSetupCode.c"))
1959

1960
        w.start_initcfunc(init_function)
1961

1962
        w.putln("PyObject* tuple_dedup_map = PyDict_New();")
1963
        w.putln("if (unlikely(!tuple_dedup_map)) return -1;")
1964

1965
        for node in self.codeobject_constants:
1966
            node.generate_codeobj(w, "bad")
1967

1968
        w.putln("Py_DECREF(tuple_dedup_map);")
1969
        w.putln("return 0;")
1970

1971
        w.putln("bad:")
1972
        w.putln("Py_DECREF(tuple_dedup_map);")
1973
        w.putln("return -1;")
1974
        w.exit_cfunc_scope()
1975
        w.putln("}")
1976

1977
        code_object_count = len(self.codeobject_constants)
1978
        self.parts['module_state'].putln(f"PyObject *{Naming.codeobjtab_cname}[{code_object_count}];")
1979
        # The code objects that we generate only contain plain constants and can never participate in reference cycles.
1980
        self._generate_module_array_traverse_and_clear(Naming.codeobjtab_cname, code_object_count, may_have_refcycles=False)
1981

1982
        self.parts['module_state_defines'].putln("#define %s %s->%s" % (
1983
            Naming.codeobjtab_cname, Naming.modulestateglobal_cname, Naming.codeobjtab_cname
1984
        ))
1985

1986
    def generate_num_constants(self):
1987
        consts = [(c.py_type, c.value[0] == '-', len(c.value), c.value, c.value_code, c)
1988
                  for c in self.num_const_index.values()]
1989
        consts.sort()
1990
        init_constants = self.parts['init_constants']
1991
        for py_type, _, _, value, value_code, c in consts:
1992
            cname = c.cname
1993
            self.parts['module_state'].putln("PyObject *%s;" % cname)
1994
            self.parts['module_state_defines'].putln("#define %s %s->%s" % (
1995
                cname, Naming.modulestateglobal_cname, cname))
1996
            self.parts['module_state_clear'].putln(
1997
                "Py_CLEAR(clear_module_state->%s);" % cname)
1998
            self.parts['module_state_traverse'].putln(
1999
                "__Pyx_VISIT_CONST(traverse_module_state->%s);" % cname)
2000
            if py_type == 'float':
2001
                function = 'PyFloat_FromDouble(%s)'
2002
            elif py_type == 'long':
2003
                function = 'PyLong_FromString("%s", 0, 0)'
2004
            elif Utils.long_literal(value):
2005
                function = 'PyLong_FromString("%s", 0, 0)'
2006
            elif len(value.lstrip('-')) > 4:
2007
                function = "PyLong_FromLong(%sL)"
2008
            else:
2009
                function = "PyLong_FromLong(%s)"
2010
            init_constants.putln('%s = %s; %s' % (
2011
                cname, function % value_code,
2012
                init_constants.error_goto_if_null(cname, self.module_pos)))
2013

2014
    # The functions below are there in a transition phase only
2015
    # and will be deprecated. They are called from Nodes.BlockNode.
2016
    # The copy&paste duplication is intentional in order to be able
2017
    # to see quickly how BlockNode worked, until this is replaced.
2018

2019
    def should_declare(self, cname, entry):
2020
        if cname in self.declared_cnames:
2021
            other = self.declared_cnames[cname]
2022
            assert str(entry.type) == str(other.type)
2023
            assert entry.init == other.init
2024
            return False
2025
        else:
2026
            self.declared_cnames[cname] = entry
2027
            return True
2028

2029
    #
2030
    # File name state
2031
    #
2032

2033
    def lookup_filename(self, source_desc):
2034
        entry = source_desc.get_filenametable_entry()
2035
        try:
2036
            index = self.filename_table[entry]
2037
        except KeyError:
2038
            index = len(self.filename_list)
2039
            self.filename_list.append(source_desc)
2040
            self.filename_table[entry] = index
2041
        return index
2042

2043
    def commented_file_contents(self, source_desc):
2044
        try:
2045
            return self.input_file_contents[source_desc]
2046
        except KeyError:
2047
            pass
2048
        source_file = source_desc.get_lines(encoding='ASCII',
2049
                                            error_handling='ignore')
2050
        try:
2051
            F = [' * ' + line.rstrip().replace(
2052
                    '*/', '*[inserted by cython to avoid comment closer]/'
2053
                    ).replace(
2054
                    '/*', '/[inserted by cython to avoid comment start]*'
2055
                    )
2056
                 for line in source_file]
2057
        finally:
2058
            if hasattr(source_file, 'close'):
2059
                source_file.close()
2060
        if not F: F.append('')
2061
        self.input_file_contents[source_desc] = F
2062
        return F
2063

2064
    #
2065
    # Utility code state
2066
    #
2067

2068
    def use_utility_code(self, utility_code):
2069
        """
2070
        Adds code to the C file. utility_code should
2071
        a) implement __eq__/__hash__ for the purpose of knowing whether the same
2072
           code has already been included
2073
        b) implement put_code, which takes a globalstate instance
2074

2075
        See UtilityCode.
2076
        """
2077
        if utility_code and utility_code not in self.utility_codes:
2078
            self.utility_codes.add(utility_code)
2079
            utility_code.put_code(self)
2080

2081
    def use_entry_utility_code(self, entry):
2082
        if entry is None:
2083
            return
2084
        if entry.utility_code:
2085
            self.use_utility_code(entry.utility_code)
2086
        if entry.utility_code_definition:
2087
            self.use_utility_code(entry.utility_code_definition)
2088

2089

2090
def funccontext_property(func):
2091
    name = func.__name__
2092
    attribute_of = operator.attrgetter(name)
2093
    def get(self):
2094
        return attribute_of(self.funcstate)
2095
    def set(self, value):
2096
        setattr(self.funcstate, name, value)
2097
    return property(get, set)
2098

2099

2100
class CCodeConfig:
2101
    # emit_linenums       boolean         write #line pragmas?
2102
    # emit_code_comments  boolean         copy the original code into C comments?
2103
    # c_line_in_traceback boolean         append the c file and line number to the traceback for exceptions?
2104

2105
    def __init__(self, emit_linenums=True, emit_code_comments=True, c_line_in_traceback=True):
2106
        self.emit_code_comments = emit_code_comments
2107
        self.emit_linenums = emit_linenums
2108
        self.c_line_in_traceback = c_line_in_traceback
2109

2110

2111
class CCodeWriter:
2112
    """
2113
    Utility class to output C code.
2114

2115
    When creating an insertion point one must care about the state that is
2116
    kept:
2117
    - formatting state (level, bol) is cloned and used in insertion points
2118
      as well
2119
    - labels, temps, exc_vars: One must construct a scope in which these can
2120
      exist by calling enter_cfunc_scope/exit_cfunc_scope (these are for
2121
      sanity checking and forward compatibility). Created insertion points
2122
      looses this scope and cannot access it.
2123
    - marker: Not copied to insertion point
2124
    - filename_table, filename_list, input_file_contents: All codewriters
2125
      coming from the same root share the same instances simultaneously.
2126
    """
2127

2128
    # f                   file            output file
2129
    # buffer              StringIOTree
2130

2131
    # level               int             indentation level
2132
    # bol                 bool            beginning of line?
2133
    # marker              string          comment to emit before next line
2134
    # funcstate           FunctionState   contains state local to a C function used for code
2135
    #                                     generation (labels and temps state etc.)
2136
    # globalstate         GlobalState     contains state global for a C file (input file info,
2137
    #                                     utility code, declared constants etc.)
2138
    # pyclass_stack       list            used during recursive code generation to pass information
2139
    #                                     about the current class one is in
2140
    # code_config         CCodeConfig     configuration options for the C code writer
2141

2142
    @cython.locals(create_from='CCodeWriter')
2143
    def __init__(self, create_from=None, buffer=None, copy_formatting=False):
2144
        if buffer is None: buffer = StringIOTree()
2145
        self.buffer = buffer
2146
        self.last_pos = None
2147
        self.last_marked_pos = None
2148
        self.pyclass_stack = []
2149

2150
        self.funcstate = None
2151
        self.globalstate = None
2152
        self.code_config = None
2153
        self.level = 0
2154
        self.call_level = 0
2155
        self.bol = 1
2156

2157
        if create_from is not None:
2158
            # Use same global state
2159
            self.set_global_state(create_from.globalstate)
2160
            self.funcstate = create_from.funcstate
2161
            # Clone formatting state
2162
            if copy_formatting:
2163
                self.level = create_from.level
2164
                self.bol = create_from.bol
2165
                self.call_level = create_from.call_level
2166
            self.last_pos = create_from.last_pos
2167
            self.last_marked_pos = create_from.last_marked_pos
2168

2169
    def create_new(self, create_from, buffer, copy_formatting):
2170
        # polymorphic constructor -- very slightly more versatile
2171
        # than using __class__
2172
        result = CCodeWriter(create_from, buffer, copy_formatting)
2173
        return result
2174

2175
    def set_global_state(self, global_state):
2176
        assert self.globalstate is None  # prevent overwriting once it's set
2177
        self.globalstate = global_state
2178
        self.code_config = global_state.code_config
2179

2180
    def copyto(self, f):
2181
        self.buffer.copyto(f)
2182

2183
    def getvalue(self):
2184
        return self.buffer.getvalue()
2185

2186
    def write(self, s):
2187
        if '\n' in s:
2188
            self._write_lines(s)
2189
        else:
2190
            self._write_to_buffer(s)
2191

2192
    def _write_lines(self, s):
2193
        # Cygdb needs to know which Cython source line corresponds to which C line.
2194
        # Therefore, we write this information into "self.buffer.markers" and then write it from there
2195
        # into cython_debug/cython_debug_info_* (see ModuleNode._serialize_lineno_map).
2196
        filename_line = self.last_marked_pos[:2] if self.last_marked_pos else (None, 0)
2197
        self.buffer.markers.extend([filename_line] * s.count('\n'))
2198

2199
        self._write_to_buffer(s)
2200

2201
    def _write_to_buffer(self, s):
2202
        self.buffer.write(s)
2203

2204
    def insertion_point(self):
2205
        other = self.create_new(create_from=self, buffer=self.buffer.insertion_point(), copy_formatting=True)
2206
        return other
2207

2208
    def new_writer(self):
2209
        """
2210
        Creates a new CCodeWriter connected to the same global state, which
2211
        can later be inserted using insert.
2212
        """
2213
        return CCodeWriter(create_from=self)
2214

2215
    def insert(self, writer):
2216
        """
2217
        Inserts the contents of another code writer (created with
2218
        the same global state) in the current location.
2219

2220
        It is ok to write to the inserted writer also after insertion.
2221
        """
2222
        assert writer.globalstate is self.globalstate
2223
        self.buffer.insert(writer.buffer)
2224

2225
    # Properties delegated to function scope
2226
    @funccontext_property
2227
    def label_counter(self): pass
2228
    @funccontext_property
2229
    def return_label(self): pass
2230
    @funccontext_property
2231
    def error_label(self): pass
2232
    @funccontext_property
2233
    def labels_used(self): pass
2234
    @funccontext_property
2235
    def continue_label(self): pass
2236
    @funccontext_property
2237
    def break_label(self): pass
2238
    @funccontext_property
2239
    def return_from_error_cleanup_label(self): pass
2240
    @funccontext_property
2241
    def yield_labels(self): pass
2242

2243
    def label_interceptor(self, new_labels, orig_labels, skip_to_label=None, pos=None, trace=True):
2244
        """
2245
        Helper for generating multiple label interceptor code blocks.
2246

2247
        @param new_labels: the new labels that should be intercepted
2248
        @param orig_labels: the original labels that we should dispatch to after the interception
2249
        @param skip_to_label: a label to skip to before starting the code blocks
2250
        @param pos: the node position to mark for each interceptor block
2251
        @param trace: add a trace line for the pos marker or not
2252
        """
2253
        for label, orig_label in zip(new_labels, orig_labels):
2254
            if not self.label_used(label):
2255
                continue
2256
            if skip_to_label:
2257
                # jump over the whole interception block
2258
                self.put_goto(skip_to_label)
2259
                skip_to_label = None
2260

2261
            if pos is not None:
2262
                self.mark_pos(pos, trace=trace)
2263
            self.put_label(label)
2264
            yield (label, orig_label)
2265
            self.put_goto(orig_label)
2266

2267
    # Functions delegated to function scope
2268
    def new_label(self, name=None):    return self.funcstate.new_label(name)
2269
    def new_error_label(self, *args):  return self.funcstate.new_error_label(*args)
2270
    def new_yield_label(self, *args):  return self.funcstate.new_yield_label(*args)
2271
    def get_loop_labels(self):         return self.funcstate.get_loop_labels()
2272
    def set_loop_labels(self, labels): return self.funcstate.set_loop_labels(labels)
2273
    def new_loop_labels(self, *args):  return self.funcstate.new_loop_labels(*args)
2274
    def get_all_labels(self):          return self.funcstate.get_all_labels()
2275
    def set_all_labels(self, labels):  return self.funcstate.set_all_labels(labels)
2276
    def all_new_labels(self):          return self.funcstate.all_new_labels()
2277
    def use_label(self, lbl):          return self.funcstate.use_label(lbl)
2278
    def label_used(self, lbl):         return self.funcstate.label_used(lbl)
2279

2280

2281
    def enter_cfunc_scope(self, scope=None):
2282
        self.funcstate = FunctionState(self, scope=scope)
2283

2284
    def exit_cfunc_scope(self):
2285
        self.funcstate.validate_exit()
2286
        self.funcstate = None
2287

2288
    def start_initcfunc(self, signature, scope=None, refnanny=False):
2289
        """
2290
        Init code helper function to start a cfunc scope and generate
2291
        the prototype and function header ("static SIG {") of the function.
2292
        """
2293
        proto = self.globalstate.parts['initfunc_declarations']
2294
        proto.putln(f"static CYTHON_SMALL_CODE {signature}; /*proto*/")
2295
        self.enter_cfunc_scope(scope)
2296
        self.putln("")
2297
        self.putln(f"static {signature} {{")
2298
        if refnanny:
2299
            self.put_declare_refcount_context()
2300

2301
    # constant handling
2302

2303
    def get_py_int(self, str_value, longness):
2304
        return self.globalstate.get_int_const(str_value, longness).cname
2305

2306
    def get_py_float(self, str_value, value_code):
2307
        return self.globalstate.get_float_const(str_value, value_code).cname
2308

2309
    def get_py_const(self, prefix, dedup_key=None):
2310
        return self.globalstate.get_py_const(prefix, dedup_key)
2311

2312
    def get_string_const(self, text):
2313
        return self.globalstate.get_string_const(text).cname
2314

2315
    def get_pyunicode_ptr_const(self, text):
2316
        return self.globalstate.get_pyunicode_ptr_const(text)
2317

2318
    def get_py_string_const(self, text, identifier=None):
2319
        return self.globalstate.get_py_string_const(text, identifier).cname
2320

2321
    def get_py_codeobj_const(self, node):
2322
        return self.globalstate.get_py_codeobj_const(node)
2323

2324
    def get_argument_default_const(self, type):
2325
        return self.globalstate.get_argument_default_const(type).cname
2326

2327
    def intern(self, text):
2328
        return self.get_py_string_const(text)
2329

2330
    def intern_identifier(self, text):
2331
        return self.get_py_string_const(text, identifier=True)
2332

2333
    def get_cached_constants_writer(self, target=None):
2334
        return self.globalstate.get_cached_constants_writer(target)
2335

2336
    # code generation
2337

2338
    def putln(self, code="", safe=False):
2339
        if self.last_pos and self.bol:
2340
            self.emit_marker()
2341
        if self.code_config.emit_linenums and self.last_marked_pos:
2342
            source_desc, line, _ = self.last_marked_pos
2343
            self._write_lines(f'\n#line {line} "{source_desc.get_escaped_description()}"\n')
2344
        if code:
2345
            if safe:
2346
                self.put_safe(code)
2347
            else:
2348
                self.put(code)
2349
        self._write_lines("\n")
2350
        self.bol = 1
2351

2352
    def mark_pos(self, pos, trace=True):
2353
        if pos is None:
2354
            return
2355
        if self.last_marked_pos and self.last_marked_pos[:2] == pos[:2]:
2356
            return
2357
        self.last_pos = (pos, trace)
2358

2359
    def emit_marker(self):
2360
        pos, trace = self.last_pos
2361
        self.last_marked_pos = pos
2362
        self.last_pos = None
2363
        self._write_lines("\n")
2364
        if self.code_config.emit_code_comments:
2365
            self.indent()
2366
            self._write_lines(self._build_marker(pos))
2367
        if trace:
2368
            self.write_trace_line(pos)
2369

2370
    def write_trace_line(self, pos):
2371
        if self.funcstate and self.funcstate.can_trace and self.globalstate.directives['linetrace']:
2372
            self.indent()
2373
            self._write_lines(
2374
                f'__Pyx_TraceLine({pos[1]:d},{self.pos_to_offset(pos):d},{not self.funcstate.gil_owned:d},{self.error_goto(pos)})\n')
2375

2376
    def _build_marker(self, pos):
2377
        source_desc, line, col = pos
2378
        assert isinstance(source_desc, SourceDescriptor)
2379
        contents = self.globalstate.commented_file_contents(source_desc)
2380
        lines = contents[max(0, line-3):line]  # line numbers start at 1
2381
        lines[-1] += '             # <<<<<<<<<<<<<<'
2382
        lines += contents[line:line+2]
2383
        code = "\n".join(lines)
2384
        return f'/* "{source_desc.get_escaped_description()}":{line:d}\n{code}\n*/\n'
2385

2386
    def put_safe(self, code):
2387
        # put code, but ignore {}
2388
        self.write(code)
2389
        self.bol = 0
2390

2391
    def put_or_include(self, code, name):
2392
        include_dir = self.globalstate.common_utility_include_dir
2393
        if include_dir and len(code) > 1024:
2394
            hash = hashlib.sha256(code.encode('utf8')).hexdigest()
2395
            include_file = f"{name}_{hash}.h"
2396
            path = os.path.join(include_dir, include_file)
2397
            if not os.path.exists(path):
2398
                tmp_path = f'{path}.tmp{os.getpid()}'
2399
                with Utils.open_new_file(tmp_path) as f:
2400
                    f.write(code)
2401
                shutil.move(tmp_path, path)
2402
            # We use forward slashes in the include path to assure identical code generation
2403
            # under Windows and Posix.  C/C++ compilers should still understand it.
2404
            c_path = path.replace('\\', '/')
2405
            code = f'#include "{c_path}"\n'
2406
        self.put(code)
2407

2408
    def put(self, code):
2409
        fix_indent = False
2410
        if "{" in code:
2411
            dl = code.count("{")
2412
        else:
2413
            dl = 0
2414
        if "}" in code:
2415
            dl -= code.count("}")
2416
            if dl < 0:
2417
                self.level += dl
2418
            elif dl == 0 and code[0] == "}":
2419
                # special cases like "} else {" need a temporary dedent
2420
                fix_indent = True
2421
                self.level -= 1
2422
        if self.bol:
2423
            self.indent()
2424
        self.write(code)
2425
        self.bol = 0
2426
        if dl > 0:
2427
            self.level += dl
2428
        elif fix_indent:
2429
            self.level += 1
2430

2431
    def increase_indent(self):
2432
        self.level += 1
2433

2434
    def decrease_indent(self):
2435
        self.level -= 1
2436

2437
    def begin_block(self):
2438
        self.putln("{")
2439
        self.increase_indent()
2440

2441
    def end_block(self):
2442
        self.decrease_indent()
2443
        self.putln("}")
2444

2445
    def indent(self):
2446
        self._write_to_buffer("  " * self.level)
2447

2448
    def get_py_version_hex(self, pyversion):
2449
        return "0x%02X%02X%02X%02X" % (tuple(pyversion) + (0,0,0,0))[:4]
2450

2451
    def put_label(self, lbl):
2452
        if lbl in self.funcstate.labels_used:
2453
            self.putln("%s:;" % lbl)
2454

2455
    def put_goto(self, lbl):
2456
        self.funcstate.use_label(lbl)
2457
        self.putln("goto %s;" % lbl)
2458

2459
    def put_var_declaration(self, entry, storage_class="",
2460
                            dll_linkage=None, definition=True):
2461
        #print "Code.put_var_declaration:", entry.name, "definition =", definition ###
2462
        if entry.visibility == 'private' and not (definition or entry.defined_in_pxd):
2463
            #print "...private and not definition, skipping", entry.cname ###
2464
            return
2465
        if entry.visibility == "private" and not entry.used:
2466
            #print "...private and not used, skipping", entry.cname ###
2467
            return
2468
        if not entry.cf_used:
2469
            self.put('CYTHON_UNUSED ')
2470
        if storage_class:
2471
            self.put("%s " % storage_class)
2472
        if entry.is_cpp_optional:
2473
            self.put(entry.type.cpp_optional_declaration_code(
2474
                entry.cname, dll_linkage=dll_linkage))
2475
        else:
2476
            self.put(entry.type.declaration_code(
2477
                entry.cname, dll_linkage=dll_linkage))
2478
        if entry.init is not None:
2479
            self.put_safe(" = %s" % entry.type.literal_code(entry.init))
2480
        elif entry.type.is_pyobject:
2481
            self.put(" = NULL")
2482
        self.putln(";")
2483
        self.funcstate.scope.use_entry_utility_code(entry)
2484

2485
    def put_temp_declarations(self, func_context):
2486
        for name, type, manage_ref, static in func_context.temps_allocated:
2487
            if type.is_cpp_class and not type.is_fake_reference and func_context.scope.directives['cpp_locals']:
2488
                decl = type.cpp_optional_declaration_code(name)
2489
            else:
2490
                decl = type.declaration_code(name)
2491
            if type.is_pyobject:
2492
                self.putln("%s = NULL;" % decl)
2493
            elif type.is_memoryviewslice:
2494
                self.putln("%s = %s;" % (decl, type.literal_code(type.default_value)))
2495
            else:
2496
                self.putln("%s%s;" % (static and "static " or "", decl))
2497

2498
        if func_context.should_declare_error_indicator:
2499
            if self.funcstate.uses_error_indicator:
2500
                unused = ''
2501
            else:
2502
                unused = 'CYTHON_UNUSED '
2503
            # Initialize these variables to silence compiler warnings
2504
            self.putln("%sint %s = 0;" % (unused, Naming.lineno_cname))
2505
            self.putln("%sconst char *%s = NULL;" % (unused, Naming.filename_cname))
2506
            self.putln("%sint %s = 0;" % (unused, Naming.clineno_cname))
2507

2508
    def put_generated_by(self):
2509
        self.putln(Utils.GENERATED_BY_MARKER)
2510
        self.putln("")
2511

2512
    def put_h_guard(self, guard):
2513
        self.putln("#ifndef %s" % guard)
2514
        self.putln("#define %s" % guard)
2515

2516
    def unlikely(self, cond):
2517
        if Options.gcc_branch_hints:
2518
            return 'unlikely(%s)' % cond
2519
        else:
2520
            return cond
2521

2522
    def build_function_modifiers(self, modifiers, mapper=modifier_output_mapper):
2523
        if not modifiers:
2524
            return ''
2525
        return '%s ' % ' '.join([mapper(m,m) for m in modifiers])
2526

2527
    # Python objects and reference counting
2528

2529
    def entry_as_pyobject(self, entry):
2530
        type = entry.type
2531
        if (not entry.is_self_arg and not entry.type.is_complete()
2532
                or entry.type.is_extension_type):
2533
            return "(PyObject *)" + entry.cname
2534
        else:
2535
            return entry.cname
2536

2537
    def as_pyobject(self, cname, type):
2538
        from .PyrexTypes import py_object_type, typecast
2539
        return typecast(py_object_type, type, cname)
2540

2541
    def put_gotref(self, cname, type):
2542
        type.generate_gotref(self, cname)
2543

2544
    def put_giveref(self, cname, type):
2545
        type.generate_giveref(self, cname)
2546

2547
    def put_xgiveref(self, cname, type):
2548
        type.generate_xgiveref(self, cname)
2549

2550
    def put_xgotref(self, cname, type):
2551
        type.generate_xgotref(self, cname)
2552

2553
    def put_incref(self, cname, type, nanny=True):
2554
        # Note: original put_Memslice_Incref/Decref also added in some utility code
2555
        # this is unnecessary since the relevant utility code is loaded anyway if a memoryview is used
2556
        # and so has been removed. However, it's potentially a feature that might be useful here
2557
        type.generate_incref(self, cname, nanny=nanny)
2558

2559
    def put_xincref(self, cname, type, nanny=True):
2560
        type.generate_xincref(self, cname, nanny=nanny)
2561

2562
    def put_decref(self, cname, type, nanny=True, have_gil=True):
2563
        type.generate_decref(self, cname, nanny=nanny, have_gil=have_gil)
2564

2565
    def put_xdecref(self, cname, type, nanny=True, have_gil=True):
2566
        type.generate_xdecref(self, cname, nanny=nanny, have_gil=have_gil)
2567

2568
    def put_decref_clear(self, cname, type, clear_before_decref=False, nanny=True, have_gil=True):
2569
        type.generate_decref_clear(self, cname, clear_before_decref=clear_before_decref,
2570
                              nanny=nanny, have_gil=have_gil)
2571

2572
    def put_xdecref_clear(self, cname, type, clear_before_decref=False, nanny=True, have_gil=True):
2573
        type.generate_xdecref_clear(self, cname, clear_before_decref=clear_before_decref,
2574
                              nanny=nanny, have_gil=have_gil)
2575

2576
    def put_decref_set(self, cname, type, rhs_cname):
2577
        type.generate_decref_set(self, cname, rhs_cname)
2578

2579
    def put_xdecref_set(self, cname, type, rhs_cname):
2580
        type.generate_xdecref_set(self, cname, rhs_cname)
2581

2582
    def put_incref_memoryviewslice(self, slice_cname, type, have_gil):
2583
        # TODO ideally this would just be merged into "put_incref"
2584
        type.generate_incref_memoryviewslice(self, slice_cname, have_gil=have_gil)
2585

2586
    def put_var_incref_memoryviewslice(self, entry, have_gil):
2587
        self.put_incref_memoryviewslice(entry.cname, entry.type, have_gil=have_gil)
2588

2589
    def put_var_gotref(self, entry):
2590
        self.put_gotref(entry.cname, entry.type)
2591

2592
    def put_var_giveref(self, entry):
2593
        self.put_giveref(entry.cname, entry.type)
2594

2595
    def put_var_xgotref(self, entry):
2596
        self.put_xgotref(entry.cname, entry.type)
2597

2598
    def put_var_xgiveref(self, entry):
2599
        self.put_xgiveref(entry.cname, entry.type)
2600

2601
    def put_var_incref(self, entry, **kwds):
2602
        self.put_incref(entry.cname, entry.type, **kwds)
2603

2604
    def put_var_xincref(self, entry, **kwds):
2605
        self.put_xincref(entry.cname, entry.type, **kwds)
2606

2607
    def put_var_decref(self, entry, **kwds):
2608
        self.put_decref(entry.cname, entry.type, **kwds)
2609

2610
    def put_var_xdecref(self, entry, **kwds):
2611
        self.put_xdecref(entry.cname, entry.type, **kwds)
2612

2613
    def put_var_decref_clear(self, entry, **kwds):
2614
        self.put_decref_clear(entry.cname, entry.type, clear_before_decref=entry.in_closure, **kwds)
2615

2616
    def put_var_decref_set(self, entry, rhs_cname, **kwds):
2617
        self.put_decref_set(entry.cname, entry.type, rhs_cname, **kwds)
2618

2619
    def put_var_xdecref_set(self, entry, rhs_cname, **kwds):
2620
        self.put_xdecref_set(entry.cname, entry.type, rhs_cname, **kwds)
2621

2622
    def put_var_xdecref_clear(self, entry, **kwds):
2623
        self.put_xdecref_clear(entry.cname, entry.type, clear_before_decref=entry.in_closure, **kwds)
2624

2625
    def put_var_decrefs(self, entries, used_only = 0):
2626
        for entry in entries:
2627
            if not used_only or entry.used:
2628
                if entry.xdecref_cleanup:
2629
                    self.put_var_xdecref(entry)
2630
                else:
2631
                    self.put_var_decref(entry)
2632

2633
    def put_var_xdecrefs(self, entries):
2634
        for entry in entries:
2635
            self.put_var_xdecref(entry)
2636

2637
    def put_var_xdecrefs_clear(self, entries):
2638
        for entry in entries:
2639
            self.put_var_xdecref_clear(entry)
2640

2641
    def put_init_to_py_none(self, cname, type, nanny=True):
2642
        from .PyrexTypes import py_object_type, typecast
2643
        py_none = typecast(type, py_object_type, "Py_None")
2644
        if nanny:
2645
            self.putln("%s = %s; __Pyx_INCREF(Py_None);" % (cname, py_none))
2646
        else:
2647
            self.putln("%s = %s; Py_INCREF(Py_None);" % (cname, py_none))
2648

2649
    def put_init_var_to_py_none(self, entry, template = "%s", nanny=True):
2650
        code = template % entry.cname
2651
        #if entry.type.is_extension_type:
2652
        #    code = "((PyObject*)%s)" % code
2653
        self.put_init_to_py_none(code, entry.type, nanny)
2654
        if entry.in_closure:
2655
            self.put_giveref('Py_None')
2656

2657
    def put_pymethoddef(self, entry, term, allow_skip=True, wrapper_code_writer=None):
2658
        is_number_slot = False
2659
        if entry.is_special or entry.name == '__getattribute__':
2660
            from . import TypeSlots
2661
            if entry.name not in special_py_methods:
2662
                if TypeSlots.is_binop_number_slot(entry.name):
2663
                    # It's useful if numeric binops are created with meth coexist
2664
                    # so they can be called directly by looking up the name, skipping the
2665
                    # dispatch wrapper that enables the reverse slots.  This is most useful
2666
                    # when c_api_binop_methods is False, but there's no reason not to do it
2667
                    # all the time
2668
                    is_number_slot = True
2669
                elif entry.name == '__getattr__' and not self.globalstate.directives['fast_getattr']:
2670
                    pass
2671
                # Python's typeobject.c will automatically fill in our slot
2672
                # in add_operators() (called by PyType_Ready) with a value
2673
                # that's better than ours.
2674
                elif allow_skip:
2675
                    return
2676

2677
        method_flags = entry.signature.method_flags()
2678
        if not method_flags:
2679
            return
2680
        if entry.is_special:
2681
            method_flags += [TypeSlots.method_coexist]
2682
        func_ptr = wrapper_code_writer.put_pymethoddef_wrapper(entry) if wrapper_code_writer else entry.func_cname
2683
        # Add required casts, but try not to shadow real warnings.
2684
        cast = entry.signature.method_function_type()
2685
        if cast != 'PyCFunction':
2686
            func_ptr = '(void*)(%s)%s' % (cast, func_ptr)
2687
        entry_name = entry.name.as_c_string_literal()
2688
        if is_number_slot:
2689
            # Unlike most special functions, binop numeric operator slots are actually generated here
2690
            # (to ensure that they can be looked up). However, they're sometimes guarded by the preprocessor
2691
            # so a bit of extra logic is needed
2692
            slot = TypeSlots.get_slot_table(self.globalstate.directives).get_slot_by_method_name(entry.name)
2693
            preproc_guard = slot.preprocessor_guard_code()
2694
            if preproc_guard:
2695
                self.putln(preproc_guard)
2696
        self.putln(
2697
            '{%s, (PyCFunction)%s, %s, %s}%s' % (
2698
                entry_name,
2699
                func_ptr,
2700
                "|".join(method_flags),
2701
                entry.doc_cname if entry.doc else '0',
2702
                term))
2703
        if is_number_slot and preproc_guard:
2704
            self.putln("#endif")
2705

2706
    def put_pymethoddef_wrapper(self, entry):
2707
        func_cname = entry.func_cname
2708
        if entry.is_special:
2709
            method_flags = entry.signature.method_flags() or []
2710
            from .TypeSlots import method_noargs
2711
            if method_noargs in method_flags:
2712
                # Special NOARGS methods really take no arguments besides 'self', but PyCFunction expects one.
2713
                func_cname = Naming.method_wrapper_prefix + func_cname
2714
                self.putln("static PyObject *%s(PyObject *self, CYTHON_UNUSED PyObject *arg) {" % func_cname)
2715
                func_call = "%s(self)" % entry.func_cname
2716
                if entry.name == "__next__":
2717
                    self.putln("PyObject *res = %s;" % func_call)
2718
                    # tp_iternext can return NULL without an exception
2719
                    self.putln("if (!res && !PyErr_Occurred()) { PyErr_SetNone(PyExc_StopIteration); }")
2720
                    self.putln("return res;")
2721
                else:
2722
                    self.putln("return %s;" % func_call)
2723
                self.putln("}")
2724
        return func_cname
2725

2726
    # GIL methods
2727

2728
    def use_fast_gil_utility_code(self):
2729
        if self.globalstate.directives['fast_gil']:
2730
            self.globalstate.use_utility_code(UtilityCode.load_cached("FastGil", "ModuleSetupCode.c"))
2731
        else:
2732
            self.globalstate.use_utility_code(UtilityCode.load_cached("NoFastGil", "ModuleSetupCode.c"))
2733

2734
    def put_ensure_gil(self, declare_gilstate=True, variable=None):
2735
        """
2736
        Acquire the GIL. The generated code is safe even when no PyThreadState
2737
        has been allocated for this thread (for threads not initialized by
2738
        using the Python API). Additionally, the code generated by this method
2739
        may be called recursively.
2740
        """
2741
        self.globalstate.use_utility_code(
2742
            UtilityCode.load_cached("ForceInitThreads", "ModuleSetupCode.c"))
2743
        self.use_fast_gil_utility_code()
2744
        if not variable:
2745
            variable = '__pyx_gilstate_save'
2746
            if declare_gilstate:
2747
                self.put("PyGILState_STATE ")
2748
        self.putln("%s = __Pyx_PyGILState_Ensure();" % variable)
2749

2750
    def put_release_ensured_gil(self, variable=None):
2751
        """
2752
        Releases the GIL, corresponds to `put_ensure_gil`.
2753
        """
2754
        self.use_fast_gil_utility_code()
2755
        if not variable:
2756
            variable = '__pyx_gilstate_save'
2757
        self.putln("__Pyx_PyGILState_Release(%s);" % variable)
2758

2759
    def put_acquire_freethreading_lock(self):
2760
        self.globalstate.use_utility_code(
2761
            UtilityCode.load_cached("AccessPyMutexForFreeThreading", "ModuleSetupCode.c"))
2762
        self.putln("#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING")
2763
        self.putln(f"PyMutex_Lock(&{Naming.parallel_freethreading_mutex});")
2764
        self.putln("#endif")
2765

2766
    def put_release_freethreading_lock(self):
2767
        self.putln("#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING")
2768
        self.putln(f"PyMutex_Unlock(&{Naming.parallel_freethreading_mutex});")
2769
        self.putln("#endif")
2770

2771
    def put_acquire_gil(self, variable=None, unknown_gil_state=True):
2772
        """
2773
        Acquire the GIL. The thread's thread state must have been initialized
2774
        by a previous `put_release_gil`
2775
        """
2776
        self.use_fast_gil_utility_code()
2777
        self.putln("__Pyx_FastGIL_Forget();")
2778
        if variable:
2779
            self.putln('_save = %s;' % variable)
2780
        if unknown_gil_state:
2781
            self.putln("if (_save) {")
2782
        self.putln("Py_BLOCK_THREADS")
2783
        if unknown_gil_state:
2784
            self.putln("}")
2785

2786
    def put_release_gil(self, variable=None, unknown_gil_state=True):
2787
        "Release the GIL, corresponds to `put_acquire_gil`."
2788
        self.use_fast_gil_utility_code()
2789
        self.putln("PyThreadState *_save;")
2790
        self.putln("_save = NULL;")
2791
        if unknown_gil_state:
2792
            # we don't *know* that we don't have the GIL (since we may be inside a nogil function,
2793
            # and Py_UNBLOCK_THREADS is unsafe without the GIL)
2794
            self.putln("if (PyGILState_Check()) {")
2795
        self.putln("Py_UNBLOCK_THREADS")
2796
        if unknown_gil_state:
2797
            self.putln("}")
2798
        if variable:
2799
            self.putln('%s = _save;' % variable)
2800
        self.putln("__Pyx_FastGIL_Remember();")
2801

2802
    def declare_gilstate(self):
2803
        self.putln("PyGILState_STATE __pyx_gilstate_save;")
2804

2805
    # error handling
2806

2807
    def put_error_if_neg(self, pos, value):
2808
        # TODO this path is almost _never_ taken, yet this macro makes is slower!
2809
        # return self.putln("if (unlikely(%s < 0)) %s" % (value, self.error_goto(pos)))
2810
        return self.putln("if (%s < 0) %s" % (value, self.error_goto(pos)))
2811

2812
    def put_error_if_unbound(self, pos, entry, in_nogil_context=False, unbound_check_code=None):
2813
        if entry.from_closure:
2814
            func = '__Pyx_RaiseClosureNameError'
2815
            self.globalstate.use_utility_code(
2816
                UtilityCode.load_cached("RaiseClosureNameError", "ObjectHandling.c"))
2817
        elif entry.type.is_memoryviewslice and in_nogil_context:
2818
            func = '__Pyx_RaiseUnboundMemoryviewSliceNogil'
2819
            self.globalstate.use_utility_code(
2820
                UtilityCode.load_cached("RaiseUnboundMemoryviewSliceNogil", "ObjectHandling.c"))
2821
        elif entry.type.is_cpp_class and entry.is_cglobal:
2822
            func = '__Pyx_RaiseCppGlobalNameError'
2823
            self.globalstate.use_utility_code(
2824
                UtilityCode.load_cached("RaiseCppGlobalNameError", "ObjectHandling.c"))
2825
        elif entry.type.is_cpp_class and entry.is_variable and not entry.is_member and entry.scope.is_c_class_scope:
2826
            # there doesn't seem to be a good way to detecting an instance-attribute of a C class
2827
            # (is_member is only set for class attributes)
2828
            func = '__Pyx_RaiseCppAttributeError'
2829
            self.globalstate.use_utility_code(
2830
                UtilityCode.load_cached("RaiseCppAttributeError", "ObjectHandling.c"))
2831
        else:
2832
            func = '__Pyx_RaiseUnboundLocalError'
2833
            self.globalstate.use_utility_code(
2834
                UtilityCode.load_cached("RaiseUnboundLocalError", "ObjectHandling.c"))
2835

2836
        if not unbound_check_code:
2837
            unbound_check_code = entry.type.check_for_null_code(entry.cname)
2838
        self.putln('if (unlikely(!%s)) { %s("%s"); %s }' % (
2839
                                unbound_check_code,
2840
                                func,
2841
                                entry.name,
2842
                                self.error_goto(pos)))
2843

2844
    def set_error_info(self, pos, used=False):
2845
        self.funcstate.should_declare_error_indicator = True
2846
        if used:
2847
            self.funcstate.uses_error_indicator = True
2848
        return "__PYX_MARK_ERR_POS(%s, %s)" % (
2849
            self.lookup_filename(pos[0]),
2850
            pos[1])
2851

2852
    def error_goto(self, pos, used=True):
2853
        lbl = self.funcstate.error_label
2854
        self.funcstate.use_label(lbl)
2855
        if pos is None:
2856
            return 'goto %s;' % lbl
2857
        self.funcstate.should_declare_error_indicator = True
2858
        if used:
2859
            self.funcstate.uses_error_indicator = True
2860
        return "__PYX_ERR(%s, %s, %s)" % (
2861
            self.lookup_filename(pos[0]),
2862
            pos[1],
2863
            lbl)
2864

2865
    def error_goto_if(self, cond, pos):
2866
        return "if (%s) %s" % (self.unlikely(cond), self.error_goto(pos))
2867

2868
    def error_goto_if_null(self, cname, pos):
2869
        return self.error_goto_if("!%s" % cname, pos)
2870

2871
    def error_goto_if_neg(self, cname, pos):
2872
        # Add extra parentheses to silence clang warnings about constant conditions.
2873
        return self.error_goto_if("(%s < 0)" % cname, pos)
2874

2875
    def error_goto_if_PyErr(self, pos):
2876
        return self.error_goto_if("PyErr_Occurred()", pos)
2877

2878
    def lookup_filename(self, filename):
2879
        return self.globalstate.lookup_filename(filename)
2880

2881
    def put_declare_refcount_context(self):
2882
        self.putln('__Pyx_RefNannyDeclarations')
2883

2884
    def put_setup_refcount_context(self, name, acquire_gil=False):
2885
        name = name.as_c_string_literal()  # handle unicode names
2886
        if acquire_gil:
2887
            self.globalstate.use_utility_code(
2888
                UtilityCode.load_cached("ForceInitThreads", "ModuleSetupCode.c"))
2889
        self.putln('__Pyx_RefNannySetupContext(%s, %d);' % (name, acquire_gil and 1 or 0))
2890

2891
    def put_finish_refcount_context(self, nogil=False):
2892
        self.putln("__Pyx_RefNannyFinishContextNogil()" if nogil else "__Pyx_RefNannyFinishContext();")
2893

2894
    def put_add_traceback(self, qualified_name, include_cline=True):
2895
        """
2896
        Build a Python traceback for propagating exceptions.
2897

2898
        qualified_name should be the qualified name of the function.
2899
        """
2900
        qualified_name = qualified_name.as_c_string_literal()  # handle unicode names
2901
        format_tuple = (
2902
            qualified_name,
2903
            Naming.clineno_cname if include_cline else 0,
2904
            Naming.lineno_cname,
2905
            Naming.filename_cname,
2906
        )
2907

2908
        self.funcstate.uses_error_indicator = True
2909
        self.putln('__Pyx_AddTraceback(%s, %s, %s, %s);' % format_tuple)
2910

2911
    def put_unraisable(self, qualified_name, nogil=False):
2912
        """
2913
        Generate code to print a Python warning for an unraisable exception.
2914

2915
        qualified_name should be the qualified name of the function.
2916
        """
2917
        format_tuple = (
2918
            qualified_name,
2919
            Naming.clineno_cname,
2920
            Naming.lineno_cname,
2921
            Naming.filename_cname,
2922
            self.globalstate.directives['unraisable_tracebacks'],
2923
            nogil,
2924
        )
2925
        self.funcstate.uses_error_indicator = True
2926
        self.putln('__Pyx_WriteUnraisable("%s", %s, %s, %s, %d, %d);' % format_tuple)
2927
        self.globalstate.use_utility_code(
2928
            UtilityCode.load_cached("WriteUnraisableException", "Exceptions.c"))
2929

2930
    def is_tracing(self):
2931
        return self.globalstate.directives['profile'] or self.globalstate.directives['linetrace']
2932

2933
    def pos_to_offset(self, pos):
2934
        """
2935
        Calculate a fake 'instruction offset' from a node position as 31 bit int (32 bit signed).
2936
        """
2937
        scope = self.funcstate.scope
2938
        while scope and pos not in scope.node_positions_to_offset:
2939
            scope = scope.parent_scope
2940
        return scope.node_positions_to_offset[pos] if scope else 0
2941

2942
    def put_trace_declarations(self, is_generator=False):
2943
        self.putln('__Pyx_TraceDeclarationsGen' if is_generator else '__Pyx_TraceDeclarationsFunc')
2944

2945
    def put_trace_frame_init(self, codeobj=None):
2946
        if codeobj:
2947
            self.putln('__Pyx_TraceFrameInit(%s)' % codeobj)
2948

2949
    def put_trace_start(self, name, pos, nogil=False, is_generator=False, is_cpdef_func=False):
2950
        trace_func = "__Pyx_TraceStartGen" if is_generator else "__Pyx_TraceStartFunc"
2951
        self.putln(
2952
            f'{trace_func}('
2953
            f'{name.as_c_string_literal()}, '
2954
            f'{Naming.filetable_cname}[{self.lookup_filename(pos[0])}], '
2955
            f'{pos[1]}, '
2956
            f'{self.pos_to_offset(pos):d}, '
2957
            f'{nogil:d}, '
2958
            f'{Naming.skip_dispatch_cname if is_cpdef_func else "0"}, '
2959
            f'{self.error_goto(pos)}'
2960
            ');'
2961
        )
2962

2963
    def put_trace_exit(self):
2964
        self.putln("__Pyx_PyMonitoring_ExitScope();")
2965

2966
    def put_trace_yield(self, retvalue_cname, pos):
2967
        error_goto = self.error_goto(pos)
2968
        self.putln(f"__Pyx_TraceYield({retvalue_cname}, {self.pos_to_offset(pos)}, {error_goto});")
2969

2970
    def put_trace_resume(self, pos):
2971
        scope = self.funcstate.scope
2972
        # pos[1] is probably not the first line, so try to find the first line of the generator function.
2973
        first_line = scope.scope_class.pos[1] if scope.scope_class else pos[1]
2974
        name = scope.name.as_c_string_literal()
2975
        filename_index = self.lookup_filename(pos[0])
2976
        error_goto = self.error_goto(pos)
2977
        self.putln(
2978
            '__Pyx_TraceResumeGen('
2979
            f'{name}, '
2980
            f'{Naming.filetable_cname}[{filename_index}], '
2981
            f'{first_line}, '
2982
            f'{self.pos_to_offset(pos)}, '
2983
            f'{error_goto}'
2984
            ');'
2985
        )
2986

2987
    def put_trace_exception(self, pos, reraise=False, fresh=False):
2988
        self.putln(f"__Pyx_TraceException({self.pos_to_offset(pos)}, {bool(reraise):d}, {bool(fresh):d});")
2989

2990
    def put_trace_exception_propagating(self):
2991
        self.putln(f"__Pyx_TraceException({Naming.lineno_cname}, 0, 0);")
2992

2993
    def put_trace_exception_handled(self, pos):
2994
        self.putln(f"__Pyx_TraceExceptionHandled({self.pos_to_offset(pos)});")
2995

2996
    def put_trace_unwind(self, pos, nogil=False):
2997
        self.putln(f"__Pyx_TraceExceptionUnwind({self.pos_to_offset(pos)}, {bool(nogil):d});")
2998

2999
    def put_trace_stopiteration(self, pos, value):
3000
        error_goto = self.error_goto(pos)
3001
        self.putln(f"__Pyx_TraceStopIteration({value}, {self.pos_to_offset(pos)}, {error_goto});")
3002

3003
    def put_trace_return(self, retvalue_cname, pos, return_type=None, nogil=False):
3004
        extra_arg = ""
3005
        trace_func = "__Pyx_TraceReturnValue"
3006

3007
        if return_type is None or return_type.is_pyobject:
3008
            pass
3009
        elif return_type.is_void:
3010
            retvalue_cname = 'Py_None'
3011
        elif return_type.to_py_function:
3012
            trace_func = "__Pyx_TraceReturnCValue"
3013
            extra_arg = f", {return_type.to_py_function}"
3014
        else:
3015
            # We don't have a Python visible return value but we still need to report that we returned.
3016
            # 'None' may not be a misleading (it's false, for one), but it's hopefully better than nothing.
3017
            retvalue_cname = 'Py_None'
3018

3019
        error_handling = self.error_goto(pos)
3020
        self.putln(f"{trace_func}({retvalue_cname}{extra_arg}, {self.pos_to_offset(pos)}, {bool(nogil):d}, {error_handling});")
3021

3022
    def putln_openmp(self, string):
3023
        self.putln("#ifdef _OPENMP")
3024
        self.putln(string)
3025
        self.putln("#endif /* _OPENMP */")
3026

3027
    def undef_builtin_expect(self, cond):
3028
        """
3029
        Redefine the macros likely() and unlikely to no-ops, depending on
3030
        condition 'cond'
3031
        """
3032
        self.putln("#if %s" % cond)
3033
        self.putln("    #undef likely")
3034
        self.putln("    #undef unlikely")
3035
        self.putln("    #define likely(x)   (x)")
3036
        self.putln("    #define unlikely(x) (x)")
3037
        self.putln("#endif")
3038

3039
    def redef_builtin_expect(self, cond):
3040
        self.putln("#if %s" % cond)
3041
        self.putln("    #undef likely")
3042
        self.putln("    #undef unlikely")
3043
        self.putln("    #define likely(x)   __builtin_expect(!!(x), 1)")
3044
        self.putln("    #define unlikely(x) __builtin_expect(!!(x), 0)")
3045
        self.putln("#endif")
3046

3047

3048
class PyrexCodeWriter:
3049
    # f                file      output file
3050
    # level            int       indentation level
3051

3052
    def __init__(self, outfile_name):
3053
        self.f = Utils.open_new_file(outfile_name)
3054
        self.level = 0
3055

3056
    def putln(self, code):
3057
        self.f.write("%s%s\n" % (" " * self.level, code))
3058

3059
    def indent(self):
3060
        self.level += 1
3061

3062
    def dedent(self):
3063
        self.level -= 1
3064

3065

3066
class PyxCodeWriter:
3067
    """
3068
    Can be used for writing out some Cython code.
3069
    """
3070

3071
    def __init__(self, buffer=None, indent_level=0, context=None, encoding='ascii'):
3072
        self.buffer = buffer or StringIOTree()
3073
        self.level = indent_level
3074
        self.original_level = indent_level
3075
        self.context = context
3076
        self.encoding = encoding
3077
        self._insertion_points = {}
3078

3079
    def indent(self, levels=1):
3080
        self.level += levels
3081
        return True
3082

3083
    def dedent(self, levels=1):
3084
        self.level -= levels
3085

3086
    @contextmanager
3087
    def indenter(self, line):
3088
        """
3089
        with pyx_code.indenter("for i in range(10):"):
3090
            pyx_code.putln("print i")
3091
        """
3092
        self.putln(line)
3093
        self.indent()
3094
        yield
3095
        self.dedent()
3096

3097
    def empty(self):
3098
        return self.buffer.empty()
3099

3100
    def getvalue(self):
3101
        result = self.buffer.getvalue()
3102
        if isinstance(result, bytes):
3103
            result = result.decode(self.encoding)
3104
        return result
3105

3106
    def putln(self, line, context=None):
3107
        if context is None:
3108
            if self.context is not None:
3109
                context = self.context
3110
        if context is not None:
3111
            line = sub_tempita(line, context)
3112
        # Avoid indenting empty lines.
3113
        self.buffer.write(f"{self.level * '    '}{line}\n" if line else "\n")
3114

3115
    def put_chunk(self, chunk, context=None):
3116
        if context is None:
3117
            if self.context is not None:
3118
                context = self.context
3119
        if context is not None:
3120
            chunk = sub_tempita(chunk, context)
3121

3122
        chunk = _indent_chunk(chunk, self.level * 4)
3123
        self.buffer.write(chunk)
3124

3125
    def insertion_point(self):
3126
        return type(self)(self.buffer.insertion_point(), self.level, self.context)
3127

3128
    def reset(self):
3129
        # resets the buffer so that nothing gets written. Most useful
3130
        # for abandoning all work in a specific insertion point
3131
        self.buffer.reset()
3132
        self.level = self.original_level
3133

3134
    def named_insertion_point(self, name):
3135
        self._insertion_points[name] = self.insertion_point()
3136

3137
    def __getitem__(self, name):
3138
        return self._insertion_points[name]
3139

3140

3141
@cython.final
3142
@cython.ccall
3143
def _indent_chunk(chunk: str, indentation_length: cython.int) -> str:
3144
    """Normalise leading space to the intended indentation and strip empty lines.
3145
    """
3146
    assert '\t' not in chunk
3147
    lines = chunk.splitlines(keepends=True)
3148
    if not lines:
3149
        return chunk
3150
    last_line = lines[-1].rstrip(' ')
3151
    if last_line:
3152
        lines[-1] = last_line
3153
    else:
3154
        del lines[-1]
3155
        if not lines:
3156
            return '\n'
3157

3158
    # Count minimal (non-empty) indentation and strip empty lines.
3159
    min_indentation: cython.int = len(chunk) + 1
3160
    line_indentation: cython.int
3161
    line: str
3162
    i: cython.int
3163
    for i, line in enumerate(lines):
3164
        line_indentation = _count_indentation(line)
3165
        if line_indentation + 1 == len(line):
3166
            lines[i] = '\n'
3167
        elif line_indentation < min_indentation:
3168
            min_indentation = line_indentation
3169

3170
    if min_indentation > len(chunk):
3171
        # All empty lines.
3172
        min_indentation = 0
3173

3174
    if min_indentation < indentation_length:
3175
        add_indent = ' ' * (indentation_length - min_indentation)
3176
        lines = [
3177
            add_indent + line if line != '\n' else '\n'
3178
            for line in lines
3179
        ]
3180
    elif min_indentation > indentation_length:
3181
        start: cython.int = min_indentation - indentation_length
3182
        lines = [
3183
            line[start:] if line != '\n' else '\n'
3184
            for line in lines
3185
        ]
3186

3187
    return ''.join(lines)
3188

3189

3190
@cython.exceptval(-1)
3191
@cython.cfunc
3192
def _count_indentation(s: str) -> cython.int:
3193
    i: cython.int = 0
3194
    ch: cython.Py_UCS4
3195
    for i, ch in enumerate(s):
3196
        if ch != ' ':
3197
            break
3198
    return i
3199

3200

3201
class ClosureTempAllocator:
3202
    def __init__(self, klass):
3203
        self.klass = klass
3204
        self.temps_allocated = {}
3205
        self.temps_free = {}
3206
        self.temps_count = 0
3207

3208
    def reset(self):
3209
        for type, cnames in self.temps_allocated.items():
3210
            self.temps_free[type] = list(cnames)
3211

3212
    def allocate_temp(self, type):
3213
        if type not in self.temps_allocated:
3214
            self.temps_allocated[type] = []
3215
            self.temps_free[type] = []
3216
        elif self.temps_free[type]:
3217
            return self.temps_free[type].pop(0)
3218
        cname = '%s%d' % (Naming.codewriter_temp_prefix, self.temps_count)
3219
        self.klass.declare_var(pos=None, name=cname, cname=cname, type=type, is_cdef=True)
3220
        self.temps_allocated[type].append(cname)
3221
        self.temps_count += 1
3222
        return cname
3223

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.