bpo-46541: Generate the global objects initializer. (gh-30941)

This change is a prerequisite for generating code for other global objects (like strings in gh-30928).

(We borrowed some code from Tools/scripts/deepfreeze.py.)

https://bugs.python.org/issue46541
This commit is contained in:
Eric Snow 2022-01-27 11:06:09 -07:00 committed by GitHub
parent 183f8d57fa
commit 247480a21c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 136 additions and 1 deletions

View file

@ -94,6 +94,8 @@ extern "C" {
_PyBytes_SIMPLE_INIT(CH, 1) \
}
/* The following is auto-generated by Tools/scripts/generate_global_objects.py. */
#define _Py_global_objects_INIT { \
.singletons = { \
.small_ints = { \
@ -622,6 +624,7 @@ extern "C" {
}, \
}, \
}
/* End auto-generated code */
#ifdef __cplusplus

View file

@ -1172,6 +1172,13 @@ Python/deepfreeze/deepfreeze.c: $(DEEPFREEZE_DEPS)
.PHONY: regen-importlib
regen-importlib: regen-frozen
############################################################################
# Global objects
.PHONY: regen-global-objects
regen-global-objects: $(srcdir)/Tools/scripts/generate_global_objects.py $(FREEZE_MODULE_DEPS)
$(PYTHON_FOR_FREEZE) $(srcdir)/Tools/scripts/generate_global_objects.py
############################################################################
# ABI
@ -1183,7 +1190,8 @@ regen-limited-abi: all
regen-all: regen-opcode regen-opcode-targets regen-typeslots \
regen-token regen-ast regen-keyword regen-frozen clinic \
regen-pegen-metaparser regen-pegen regen-test-frozenmain
regen-pegen-metaparser regen-pegen regen-test-frozenmain \
regen-global-objects
@echo
@echo "Note: make regen-stdlib-module-names and make autoconf should be run manually"

View file

@ -0,0 +1,124 @@
import argparse
import ast
import builtins
import collections
import contextlib
import os.path
import sys
assert os.path.isabs(__file__), __file__
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
INTERNAL = os.path.join(ROOT, 'Include', 'internal')
#######################################
# helpers
def iter_to_marker(lines, marker):
for line in lines:
if line.rstrip() == marker:
break
yield line
class Printer:
def __init__(self, file):
self.level = 0
self.file = file
self.continuation = [False]
@contextlib.contextmanager
def indent(self):
save_level = self.level
try:
self.level += 1
yield
finally:
self.level = save_level
def write(self, arg):
eol = '\n'
if self.continuation[-1]:
eol = f' \\{eol}' if arg else f'\\{eol}'
self.file.writelines((" "*self.level, arg, eol))
@contextlib.contextmanager
def block(self, prefix, suffix="", *, continuation=None):
if continuation is None:
continuation = self.continuation[-1]
self.continuation.append(continuation)
self.write(prefix + " {")
with self.indent():
yield
self.continuation.pop()
self.write("}" + suffix)
#######################################
# the global objects
START = '/* The following is auto-generated by Tools/scripts/generate_global_objects.py. */'
END = '/* End auto-generated code */'
def generate_runtime_init():
# First get some info from the declarations.
nsmallposints = None
nsmallnegints = None
with open(os.path.join(INTERNAL, 'pycore_global_objects.h')) as infile:
for line in infile:
if line.startswith('#define _PY_NSMALLPOSINTS'):
nsmallposints = int(line.split()[-1])
elif line.startswith('#define _PY_NSMALLNEGINTS'):
nsmallnegints = int(line.split()[-1])
break
else:
raise NotImplementedError
assert nsmallposints and nsmallnegints
# Then target the runtime initializer.
filename = os.path.join(INTERNAL, 'pycore_runtime_init.h')
# Read the non-generated part of the file.
with open(filename) as infile:
before = ''.join(iter_to_marker(infile, START))[:-1]
for _ in iter_to_marker(infile, END):
pass
after = infile.read()[:-1]
# Generate the file.
with open(filename, 'w', encoding='utf-8') as outfile:
printer = Printer(outfile)
printer.write(before)
printer.write(START)
with printer.block('#define _Py_global_objects_INIT', continuation=True):
with printer.block('.singletons =', ','):
# Global int objects.
with printer.block('.small_ints =', ','):
for i in range(-nsmallnegints, nsmallposints):
printer.write(f'_PyLong_DIGIT_INIT({i}),')
printer.write('')
# Global bytes objects.
printer.write('.bytes_empty = _PyBytes_SIMPLE_INIT(0, 0),')
with printer.block('.bytes_characters =', ','):
for i in range(256):
printer.write(f'_PyBytes_CHAR_INIT({i}),')
printer.write(END)
printer.write(after)
#######################################
# the script
def main() -> None:
generate_runtime_init()
if __name__ == '__main__':
argv = sys.argv[1:]
if argv:
sys.exit(f'ERROR: got unexpected args {argv}')
main()