""" Generate Lib/_opcode_metadata.py for RustPython bytecode. This file generates opcode metadata that is compatible with CPython 3.14. """ import functools import io import itertools import operator import pathlib import typing from opcodes import OpcodeInfo from utils import DEFAULT_INPUT, ROOT, get_conf, to_pascal_case, to_upper_snake_case OUT_FILE = ROOT / "Lib/_opcode_metadata.py" # Opcodes that needs to be first, regardless of their opcode ID. PRIORITY_OPMAP = { "CACHE", "RESERVED", "RESUME", "INSTRUMENTED_LINE", "ENTER_EXECUTOR", } INDENT = " " * 4 INDENT2 = INDENT * 2 def main(): override_conf = get_conf() inp = DEFAULT_INPUT.read_text() infos = tuple(OpcodeInfo.iter_infos(inp, override_conf)) opcodes = tuple(itertools.chain.from_iterable(info.opcodes for info in infos)) script_path = pathlib.Path(__file__).resolve().relative_to(ROOT).as_posix() out = io.StringIO() out.write( f""" # This file is generated by {script_path} # for RustPython bytecode format (CPython 3.14 compatible opcode numbers). # Do not edit! """.lstrip() ) # _specializations out.write("\n") out.write("_specializations = {\n") deopts = functools.reduce(operator.ior, map(operator.attrgetter("deopts"), infos)) for key, lst in deopts.items(): key = to_upper_snake_case(key) out.write(f'{INDENT}"{key}": [\n') for item in map(to_upper_snake_case, lst): out.write(f'{INDENT2}"{item}",\n') out.write(f"{INDENT}],\n") out.write("}\n") # _specialized_opmap out.write("\n") out.write("_specialized_opmap = {\n") specialized = set(itertools.chain.from_iterable(deopts.values())) for opcode in sorted(opcodes, key=lambda op: op.cpython_name): if opcode.rust_name not in specialized: continue out.write(f"{INDENT}'{opcode.cpython_name}': {opcode.id},\n") out.write("}\n") # opmap out.write("\n") out.write("opmap = {\n") key = lambda op: (op.cpython_name not in PRIORITY_OPMAP, op.id) for opcode in sorted(opcodes, key=key): if opcode.rust_name in specialized: continue out.write(f"{INDENT}'{opcode.cpython_name}': {opcode.id},\n") out.write("}\n") # min out.write("\n") have_argument = min(opcode.id for opcode in opcodes if opcode.have_argument) - 1 out.write(f"HAVE_ARGUMENT = {have_argument}\n") min_instrumented = min(opcode.id for opcode in opcodes if opcode.is_instrumented) out.write(f"MIN_INSTRUMENTED_OPCODE = {min_instrumented}\n") # write output generated = out.getvalue() OUT_FILE.write_text(generated) if __name__ == "__main__": main()