diff --git a/PORT_STATUS.md b/PORT_STATUS.md index 2ba7493..2af86b5 100644 --- a/PORT_STATUS.md +++ b/PORT_STATUS.md @@ -1,4 +1,196 @@ -## Latest revision (frameobject C89 work; frontier advances past floatobject) +## Latest revision (generated compatibility patch refreshed; VC6 frontier in object.c) + +CPython baseline: `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +The incremental C89/VC6 port now compiles `memoryobject.c`, `methodobject.c`, and `moduleobject.c` directly with VC6. Focused checks cover their declaration, evaluation, allocation, reference, and type-initialization ordering. Additional C89 conversions and focused checks have been added for `object.c`; its direct VC6 compile still fails with 72 diagnostics, many likely cascading from late declarations and remaining C99 constructs. This is compile-only progress; no interpreter link or Windows 98 SE runtime test is verified. + +The tracked compatibility patch `compat/msvc600/cpython-3.11.16-vc6-headers.patch` has been regenerated from the full nested CPython working-tree diff and the untracked `Include/internal/pycore_win9x_loadlibrary.h`. A byte-for-byte check confirmed the patch contains all 121 modified/new file diffs. The nested CPython checkout remains detached; its source changes are represented in the root repository by this patch, not by committing the nested checkout. + +Next: finish `object.c`, rerun the forced source-list compile frontier and focused regressions, then tackle linking and real Windows 98 SE execution separately. No link/runtime success is claimed. + +--- + +## Latest revision (`PyLong_FromUnsignedLongLong` uses VC6-compatible unsigned type spelling) + +Date: 2026-10-03 14:15 UTC + +Read `PORT_STATUS.md`, `README.md`, and the invoked legacy-toolchain guidance before work. The on-disk `python-3-win9x-msvc6.md` reference was absent. Preserved all pre-existing outer and detached CPython work; no reset, clean, stash, discard, commit, push, or publication. CPython remains at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549` (the same hash as recorded previously). The prior recorded `_PyLong_FromLarge()` frontier was stale. Forced-fresh MSVC600 `cl.exe` via Wine re-probe confirmed the actual first blocker was C2632 at `Objects/longobject.c(374)` and `(376)`: `unsigned long long` is rejected by VC6 in the `PyLong_FromUnsignedLongLong` definition and `PYLONG_FROM_UINT` instantiation. The compiler probe output did not print a separate compiler version banner. + +Used one focused Codex CLI lane (`codex-cli 0.153.4`, workspace-write) for one narrow TDD slice. Extended `scripts/vc6-longobject-c89.test.py`, observed RED against the original spelling, then changed only the function parameter and macro type argument to `unsigned PY_LONG_LONG`. This matches the public declaration in `Include/longobject.h` and the existing VC6 mapping to `__int64` in `PC/pyconfig.h`, preserving unsigned 64-bit intent. No other source declaration or behavior changed. The existing later C89 errors were left untouched. + +Exact verification: + +- Initial forced-fresh probe: `XDG_RUNTIME_DIR="$PWD/build/current-recheck/runtime" WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` — exit 2, `total=95 compiled=94 cached=0 skipped=0`; first errors C2632 at `longobject.c(374)` and `(376)`, followed by line-407 C89 errors. Log: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1791036400-3732714-7090.log`. +- RED: `python3 scripts/vc6-longobject-c89.test.py cpython/Objects/longobject.c` — exit 1, expected assertion that `PyLong_FromUnsignedLongLong` must use the public/VC6-compatible unsigned `PY_LONG_LONG` spelling. +- GREEN: same focused test — exit 0; all six longobject assertions passed. +- Applicable regression suite: `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1. The longobject checks and other applicable checks passed; two known unrelated failures remain: console type lookup/cleanup order and numeric dispatch (`ValueError: substring not found`). Log: `/tmp/vc6-longlong-slice/suite.log`. +- Post-change forced-fresh probe (same command) — exit 2, `total=95 compiled=94 cached=0 skipped=0`. The C2632 errors at lines 374/376 are gone; first stable errors now occur at line 407 in `PyLong_FromDouble()` (C2275/C2065 on late `PyLongObject *v`, followed by C89 declaration errors). Later independent long-long/CRT diagnostics remain. Log: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1791036840-3732714-b110.log`. +- `git -C cpython diff --check -- Objects/longobject.c` and `git diff --check -- scripts/vc6-longobject-c89.test.py` — exit 0. Preservation comparison confirmed only this source and focused test changed beyond the pre-run snapshot; `PORT_STATUS.md`, compatibility patch, and all unrelated local changes were preserved. + +Files changed by this slice: `cpython/Objects/longobject.c`, `scripts/vc6-longobject-c89.test.py`, and this status entry. This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or actual Windows 98 SE execution. Next frontier: test-first address only the first late-local declaration family in `PyLong_FromDouble()` at line 407, preserving all fast-path and validation/evaluation order; keep subsequent long-long/CRT issues separate. + +--- + +## Previous revision (`_PyLong_FromLarge` C89 locals hoisted; frontier advances within longobject) + +Date: 2026-10-03 09:47 UTC + +Read `PORT_STATUS.md`, `README.md`, and the legacy-toolchain guidance first. The referenced on-disk `python-3-win9x-msvc6.md` file was not found in the active profile or repository; the invoked skill guidance was available. Preserved all pre-existing outer and detached CPython work; no reset, clean, stash, discard, commit, push, or publication. CPython remains at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +The recorded `_PyLong_FromLarge()` frontier remained current on forced-fresh probe. Used one focused Codex CLI lane (`codex-cli 0.153.4`, workspace-write) for one narrow TDD slice. Extended `scripts/vc6-longobject-c89.test.py`, observed RED before the source edit, then hoisted only `twodigits abs_ival`, `twodigits t`, `Py_ssize_t ndigits`, `PyLongObject *v`, `digit *p`, and `int sign` to `_PyLong_FromLarge()`'s entry declarations in `cpython/Objects/longobject.c`. Initializers became assignments at their original execution points. Assertions, sign/absolute-value branches, digit-count loop, allocation, initialization, digit-write loop, and return ordering remain unchanged. Independently inspected the nested diff and confirmed only this function changed in the nested CPython slice. + +Exact verification: + +- Initial forced-fresh compiler probe: `XDG_RUNTIME_DIR="$PWD/build/current-recheck/runtime" WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` — exit 2, `total=95 compiled=94 cached=0 skipped=0`. `_blake2/blake2s_impl.c` compiled. First errors were C2275/C2146/C2065 on late `twodigits t` at `longobject.c(239)`, followed by late `ndigits` at 240 and `v` at 245. Full log: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1791020452-3685379-2910.log`. +- RED: `python3 scripts/vc6-longobject-c89.test.py cpython/Objects/longobject.c` — exit 1 at the new `_PyLong_FromLarge` assertion; the prior get_small_int, `_PyLong_New`, and `_PyLong_FromMedium` checks passed. +- GREEN: same focused command — exit 0; all four longobject regression checks passed. +- Applicable broad regression: `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1. Longobject checks and the rest of applicable checks run and pass. Two known unrelated failures remain: console type lookup/cleanup order and numeric dispatch (`ValueError: substring not found`). +- Post-change forced-fresh probe (same command) — exit 2, `total=95 compiled=94 cached=0 skipped=0`. `longobject.c` now compiles past `_PyLong_FromLarge`; first errors advanced to line 364 in `PyLong_FromLong()` (C2275/C2146/C2065 on a late `Py_ssize_t ndigits`, then cascading late `t`, `v`, and `p` declarations). Subsequent diagnostics include independent long-long/CRT blockers. Full log: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1791020810-3685379-5b50.log`. +- `git -C cpython diff --check -- Objects/longobject.c` and `git diff --check -- PORT_STATUS.md scripts/vc6-longobject-c89.test.py` — exit 0. + +Files changed by this slice: `cpython/Objects/longobject.c`, `scripts/vc6-longobject-c89.test.py`, and this status entry. Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or actual Windows 98 SE execution. Next frontier: test-first address only the first `PyLong_FromLong()` declaration family at line 364, retaining its small/medium fast paths and digit-count/allocation order; handle later `long long` and CRT issues separately. + +--- + +## Previous revision (`_PyLong_FromMedium` C89 locals hoisted; frontier advances within longobject) + +Date: 2026-10-03 07:39 UTC + +Read `PORT_STATUS.md`, `README.md`, and the legacy-toolchain reference before acting. Preserved the existing outer worktree and detached CPython changes; no reset, clean, stash, discard, commit, push, or publication. CPython remains at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The prior `_PyLong_New()` frontier was freshly rechecked: `_blake2/blake2s_impl.c` compiled, `_PyLong_New()`'s line-156 diagnostic was gone, and the first blocker was `_PyLong_FromMedium()` at line 205. + +Used one focused Codex CLI coding lane (`codex-cli 0.153.4`, workspace-write). Strict TDD: extended `scripts/vc6-longobject-c89.test.py` for `_PyLong_FromMedium()` declarations and statement order, observed RED before the production edit, then hoisted only `PyLongObject *v`, `Py_ssize_t sign`, and `digit abs_x` to the opening declaration group in `cpython/Objects/longobject.c`. Assignments remain at their original points; assertions, allocation, NULL/error/return, initialization, digit write, and return order are preserved. The agent made no other changes; independently inspected the narrow diff. + +Exact verification: + +- Initial forced-fresh probe: `XDG_RUNTIME_DIR="$PWD/build/current-recheck/runtime" WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` — exit 2, `total=95 compiled=94 cached=0 skipped=0`. `_blake2/blake2s_impl.c` compiled. First blocker was C2143 at `longobject.c(205)` on the late `PyLongObject *v`; dependent errors included C2065 `v` and late `Py_ssize_t sign` / `digit abs_x`. Full log: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1791012585-3658734-75d0.log`. +- RED: `python3 scripts/vc6-longobject-c89.test.py cpython/Objects/longobject.c` — exit 1, expected assertion that `_PyLong_FromMedium` declarations and order did not match; prior `get_small_int` and `_PyLong_New` checks passed. +- GREEN: same command — exit 0; all three longobject checks passed, including the new `_PyLong_FromMedium` check. +- Applicable regression suite: `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1. The broad suite ran; this slice's source behavior assertion passed when run directly. Two known unrelated failures remain: console type lookup/cleanup order and numeric dispatch (`ValueError: substring not found`). +- Post-change forced-fresh probe (same command) — exit 2, `total=95 compiled=94 cached=0 skipped=0`. `_PyLong_FromMedium()` passed its former diagnostics. First stable diagnostics now occur in `_PyLong_FromLarge()` at `longobject.c(239)` on late `twodigits t` (C2275/C2146, with C2065 `t`), followed by late `Py_ssize_t ndigits` at line 240 and late `PyLongObject *v` at line 245. Full log: `build/current-recheck/longobject-frommedium-post.log`. +- `git diff --check -- PORT_STATUS.md scripts/vc6-longobject-c89.test.py && git -C cpython diff --check -- Objects/longobject.c` — exit 0. + +Files changed by this slice: `cpython/Objects/longobject.c`, `scripts/vc6-longobject-c89.test.py`, and this status entry. This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or actual Windows 98 SE execution. Next frontier: test-first hoist only the first `_PyLong_FromLarge()` declaration family in function-entry order, preserving assertions, sign/absolute-value decisions, digit-count loop, allocation, initialization, and digit-write order; keep later `long long`/CRT blockers separate. + +--- + +## Previous revision (_PyLong_New ndigits declaration hoisted; frontier remains longobject) + +Date: 2026-10-03 05:16 UTC + +Read `PORT_STATUS.md`, `README.md`, and the CPython/Win9x/MSVC6 reference before acting. Preserved existing outer and detached CPython edits; no reset, clean, stash, discard, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The recorded frontier had become stale: forced-fresh VC6 re-probe confirmed `_blake2/blake2s_impl.c` compiles and first blocked at `Objects/longobject.c(156)` inside `_PyLong_New()`. + +Applied one narrow test-first C89 slice: hoisted only `Py_ssize_t ndigits` into `_PyLong_New()`'s declaration group; its assignment remains after the overflow check and before allocation. The regression `scripts/vc6-longobject-c89.test.py` now checks the declaration and exact overflow-check, ndigits assignment, allocation, allocation error, initialization, and return order. The native Hermes coding-subagent API could not start because its Anthropic package is absent; used one focused Codex CLI lane (`codex-cli 0.153.4`, workspace-write) and independently checked results. Existing uncommitted `get_small_int()` changes were retained. + +Exact verification: + +- Initial forced-fresh probe: `XDG_RUNTIME_DIR="$PWD/build/current-recheck/runtime" WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` — exit 2, `total=95 compiled=94 cached=0 skipped=0`. `_blake2/blake2s_impl.c` compiled; first blocker was C2275/C2146/C2065 at `longobject.c(156)` for late `Py_ssize_t ndigits`. Full output: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1791004605-3636193-e850.log`. +- RED: `python3 scripts/vc6-longobject-c89.test.py cpython/Objects/longobject.c` — exit 1, expected `_PyLong_New` assertion failed; existing `get_small_int` check passed. +- GREEN: same command — exit 0, both `get_small_int` and `_PyLong_New` checks passed. +- Post-change forced-fresh probe (same command) — exit 2, `total=95 compiled=94 cached=0 skipped=0`. The `_PyLong_New()` line-156 diagnostic is gone. First remaining stable diagnostics start at `longobject.c(205)` in `_PyLong_FromMedium()` (C2143 before type and dependent C2065 `v`); later declaration-order and `long long` blockers remain separate. Full output: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1791004999-3636193-ef50.log`. +- Applicable regression command `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1. The new longobject checks pass when run directly; the full suite still has the two known failures: console type-detection ordering and numeric dispatch (`ValueError: substring not found`). +- `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && git diff --check -- PORT_STATUS.md scripts/vc6-longobject-c89.test.py && git -C cpython diff --check -- Objects/longobject.c` — exit 0. + +Files changed by this slice: `cpython/Objects/longobject.c`, `scripts/vc6-longobject-c89.test.py`, and `PORT_STATUS.md`. Compile-frontier evidence only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or actual Windows 98 SE execution. Next frontier: test-first hoist only the first `_PyLong_FromMedium()` declaration family at line 205, preserving allocation/error and initialization order; keep subsequent locals and `long long` issues separate. + +Read `PORT_STATUS.md`, `README.md`, and the invoked legacy-toolchain guidance before acting. The referenced on-disk `python-3-win9x-msvc6.md` was not present at either the expected profile skills reference path or in the repository. Preserved every pre-existing outer and detached CPython change; no reset, clean, stash, discard, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +The status-recorded listobject frontier was stale. A forced-fresh probe confirmed `Objects/listobject.c` and `_blake2/blake2s_impl.c` now compile; first blocker is `Objects/longobject.c`. Applied one narrow C89 slice in `get_small_int()`: moved only `PyObject *v` to the function's declaration group and retained the assertion → small-int lookup assignment → incref → return order. Added `scripts/vc6-longobject-c89.test.py`. Coding subagent dispatch was attempted, but the delegate service failed because the Anthropic Python package is unavailable; proceeded with the minimal test-first edit directly, without a coding CLI. + +Exact verification: + +- RED: `python3 scripts/vc6-longobject-c89.test.py cpython/Objects/longobject.c` — exit 1 with the expected assertion that `get_small_int` must declare `v` at function entry and preserve evaluation order. +- GREEN: same command — exit 0, `longobject get_small_int C89 declaration and evaluation order: PASS`. +- Applicable shell regression: `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1. The longobject changes do not introduce suite assertions (no longobject integration existed); the known console-type-detection assertion fails and numeric-dispatch check raises `ValueError: substring not found`. The listobject and other targeted checks pass. +- Initial fresh probe before the slice: `XDG_RUNTIME_DIR="$PWD/build/current-recheck/runtime" WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` — exit 2, `total=95 compiled=94 cached=0 skipped=0`. First diagnostics in `longobject.c`: C2143 at line 61 before `type`, C2065 `v`; subsequent errors are distinct declaration-order and `long long`/CRT compatibility issues. Log: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1790996934-3608344-9090.log`. +- Post-change forced fresh probe (same command) — exit 2, `total=95 compiled=94 cached=0 skipped=0`. `get_small_int()`'s prior C2143/C2065 diagnostics are gone; first remaining errors are C2275/C2146/C2065 at `longobject.c(156)` for late `Py_ssize_t ndigits` in `_PyLong_New()`. Later declaration-order and VC6 64-bit type issues remain separate. Log: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1790997084-3608344-5490.log`. +- `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh`, focused/nested/outer scoped `git diff --check` — exit 0. + +Files changed by this slice: `cpython/Objects/longobject.c`, `scripts/vc6-longobject-c89.test.py`, and `PORT_STATUS.md`. Compiler-probe progress only: no `pythoncore.dll`/`python.exe` link, Wine interpreter execution, or actual Windows 98 SE execution. Next frontier: test-first hoist only `ndigits` in `_PyLong_New()`, preserving the overflow check and allocation/error sequence. + +--- + +## Latest revision (expanded listobject VC6/C89 fixes; forced recheck in progress) + +Date: 2026-10-03 03:05 UTC + +Preserved all pre-existing outer and detached CPython changes; no reset, clean, stash, discard, commit, or push. CPython remains on the v3.11.16 baseline. + +The last completed forced-fresh probe had advanced past the earlier list-allocation, list-repeat, and powersort-helper blockers, then reported additional C89 violations in `listobject.c` sorting, comparison, vectorcall, extended-slice assignment, type initialization, and iterator-pickling code. Applied declaration/order fixes in those paths and replaced the `PyList_Type.tp_vectorcall` designated initializer with a positional tail matching `PyTypeObject` field order. Extended the focused listobject regression to cover the added behavior/order checks. + +Verification: + +- Focused regressions pass for listobject, frameobject, funcobject, genericaliasobject (including `object.c`), genobject, interpreteridobject, and iterobject. +- Python test-script compilation, `bash -n` for frontier scripts, and nested/outer `git diff --check` passed. +- Full harness remains blocked by the lifecycle guard's exhausted scan budget; no full-suite pass is claimed. +- A fresh forced probe after all current listobject changes is running; log: `build/current-recheck/frontier-listobject-expanded-fixes.log`. + +Compile-only evidence; no interpreter link or runtime execution. Next step: inspect the fresh frontier result and continue from the first compiler diagnostic. + +--- + +## Previous latest revision (listobject repeat C89 declarations; forced recheck) + +Date: 2026-10-03 00:57 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile `python-3-win9x-msvc6.md` reference before acting. Preserved the existing outer and detached CPython work; no reset, clean, stash, discard, commit, push, publication, or compatibility-patch regeneration. CPython remains at `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +The recorded `_blake2/blake2s_impl.c` state was no longer the frontier. Forced-fresh probing found `Objects/genericaliasobject.c` as the first blocker. Applied one narrow initializer-compatibility slice: replaced the designated `.mp_subscript` initializer with positional `PyMappingMethods` fields `0, ga_getitem, 0` in declared field order. The first positional attempt exposed type-mismatch warnings because it omitted the leading `mp_length` slot; a stronger focused assertion failed first, then the initializer was corrected to preserve the intended mp_subscript semantics. + +Exact commands/results: + +- Initial forced-fresh probe: `XDG_RUNTIME_DIR="$PWD/build/current-recheck/runtime" WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` — exit 2; `total=90 compiled=89 cached=0 skipped=0`. First diagnostic was C2059 at `genericaliasobject.c(583)` for `.mp_subscript = ga_getitem`. +- TDD RED: `python3 scripts/vc6-genericaliasobject-c89.test.py cpython/Objects/genericaliasobject.c` — exit 1 first on missing VC6-compatible initializer; after correcting the field order, that assertion passes and the script advances to the pre-existing `ga_hash second hash local must be declared at function entry` failure (exit 1). A direct assertion of the exact `0, ga_getitem, 0` field layout passed (exit 0). +- `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1. The new initializer assertion and other generic-alias checks run, but existing generic-alias `ga_hash second hash local must be declared at function entry` remains failing. Also two unrelated pre-existing failures persist: console type detection order and numeric dispatch (`ValueError: substring not found`). No unrelated changes made. +- Post-change forced-fresh probe (same command) — exit 2; `total=90 compiled=89 cached=0 skipped=0`. The line-583 C2059 is gone and the ordered initializer emits no mismatch warning. New first stable errors are C2275/C2146/C2065 at line 597 for late `Py_hash_t h1`; later line-658 and subsequent diagnostics are separate. Full output: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1790989338-3437483-1c50.log`. +- Scoped `git diff --check` on the test, status file, and nested `Objects/genericaliasobject.c` — exit 0. + +Files changed by this slice: `cpython/Objects/genericaliasobject.c`, `scripts/vc6-genericaliasobject-c89.test.py`, and this status log. The coding-subagent dispatch was attempted, but could not start because the delegate environment lacks the required `anthropic` Python package; completed the narrowly scoped test-first edit directly and verified it. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is claimed. Next frontier: separately test-first hoist `Py_hash_t h1` in `ga_hash`, preserving origin-first hash/error order. + +--- + +## Previous revision (`tuple_index` loop declaration hoisted; genericaliasobject frontier advances) + +Date: 2026-10-02 22:56 UTC + +Read `PORT_STATUS.md`, `README.md`, and the loaded legacy-toolchain guidance before acting; the on-disk `references/python-3-win9x-msvc6.md` was not found. Preserved all existing outer and detached CPython work, including the pre-existing untracked `scripts/vc6-genericaliasobject-c89.test.py`. No reset, clean, stash, discard, commit, push, or publication. CPython remains at baseline tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +The recorded frameobject frontier was rechecked with a forced-fresh MSVC600 probe: it compiled through `Objects/frameobject.c`; the active first blocker is `Objects/genericaliasobject.c`. The narrow C89 slice was limited to `tuple_index()`: moved `Py_ssize_t i` to the function-entry declaration group and reused it in the loop. Scan order and `return -1` behavior are unchanged. Used exactly one focused Codex CLI coding lane (0.153.4, workspace-write); independently checked its narrow diff and ran the applicable checks. The existing focused test failed before the source edit as expected. + +Exact verification: + +- RED: `python3 scripts/vc6-genericaliasobject-c89.test.py cpython/Objects/genericaliasobject.c` — exit 1, `tuple_index loop variable must be declared at function entry`. +- GREEN: same command — exit 0, `genericaliasobject C89 declaration and writer order: PASS`. +- Forced fresh probe: `XDG_RUNTIME_DIR="$PWD/build/current-recheck/runtime" WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` — exit 2, `total=90 compiled=89 cached=0 skipped=0`. `genericaliasobject.c` passes the `tuple_index()` diagnostic. First stable remaining diagnostics begin at line 206 in `tuple_extend()` with C2143 “missing ';' before 'type'” on the C99 loop declaration; subsequent errors are follow-ons/additional C89 sites in this translation unit. Full log: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1790981507-3342471-aa90.log`. +- Focused regression rerun — exit 0, `genericaliasobject C89 declaration and writer order: PASS`. +- `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1. The genericalias regression passes; two known unrelated assertions remain failing: console type lookup/cleanup order and numeric dispatch (`ValueError: substring not found`). No unrelated fixes attempted. +- Scoped `git diff --check` for `cpython/Objects/genericaliasobject.c`, the focused test and `PORT_STATUS.md` — exit 0. + +Files changed by this slice: `cpython/Objects/genericaliasobject.c` and this status log. The focused test file was pre-existing and remains untracked; it was not altered this run. The generated compatibility patch was not regenerated. This is compiler-probe progress only: no `pythoncore.dll`/`python.exe` link, Wine interpreter execution, or real Windows 98 SE execution. Next frontier: inspect and test-first address only the first `tuple_extend()` loop declaration diagnostic at line 206. + +--- + +## Previous latest revision (`frame_setlineno` C89 declarations hoisted; frameobject frontier advances) + +Date: 2026-10-02 20:33 UTC + +Read `PORT_STATUS.md`, `README.md`, and the loaded legacy-toolchain porting guidance before editing. The on-disk `references/python-3-win9x-msvc6.md` was not present at the path checked; the relevant project rules were available in the invoked skill content. Preserved all pre-existing outer and detached CPython work. No reset, clean, stash, discard, commit, push, or publication. CPython remains at baseline tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +A forced fresh VC6 probe confirmed the recorded frontier was current: 87/88 translation units compiled, `_blake2/blake2s_impl.c` compiled, and `Objects/frameobject.c` was first blocker at `frame_setlineno()` with C2275/C2146/C2065 for late `PyFrameState state` at line 686. Used one focused Codex CLI coding lane after confirming `codex-cli 0.153.4` was available. Following strict TDD, it extended `scripts/vc6-frameobject-c89.test.py`, observed expected RED before production change, then hoisted the 13 function-scope locals in `frame_setlineno()` into the opening declaration group. Initializer expressions remain assignments at their previous execution points; nested block locals, validation/error/cleanup/iteration/frame-stack ordering remain unchanged. Independently verified the diff and reran checks. + +Exact commands/results: + +- Initial forced probe `XDG_RUNTIME_DIR="$PWD/build/current-recheck/runtime" WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` — exit 2, 87/88 compiled, zero cached. First stable diagnostics were C2275/C2146/C2065 on `PyFrameState state` at `frameobject.c(686)`; subsequent errors in that function were follow-ons. Full log: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1790973232-3123322-1550.log`. +- RED focused test (before source edit): `python3 scripts/vc6-frameobject-c89.test.py cpython/Objects/frameobject.c` — exit 1, expected missing-entry-declaration assertion for `PyFrameState state;`. +- GREEN focused test: same command — exit 0, `frameobject C89 declarations and frame locals ownership order: PASS`. +- `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh` and scoped nested/outer `git diff --check` — exit 0. +- `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1. The new frameobject regression passes; 250 checks pass overall. Two existing unrelated failures remain: console type lookup/cleanup assertion and numeric dispatch assertion (`ValueError: substring not found`). No unrelated fixes attempted. +- Forced post-change probe, after preparing writable workspace runtime directory: `mkdir -p build/current-recheck/runtime && chmod 700 build/current-recheck/runtime && XDG_RUNTIME_DIR="$PWD/build/current-recheck/runtime" WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` — exit 2, 87/88 compiled, zero cached. `frame_setlineno()` now compiles past its prior diagnostics. First stable remaining diagnostic is C2143 at `frameobject.c(900)` in the next function; further errors there and later in the translation unit are distinct follow-on blockers. Full log: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1790973669-3123322-2e50.log`. + +Files changed by this slice: `cpython/Objects/frameobject.c`, `scripts/vc6-frameobject-c89.test.py`, and this log. Other attempted VC6 smoke suites encountered the same read-only Wine socket failure when invoked outside the writable runtime-directory context; shell, fixture-generation, and focused frameobject checks passed. This is compiler-probe progress only: no `pythoncore.dll`/`python.exe` link, Wine interpreter execution, or Windows 98 SE execution. Next frontier: inspect and test-first address the first diagnostic at `frameobject.c(900)`, without bundling later errors. + +--- + +## Previous latest revision (frameobject C89 work; frontier advances past floatobject) Date: 2026-10-02 19:09 UTC @@ -7812,4 +8004,24 @@ Exact commands/results: Files changed by this slice: `cpython/Objects/codeobject.c`, `scripts/vc6-codeobject-c89.test.py`, and this status log. This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution. Next frontier: separately test-first hoist only `Py_hash_t h4` and `h5` in the functions at lines 1830/1834, preserving hash computation order. Keep the existing outer regression failures explicit; compatibility patch synchronization remains unresolved. --- +## Latest revision (`PYLONG_FROM_UINT` C89 locals hoisted; frontier reaches long-long syntax) + +Date: 2026-10-03 12:05 UTC + +Read `PORT_STATUS.md`, `README.md`, and the loaded legacy-toolchain guidance before acting; the referenced on-disk CPython reference remains unavailable. Preserved all pre-existing outer and detached CPython changes; no reset, clean, stash, discard, commit, push, publication, or compatibility-patch regeneration. CPython remains at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +The previous status named `PyLong_FromLong()` as the next frontier, but fresh compiler evidence showed the line-364 declarations were emitted from the `PYLONG_FROM_UINT` macro invoked by `PyLong_FromUnsignedLong()`. `PyLong_FromLong()` already had its function locals declared at entry. Used one focused Codex CLI coding lane (`codex-cli 0.153.4`, workspace-write) for a single test-first macro slice. Extended `scripts/vc6-longobject-c89.test.py`, observed expected RED before the source change, then hoisted only `Py_ssize_t ndigits`, `INT_TYPE t`, `PyLongObject *v`, and `digit *p` to the start of the macro's `do` block in `cpython/Objects/longobject.c`. Initializers became assignments at the previous evaluation points. Small-int fast return, digit-count loop, allocation/error path, value consumption/mutation, digit writes, and return order are unchanged. Independently inspected the complete nested diff: the macro is the only new nested source change in this slice; earlier uncommitted `longobject.c` edits are preserved. + +Exact verification: + +- Forced-fresh MSVC600 probe before the slice: `XDG_RUNTIME_DIR="$PWD/build/current-recheck/runtime" WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` — exit 2, `total=95 compiled=94 cached=0 skipped=0`. `_blake2/blake2s_impl.c` compiled. The line-364 errors attributed to `PyLong_FromLong()` in the prior log were actually macro expansion diagnostics: late `Py_ssize_t ndigits`, `INT_TYPE t`, `PyLongObject *v`, and `digit *p` in `PYLONG_FROM_UINT`. Full log: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1791028366-3708731-9a10.log`. +- RED: `python3 scripts/vc6-longobject-c89.test.py cpython/Objects/longobject.c` — exit 1 before the source edit, expected `AssertionError: PYLONG_FROM_UINT must declare ndigits, t, v, and p at do-block entry`. +- GREEN: same command after edit — exit 0; all five focused longobject checks passed, including macro declaration/evaluation/error order. +- Applicable regression: `MSVC600_ROOT=/tmp/pylong-uint-no-toolchain bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1; regression run completed, with only the two known unrelated failures: console type detection/cleanup order and numeric dispatch (`ValueError: substring not found`). The live compiler integration check was intentionally skipped for this suite run; the separate forced-fresh probe below ran with the actual installed toolchain. +- Independent forced-fresh post-change probe (same `XDG_RUNTIME_DIR`, `WINEPREFIX`, and `VC6_FRONTIER_FORCE=1` command) — exit 2, `total=95 compiled=94 cached=0 skipped=0`. `longobject.c` passed the `PYLONG_FROM_UINT` macro declaration diagnostics. First diagnostics now occur at `longobject.c(374)` / `(376)`: C2632, `long` followed by `long` is illegal in the `PyLong_FromUnsignedLongLong` signature and macro argument. Further longobject diagnostics (including C89 sites and missing 64-bit CRT names) remain separate. Full log: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1791028867-3708731-dc50.log`. +- `python3 scripts/vc6-longobject-c89.test.py cpython/Objects/longobject.c`, nested `git diff --check -- Objects/longobject.c`, and outer `git diff --check -- scripts/vc6-longobject-c89.test.py` — exit 0. + +Files changed by this slice: `cpython/Objects/longobject.c`, `scripts/vc6-longobject-c89.test.py`, and this status entry. This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution. Next frontier: investigate the VC6 `long long` blocker at `PyLong_FromUnsignedLongLong` (including applicable compatibility typedef/macros and declarations) as its own evidence-backed slice; do not bundle later C89/CRT diagnostics. No commit or push. + +--- diff --git a/compat/msvc600/cpython-3.11.16-vc6-headers.patch b/compat/msvc600/cpython-3.11.16-vc6-headers.patch index 4f4013e..8790364 100644 --- a/compat/msvc600/cpython-3.11.16-vc6-headers.patch +++ b/compat/msvc600/cpython-3.11.16-vc6-headers.patch @@ -17834,7 +17834,7 @@ index 29a74f2..4a7c392 100644 if ((float_format == ieee_little_endian_format && !le) diff --git a/Objects/frameobject.c b/Objects/frameobject.c -index c95e871..5c2f86f 100644 +index c95e871..15bfa79 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -25,9 +25,11 @@ static PyMemberDef frame_memberlist[] = { @@ -18083,29 +18083,3111 @@ index c95e871..5c2f86f 100644 if (lines[i] < result && lines[i] >= line) { result = lines[i]; } -@@ -648,6 +670,8 @@ _PyFrame_GetState(PyFrameObject *frame) +@@ -648,6 +670,20 @@ _PyFrame_GetState(PyFrameObject *frame) static int frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignored)) { ++ PyFrameState state; + int i; ++ int new_lineno; ++ int overflow; ++ long l_new_lineno; ++ int len; ++ int *lines; ++ int64_t *stacks; ++ int64_t best_stack; ++ int best_addr; ++ int64_t start_stack; ++ int err; ++ const char *msg; + if (p_new_lineno == NULL) { PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); return -1; -@@ -748,7 +772,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore - int64_t start_stack = stacks[_PyInterpreterFrame_LASTI(f->f_frame)]; - int err = -1; - const char *msg = "cannot find bytecode for specified line"; +@@ -659,7 +695,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore + return -1; + } + +- PyFrameState state = _PyFrame_GetState(f); ++ state = _PyFrame_GetState(f); + /* + * This code preserves the historical restrictions on + * setting the line number of a frame. +@@ -695,12 +731,9 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore + return -1; + } + +- int new_lineno; +- + /* Fail if the line falls outside the code block and + select first line with actual code. */ +- int overflow; +- long l_new_lineno = PyLong_AsLongAndOverflow(p_new_lineno, &overflow); ++ l_new_lineno = PyLong_AsLongAndOverflow(p_new_lineno, &overflow); + if (overflow + #if SIZEOF_LONG > SIZEOF_INT + || l_new_lineno > INT_MAX +@@ -722,8 +755,8 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore + + /* PyCode_NewWithPosOnlyArgs limits co_code to be under INT_MAX so this + * should never overflow. */ +- int len = (int)Py_SIZE(f->f_frame->f_code); +- int *lines = marklines(f->f_frame->f_code, len); ++ len = (int)Py_SIZE(f->f_frame->f_code); ++ lines = marklines(f->f_frame->f_code, len); + if (lines == NULL) { + return -1; + } +@@ -737,18 +770,18 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore + return -1; + } + +- int64_t *stacks = mark_stacks(f->f_frame->f_code, len); ++ stacks = mark_stacks(f->f_frame->f_code, len); + if (stacks == NULL) { + PyMem_Free(lines); + return -1; + } + +- int64_t best_stack = OVERFLOWED; +- int best_addr = -1; +- int64_t start_stack = stacks[_PyInterpreterFrame_LASTI(f->f_frame)]; +- int err = -1; +- const char *msg = "cannot find bytecode for specified line"; - for (int i = 0; i < len; i++) { ++ best_stack = OVERFLOWED; ++ best_addr = -1; ++ start_stack = stacks[_PyInterpreterFrame_LASTI(f->f_frame)]; ++ err = -1; ++ msg = "cannot find bytecode for specified line"; + for (i = 0; i < len; i++) { if (lines[i] == new_lineno) { int64_t target_stack = stacks[i]; if (compatible_stack(start_stack, target_stack)) { +@@ -856,6 +889,11 @@ static PyGetSetDef frame_getsetlist[] = { + static void + frame_dealloc(PyFrameObject *f) + { ++ PyCodeObject *co; ++ _PyInterpreterFrame *frame; ++ PyObject **locals; ++ int i; ++ + /* It is the responsibility of the owning generator/coroutine + * to have cleared the generator pointer */ + +@@ -864,13 +902,13 @@ frame_dealloc(PyFrameObject *f) + } + + Py_TRASHCAN_BEGIN(f, frame_dealloc); +- PyCodeObject *co = NULL; ++ co = NULL; + + /* GH-106092: If f->f_frame was on the stack and we reached the maximum + * nesting depth for deallocations, the trashcan may have delayed this + * deallocation until after f->f_frame is freed. Avoid dereferencing + * f->f_frame unless we know it still points to valid memory. */ +- _PyInterpreterFrame *frame = (_PyInterpreterFrame *)f->_f_frame_data; ++ frame = (_PyInterpreterFrame *)f->_f_frame_data; + + /* Kill all local variables including specials, if we own them */ + if (f->f_frame == frame && frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) { +@@ -879,8 +917,8 @@ frame_dealloc(PyFrameObject *f) + frame->f_code = NULL; + Py_CLEAR(frame->f_func); + Py_CLEAR(frame->f_locals); +- PyObject **locals = _PyFrame_GetLocalsArray(frame); +- for (int i = 0; i < frame->stacktop; i++) { ++ locals = _PyFrame_GetLocalsArray(frame); ++ for (i = 0; i < frame->stacktop; i++) { + Py_CLEAR(locals[i]); + } + } +@@ -906,12 +944,15 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) + static int + frame_tp_clear(PyFrameObject *f) + { ++ PyObject **locals; ++ int i; ++ + Py_CLEAR(f->f_trace); + + /* locals and stack */ +- PyObject **locals = _PyFrame_GetLocalsArray(f->f_frame); ++ locals = _PyFrame_GetLocalsArray(f->f_frame); + assert(f->f_frame->stacktop >= 0); +- for (int i = 0; i < f->f_frame->stacktop; i++) { ++ for (i = 0; i < f->f_frame->stacktop; i++) { + Py_CLEAR(locals[i]); + } + f->f_frame->stacktop = 0; +@@ -949,8 +990,10 @@ static PyObject * + frame_sizeof(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) + { + Py_ssize_t res; ++ PyCodeObject *code; ++ + res = offsetof(PyFrameObject, _f_frame_data) + offsetof(_PyInterpreterFrame, localsplus); +- PyCodeObject *code = f->f_frame->f_code; ++ code = f->f_frame->f_code; + res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *); + return PyLong_FromSsize_t(res); + } +@@ -1015,12 +1058,15 @@ PyTypeObject PyFrame_Type = { + static void + init_frame(_PyInterpreterFrame *frame, PyFunctionObject *func, PyObject *locals) + { ++ PyCodeObject *code; ++ Py_ssize_t i; ++ + /* _PyFrame_InitializeSpecials consumes reference to func */ + Py_INCREF(func); +- PyCodeObject *code = (PyCodeObject *)func->func_code; ++ code = (PyCodeObject *)func->func_code; + _PyFrame_InitializeSpecials(frame, func, locals, code->co_nlocalsplus); + frame->previous = NULL; +- for (Py_ssize_t i = 0; i < code->co_nlocalsplus; i++) { ++ for (i = 0; i < code->co_nlocalsplus; i++) { + frame->localsplus[i] = NULL; + } + } +@@ -1028,9 +1074,12 @@ init_frame(_PyInterpreterFrame *frame, PyFunctionObject *func, PyObject *locals) + PyFrameObject* + _PyFrame_New_NoTrack(PyCodeObject *code) + { ++ int slots; ++ PyFrameObject *f; ++ + CALL_STAT_INC(frame_objects_created); +- int slots = code->co_nlocalsplus + code->co_stacksize; +- PyFrameObject *f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, slots); ++ slots = code->co_nlocalsplus + code->co_stacksize; ++ f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, slots); + if (f == NULL) { + return NULL; + } +@@ -1048,25 +1097,28 @@ PyFrameObject* + PyFrame_New(PyThreadState *tstate, PyCodeObject *code, + PyObject *globals, PyObject *locals) + { +- PyObject *builtins = _PyEval_BuiltinsFromGlobals(tstate, globals); // borrowed ref ++ PyObject *builtins; ++ PyFrameConstructor desc; ++ PyFunctionObject *func; ++ PyFrameObject *f; ++ ++ builtins = _PyEval_BuiltinsFromGlobals(tstate, globals); // borrowed ref + if (builtins == NULL) { + return NULL; + } +- PyFrameConstructor desc = { +- .fc_globals = globals, +- .fc_builtins = builtins, +- .fc_name = code->co_name, +- .fc_qualname = code->co_name, +- .fc_code = (PyObject *)code, +- .fc_defaults = NULL, +- .fc_kwdefaults = NULL, +- .fc_closure = NULL +- }; +- PyFunctionObject *func = _PyFunction_FromConstructor(&desc); ++ desc.fc_globals = globals; ++ desc.fc_builtins = builtins; ++ desc.fc_name = code->co_name; ++ desc.fc_qualname = code->co_name; ++ desc.fc_code = (PyObject *)code; ++ desc.fc_defaults = NULL; ++ desc.fc_kwdefaults = NULL; ++ desc.fc_closure = NULL; ++ func = _PyFunction_FromConstructor(&desc); + if (func == NULL) { + return NULL; + } +- PyFrameObject *f = _PyFrame_New_NoTrack(code); ++ f = _PyFrame_New_NoTrack(code); + if (f == NULL) { + Py_DECREF(func); + return NULL; +@@ -1085,10 +1137,13 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, + static int + _PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, int opcode, int oparg) + { ++ int check_oparg; ++ _Py_CODEUNIT *instruction; ++ + // This only works when opcode is a non-quickened form: + assert(_PyOpcode_Deopt[opcode] == opcode); +- int check_oparg = 0; +- for (_Py_CODEUNIT *instruction = _PyCode_CODE(frame->f_code); ++ check_oparg = 0; ++ for (instruction = _PyCode_CODE(frame->f_code); + instruction < frame->prev_instr; instruction++) + { + int check_opcode = _PyOpcode_Deopt[_Py_OPCODE(*instruction)]; +@@ -1113,6 +1168,8 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) { + PyObject *locals; + PyObject **fast; + PyCodeObject *co; ++ int lasti; ++ int i; + locals = frame->f_locals; + if (locals == NULL) { + locals = frame->f_locals = PyDict_New(); +@@ -1123,13 +1180,16 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) { + fast = _PyFrame_GetLocalsArray(frame); + // COPY_FREE_VARS has no quickened forms, so no need to use _PyOpcode_Deopt + // here: +- int lasti = _PyInterpreterFrame_LASTI(frame); ++ lasti = _PyInterpreterFrame_LASTI(frame); + if (lasti < 0 && _Py_OPCODE(_PyCode_CODE(co)[0]) == COPY_FREE_VARS) { ++ PyObject *closure; ++ int offset; ++ + /* Free vars have not been initialized -- Do that */ +- PyCodeObject *co = frame->f_code; +- PyObject *closure = frame->f_func->func_closure; +- int offset = co->co_nlocals + co->co_nplaincellvars; +- for (int i = 0; i < co->co_nfreevars; ++i) { ++ co = frame->f_code; ++ closure = frame->f_func->func_closure; ++ offset = co->co_nlocals + co->co_nplaincellvars; ++ for (i = 0; i < co->co_nfreevars; ++i) { + PyObject *o = PyTuple_GET_ITEM(closure, i); + Py_INCREF(o); + frame->localsplus[offset + i] = o; +@@ -1137,8 +1197,12 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) { + // COPY_FREE_VARS doesn't have inline CACHEs, either: + frame->prev_instr = _PyCode_CODE(frame->f_code); + } +- for (int i = 0; i < co->co_nlocalsplus; i++) { +- _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i); ++ for (i = 0; i < co->co_nlocalsplus; i++) { ++ PyObject *name; ++ PyObject *value; ++ _PyLocals_Kind kind; ++ ++ kind = _PyLocals_GetKind(co->co_localspluskinds, i); + + /* If the namespace is unoptimized, then one of the + following cases applies: +@@ -1152,8 +1216,8 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) { + continue; + } + +- PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); +- PyObject *value = fast[i]; ++ name = PyTuple_GET_ITEM(co->co_localsplusnames, i); ++ value = fast[i]; + if (frame->stacktop) { + if (kind & CO_FAST_FREE) { + // The cell was set by COPY_FREE_VARS. +@@ -1203,12 +1267,14 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) { + int + PyFrame_FastToLocalsWithError(PyFrameObject *f) + { ++ int err; ++ + assert(!_PyFrame_IsIncomplete(f->f_frame)); + if (f == NULL) { + PyErr_BadInternalCall(); + return -1; + } +- int err = _PyFrame_FastToLocalsWithError(f->f_frame); ++ err = _PyFrame_FastToLocalsWithError(f->f_frame); + if (err == 0) { + f->f_fast_as_locals = 1; + } +@@ -1235,6 +1301,7 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear) + PyObject **fast; + PyObject *error_type, *error_value, *error_traceback; + PyCodeObject *co; ++ int i; + locals = frame->f_locals; + if (locals == NULL) { + return; +@@ -1243,15 +1310,21 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear) + co = frame->f_code; + + PyErr_Fetch(&error_type, &error_value, &error_traceback); +- for (int i = 0; i < co->co_nlocalsplus; i++) { +- _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i); ++ for (i = 0; i < co->co_nlocalsplus; i++) { ++ PyObject *name; ++ PyObject *value; ++ PyObject *oldvalue; ++ PyObject *cell; ++ _PyLocals_Kind kind; ++ ++ kind = _PyLocals_GetKind(co->co_localspluskinds, i); + + /* Same test as in PyFrame_FastToLocals() above. */ + if (kind & CO_FAST_FREE && !(co->co_flags & CO_OPTIMIZED)) { + continue; + } +- PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); +- PyObject *value = PyObject_GetItem(locals, name); ++ name = PyTuple_GET_ITEM(co->co_localsplusnames, i); ++ value = PyObject_GetItem(locals, name); + /* We only care about NULLs if clear is true. */ + if (value == NULL) { + PyErr_Clear(); +@@ -1259,8 +1332,8 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear) + continue; + } + } +- PyObject *oldvalue = fast[i]; +- PyObject *cell = NULL; ++ oldvalue = fast[i]; ++ cell = NULL; + if (kind == CO_FAST_FREE) { + // The cell was set when the frame was created from + // the function's closure. +@@ -1316,9 +1389,11 @@ int _PyFrame_IsEntryFrame(PyFrameObject *frame) + PyCodeObject * + PyFrame_GetCode(PyFrameObject *frame) + { ++ PyCodeObject *code; ++ + assert(frame != NULL); + assert(!_PyFrame_IsIncomplete(frame->f_frame)); +- PyCodeObject *code = frame->f_frame->f_code; ++ code = frame->f_frame->f_code; + assert(code != NULL); + Py_INCREF(code); + return code; +@@ -1328,9 +1403,11 @@ PyFrame_GetCode(PyFrameObject *frame) + PyFrameObject* + PyFrame_GetBack(PyFrameObject *frame) + { ++ PyFrameObject *back; ++ + assert(frame != NULL); + assert(!_PyFrame_IsIncomplete(frame->f_frame)); +- PyFrameObject *back = frame->f_back; ++ back = frame->f_back; + if (back == NULL) { + _PyInterpreterFrame *prev = frame->f_frame->previous; + while (prev && _PyFrame_IsIncomplete(prev)) { +@@ -1368,8 +1445,10 @@ PyFrame_GetBuiltins(PyFrameObject *frame) + int + PyFrame_GetLasti(PyFrameObject *frame) + { ++ int lasti; ++ + assert(!_PyFrame_IsIncomplete(frame->f_frame)); +- int lasti = _PyInterpreterFrame_LASTI(frame->f_frame); ++ lasti = _PyInterpreterFrame_LASTI(frame->f_frame); + if (lasti < 0) { + return -1; + } +@@ -1379,11 +1458,13 @@ PyFrame_GetLasti(PyFrameObject *frame) + PyObject * + PyFrame_GetGenerator(PyFrameObject *frame) + { ++ PyGenObject *gen; ++ + assert(!_PyFrame_IsIncomplete(frame->f_frame)); + if (frame->f_frame->owner != FRAME_OWNED_BY_GENERATOR) { + return NULL; + } +- PyGenObject *gen = _PyFrame_GetGenerator(frame->f_frame); ++ gen = _PyFrame_GetGenerator(frame->f_frame); + return Py_NewRef(gen); + } + +diff --git a/Objects/funcobject.c b/Objects/funcobject.c +index 3a05532..be3dbc9 100644 +--- a/Objects/funcobject.c ++++ b/Objects/funcobject.c +@@ -48,16 +48,25 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr) + PyObject * + PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname) + { ++ PyThreadState *tstate; ++ PyCodeObject *code_obj; ++ PyObject *name; ++ PyObject *consts; ++ PyObject *doc; ++ PyObject *module; ++ PyObject *builtins; ++ PyFunctionObject *op; ++ + assert(globals != NULL); + assert(PyDict_Check(globals)); + Py_INCREF(globals); + +- PyThreadState *tstate = _PyThreadState_GET(); ++ tstate = _PyThreadState_GET(); + +- PyCodeObject *code_obj = (PyCodeObject *)code; ++ code_obj = (PyCodeObject *)code; + Py_INCREF(code_obj); + +- PyObject *name = code_obj->co_name; ++ name = code_obj->co_name; + assert(name != NULL); + Py_INCREF(name); + +@@ -67,9 +76,8 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname + assert(qualname != NULL); + Py_INCREF(qualname); + +- PyObject *consts = code_obj->co_consts; ++ consts = code_obj->co_consts; + assert(PyTuple_Check(consts)); +- PyObject *doc; + if (PyTuple_Size(consts) >= 1) { + doc = PyTuple_GetItem(consts, 0); + if (!PyUnicode_Check(doc)) { +@@ -82,8 +90,8 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname + Py_INCREF(doc); + + // __module__: Use globals['__name__'] if it exists, or NULL. +- PyObject *module = PyDict_GetItemWithError(globals, &_Py_ID(__name__)); +- PyObject *builtins = NULL; ++ module = PyDict_GetItemWithError(globals, &_Py_ID(__name__)); ++ builtins = NULL; + if (module == NULL && _PyErr_Occurred(tstate)) { + goto error; + } +@@ -95,7 +103,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname + } + Py_INCREF(builtins); + +- PyFunctionObject *op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type); ++ op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type); + if (op == NULL) { + goto error; + } +@@ -133,13 +141,15 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname + + uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func) + { ++ uint32_t v; ++ + if (func->func_version != 0) { + return func->func_version; + } + if (next_func_version == 0) { + return 0; + } +- uint32_t v = next_func_version++; ++ v = next_func_version++; + func->func_version = v; + return v; + } +@@ -283,6 +293,7 @@ func_get_annotation_dict(PyFunctionObject *op) + return NULL; + } + if (PyTuple_CheckExact(op->func_annotations)) { ++ Py_ssize_t i; + PyObject *ann_tuple = op->func_annotations; + PyObject *ann_dict = PyDict_New(); + if (ann_dict == NULL) { +@@ -291,7 +302,7 @@ func_get_annotation_dict(PyFunctionObject *op) + + assert(PyTuple_GET_SIZE(ann_tuple) % 2 == 0); + +- for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(ann_tuple); i += 2) { ++ for (i = 0; i < PyTuple_GET_SIZE(ann_tuple); i += 2) { + int err = PyDict_SetItem(ann_dict, + PyTuple_GET_ITEM(ann_tuple, i), + PyTuple_GET_ITEM(ann_tuple, i + 1)); +@@ -528,12 +539,14 @@ func_set_kwdefaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignor + static PyObject * + func_get_annotations(PyFunctionObject *op, void *Py_UNUSED(ignored)) + { ++ PyObject *d; ++ + if (op->func_annotations == NULL) { + op->func_annotations = PyDict_New(); + if (op->func_annotations == NULL) + return NULL; + } +- PyObject *d = func_get_annotation_dict(op); ++ d = func_get_annotation_dict(op); + if (d) { + Py_INCREF(d); + } +@@ -801,7 +814,10 @@ PyTypeObject PyFunction_Type = { + static int + functools_copy_attr(PyObject *wrapper, PyObject *wrapped, PyObject *name) + { +- PyObject *value = PyObject_GetAttr(wrapped, name); ++ PyObject *value; ++ int res; ++ ++ value = PyObject_GetAttr(wrapped, name); + if (value == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); +@@ -810,7 +826,7 @@ functools_copy_attr(PyObject *wrapper, PyObject *wrapped, PyObject *name) + return -1; + } + +- int res = PyObject_SetAttr(wrapper, name, value); ++ res = PyObject_SetAttr(wrapper, name, value); + Py_DECREF(value); + return res; + } +diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c +index 139bab7..a012ddb 100644 +--- a/Objects/genericaliasobject.c ++++ b/Objects/genericaliasobject.c +@@ -126,6 +126,7 @@ ga_repr(PyObject *self) + { + gaobject *alias = (gaobject *)self; + Py_ssize_t len = PyTuple_GET_SIZE(alias->args); ++ Py_ssize_t i; + + _PyUnicodeWriter writer; + _PyUnicodeWriter_Init(&writer); +@@ -141,13 +142,15 @@ ga_repr(PyObject *self) + if (_PyUnicodeWriter_WriteASCIIString(&writer, "[", 1) < 0) { + goto error; + } +- for (Py_ssize_t i = 0; i < len; i++) { ++ for (i = 0; i < len; i++) { ++ PyObject *p; ++ + if (i > 0) { + if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) { + goto error; + } + } +- PyObject *p = PyTuple_GET_ITEM(alias->args, i); ++ p = PyTuple_GET_ITEM(alias->args, i); + if (ga_repr_item(&writer, p) < 0) { + goto error; + } +@@ -171,7 +174,8 @@ ga_repr(PyObject *self) + static Py_ssize_t + tuple_index(PyObject *self, Py_ssize_t len, PyObject *item) + { +- for (Py_ssize_t i = 0; i < len; i++) { ++ Py_ssize_t i; ++ for (i = 0; i < len; i++) { + if (PyTuple_GET_ITEM(self, i) == item) { + return i; + } +@@ -194,13 +198,16 @@ static Py_ssize_t + tuple_extend(PyObject **dst, Py_ssize_t dstindex, + PyObject **src, Py_ssize_t count) + { ++ Py_ssize_t i; ++ + assert(count >= 0); + if (_PyTuple_Resize(dst, PyTuple_GET_SIZE(*dst) + count - 1) != 0) { + return -1; + } + assert(dstindex + count <= PyTuple_GET_SIZE(*dst)); +- for (Py_ssize_t i = 0; i < count; ++i) { +- PyObject *item = src[i]; ++ for (i = 0; i < count; ++i) { ++ PyObject *item; ++ item = src[i]; + Py_INCREF(item); + PyTuple_SET_ITEM(*dst, dstindex + i, item); + } +@@ -210,13 +217,19 @@ tuple_extend(PyObject **dst, Py_ssize_t dstindex, + PyObject * + _Py_make_parameters(PyObject *args) + { +- Py_ssize_t nargs = PyTuple_GET_SIZE(args); +- Py_ssize_t len = nargs; +- PyObject *parameters = PyTuple_New(len); ++ Py_ssize_t nargs; ++ Py_ssize_t len; ++ PyObject *parameters; ++ Py_ssize_t iparam; ++ Py_ssize_t iarg; ++ ++ nargs = PyTuple_GET_SIZE(args); ++ len = nargs; ++ parameters = PyTuple_New(len); + if (parameters == NULL) + return NULL; +- Py_ssize_t iparam = 0; +- for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) { ++ iparam = 0; ++ for (iarg = 0; iarg < nargs; iarg++) { + PyObject *t = PyTuple_GET_ITEM(args, iarg); + PyObject *subst; + // We don't want __parameters__ descriptor of a bare Python class. +@@ -241,6 +254,8 @@ _Py_make_parameters(PyObject *args) + if (subparams && PyTuple_Check(subparams)) { + Py_ssize_t len2 = PyTuple_GET_SIZE(subparams); + Py_ssize_t needed = len2 - 1 - (iarg - iparam); ++ Py_ssize_t j; ++ + if (needed > 0) { + len += needed; + if (_PyTuple_Resize(¶meters, len) < 0) { +@@ -249,7 +264,7 @@ _Py_make_parameters(PyObject *args) + return NULL; + } + } +- for (Py_ssize_t j = 0; j < len2; j++) { ++ for (j = 0; j < len2; j++) { + PyObject *t2 = PyTuple_GET_ITEM(subparams, j); + iparam += tuple_add(parameters, iparam, t2); + } +@@ -280,6 +295,8 @@ subs_tvars(PyObject *obj, PyObject *params, + return NULL; + } + if (subparams && PyTuple_Check(subparams) && PyTuple_GET_SIZE(subparams)) { ++ Py_ssize_t i; ++ Py_ssize_t j; + Py_ssize_t nparams = PyTuple_GET_SIZE(params); + Py_ssize_t nsubargs = PyTuple_GET_SIZE(subparams); + PyObject *subargs = PyTuple_New(nsubargs); +@@ -287,8 +304,8 @@ subs_tvars(PyObject *obj, PyObject *params, + Py_DECREF(subparams); + return NULL; + } +- Py_ssize_t j = 0; +- for (Py_ssize_t i = 0; i < nsubargs; ++i) { ++ j = 0; ++ for (i = 0; i < nsubargs; ++i) { + PyObject *arg = PyTuple_GET_ITEM(subparams, i); + Py_ssize_t iparam = tuple_index(params, nparams, arg); + if (iparam >= 0) { +@@ -325,10 +342,12 @@ static int + _is_unpacked_typevartuple(PyObject *arg) + { + PyObject *tmp; ++ int res; ++ + if (PyType_Check(arg)) { // TODO: Add test + return 0; + } +- int res = _PyObject_LookupAttr(arg, &_Py_ID(__typing_is_unpacked_typevartuple__), &tmp); ++ res = _PyObject_LookupAttr(arg, &_Py_ID(__typing_is_unpacked_typevartuple__), &tmp); + if (res > 0) { + res = PyObject_IsTrue(tmp); + Py_DECREF(tmp); +@@ -364,14 +383,20 @@ _unpacked_tuple_args(PyObject *arg) + static PyObject * + _unpack_args(PyObject *item) + { +- PyObject *newargs = PyList_New(0); ++ PyObject *newargs; ++ int is_tuple; ++ Py_ssize_t nitems; ++ PyObject **argitems; ++ Py_ssize_t i; ++ ++ newargs = PyList_New(0); + if (newargs == NULL) { + return NULL; + } +- int is_tuple = PyTuple_Check(item); +- Py_ssize_t nitems = is_tuple ? PyTuple_GET_SIZE(item) : 1; +- PyObject **argitems = is_tuple ? &PyTuple_GET_ITEM(item, 0) : &item; +- for (Py_ssize_t i = 0; i < nitems; i++) { ++ is_tuple = PyTuple_Check(item); ++ nitems = is_tuple ? PyTuple_GET_SIZE(item) : 1; ++ argitems = is_tuple ? &PyTuple_GET_ITEM(item, 0) : &item; ++ for (i = 0; i < nitems; i++) { + item = argitems[i]; + if (!PyType_Check(item)) { + PyObject *subargs = _unpacked_tuple_args(item); +@@ -406,14 +431,24 @@ _unpack_args(PyObject *item) + PyObject * + _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObject *item) + { +- Py_ssize_t nparams = PyTuple_GET_SIZE(parameters); ++ Py_ssize_t nparams; ++ Py_ssize_t i; ++ int is_tuple; ++ Py_ssize_t nitems; ++ PyObject **argitems; ++ Py_ssize_t nargs; ++ PyObject *newargs; ++ Py_ssize_t iarg; ++ Py_ssize_t jarg; ++ ++ nparams = PyTuple_GET_SIZE(parameters); + if (nparams == 0) { + return PyErr_Format(PyExc_TypeError, + "%R is not a generic class", + self); + } + item = _unpack_args(item); +- for (Py_ssize_t i = 0; i < nparams; i++) { ++ for (i = 0; i < nparams; i++) { + PyObject *param = PyTuple_GET_ITEM(parameters, i); + PyObject *prepare, *tmp; + if (_PyObject_LookupAttr(param, &_Py_ID(__typing_prepare_subst__), &prepare) < 0) { +@@ -434,9 +469,9 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje + } + } + } +- int is_tuple = PyTuple_Check(item); +- Py_ssize_t nitems = is_tuple ? PyTuple_GET_SIZE(item) : 1; +- PyObject **argitems = is_tuple ? &PyTuple_GET_ITEM(item, 0) : &item; ++ is_tuple = PyTuple_Check(item); ++ nitems = is_tuple ? PyTuple_GET_SIZE(item) : 1; ++ argitems = is_tuple ? &PyTuple_GET_ITEM(item, 0) : &item; + if (nitems != nparams) { + Py_DECREF(item); + return PyErr_Format(PyExc_TypeError, +@@ -450,14 +485,17 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje + t = dict[str, T]; t[int] -> newargs = [str, int] + t = dict[T, list[S]]; t[str, int] -> newargs = [str, list[int]] + */ +- Py_ssize_t nargs = PyTuple_GET_SIZE(args); +- PyObject *newargs = PyTuple_New(nargs); ++ nargs = PyTuple_GET_SIZE(args); ++ newargs = PyTuple_New(nargs); + if (newargs == NULL) { + Py_DECREF(item); + return NULL; + } +- for (Py_ssize_t iarg = 0, jarg = 0; iarg < nargs; iarg++) { ++ for (iarg = 0, jarg = 0; iarg < nargs; iarg++) { + PyObject *arg = PyTuple_GET_ITEM(args, iarg); ++ int unpack; ++ PyObject *subst; ++ + if (PyType_Check(arg)) { + Py_INCREF(arg); + PyTuple_SET_ITEM(newargs, jarg, arg); +@@ -465,13 +503,12 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje + continue; + } + +- int unpack = _is_unpacked_typevartuple(arg); ++ unpack = _is_unpacked_typevartuple(arg); + if (unpack < 0) { + Py_DECREF(newargs); + Py_DECREF(item); + return NULL; + } +- PyObject *subst; + if (_PyObject_LookupAttr(arg, &_Py_ID(__typing_subst__), &subst) < 0) { + Py_DECREF(newargs); + Py_DECREF(item); +@@ -519,6 +556,9 @@ static PyObject * + ga_getitem(PyObject *self, PyObject *item) + { + gaobject *alias = (gaobject *)self; ++ PyObject *newargs; ++ PyObject *res; ++ + // Populate __parameters__ if needed. + if (alias->parameters == NULL) { + alias->parameters = _Py_make_parameters(alias->args); +@@ -527,12 +567,12 @@ ga_getitem(PyObject *self, PyObject *item) + } + } + +- PyObject *newargs = _Py_subs_parameters(self, alias->args, alias->parameters, item); ++ newargs = _Py_subs_parameters(self, alias->args, alias->parameters, item); + if (newargs == NULL) { + return NULL; + } + +- PyObject *res = Py_GenericAlias(alias->origin, newargs); ++ res = Py_GenericAlias(alias->origin, newargs); + ((gaobject *)res)->starred = alias->starred; + + Py_DECREF(newargs); +@@ -540,19 +580,22 @@ ga_getitem(PyObject *self, PyObject *item) + } + + static PyMappingMethods ga_as_mapping = { +- .mp_subscript = ga_getitem, ++ 0, ++ ga_getitem, ++ 0, + }; + + static Py_hash_t + ga_hash(PyObject *self) + { + gaobject *alias = (gaobject *)self; ++ Py_hash_t h1; + // TODO: Hash in the hash for the origin + Py_hash_t h0 = PyObject_Hash(alias->origin); + if (h0 == -1) { + return -1; + } +- Py_hash_t h1 = PyObject_Hash(alias->args); ++ h1 = PyObject_Hash(alias->args); + if (h1 == -1) { + return -1; + } +@@ -612,8 +655,10 @@ static PyObject * + ga_getattro(PyObject *self, PyObject *name) + { + gaobject *alias = (gaobject *)self; ++ const char * const *p; ++ + if (PyUnicode_Check(name)) { +- for (const char * const *p = attr_exceptions; ; p++) { ++ for (p = attr_exceptions; ; p++) { + if (*p == NULL) { + return PyObject_GetAttr(alias->origin, name); + } +@@ -628,6 +673,10 @@ ga_getattro(PyObject *self, PyObject *name) + static PyObject * + ga_richcompare(PyObject *a, PyObject *b, int op) + { ++ gaobject *aa; ++ gaobject *bb; ++ int eq; ++ + if (!_PyGenericAlias_Check(b) || + (op != Py_EQ && op != Py_NE)) + { +@@ -647,12 +696,12 @@ ga_richcompare(PyObject *a, PyObject *b, int op) + } + } + +- gaobject *aa = (gaobject *)a; +- gaobject *bb = (gaobject *)b; ++ aa = (gaobject *)a; ++ bb = (gaobject *)b; + if (aa->starred != bb->starred) { + Py_RETURN_FALSE; + } +- int eq = PyObject_RichCompareBool(aa->origin, bb->origin, Py_EQ); ++ eq = PyObject_RichCompareBool(aa->origin, bb->origin, Py_EQ); + if (eq < 0) { + return NULL; + } +@@ -707,13 +756,18 @@ static PyObject * + ga_dir(PyObject *self, PyObject *Py_UNUSED(ignored)) + { + gaobject *alias = (gaobject *)self; +- PyObject *dir = PyObject_Dir(alias->origin); ++ PyObject *dir; ++ PyObject *dir_entry; ++ const char * const *p; ++ int contains; ++ ++ dir = PyObject_Dir(alias->origin); + if (dir == NULL) { + return NULL; + } + +- PyObject *dir_entry = NULL; +- for (const char * const *p = attr_exceptions; ; p++) { ++ dir_entry = NULL; ++ for (p = attr_exceptions; ; p++) { + if (*p == NULL) { + break; + } +@@ -722,7 +776,7 @@ ga_dir(PyObject *self, PyObject *Py_UNUSED(ignored)) + if (dir_entry == NULL) { + goto error; + } +- int contains = PySequence_Contains(dir, dir_entry); ++ contains = PySequence_Contains(dir, dir_entry); + if (contains < 0) { + goto error; + } +@@ -822,15 +876,19 @@ setup_ga(gaobject *alias, PyObject *origin, PyObject *args) { + static PyObject * + ga_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + { ++ PyObject *origin; ++ PyObject *arguments; ++ gaobject *self; ++ + if (!_PyArg_NoKeywords("GenericAlias", kwds)) { + return NULL; + } + if (!_PyArg_CheckPositional("GenericAlias", PyTuple_GET_SIZE(args), 2, 2)) { + return NULL; + } +- PyObject *origin = PyTuple_GET_ITEM(args, 0); +- PyObject *arguments = PyTuple_GET_ITEM(args, 1); +- gaobject *self = (gaobject *)type->tp_alloc(type, 0); ++ origin = PyTuple_GET_ITEM(args, 0); ++ arguments = PyTuple_GET_ITEM(args, 1); ++ self = (gaobject *)type->tp_alloc(type, 0); + if (self == NULL) { + return NULL; + } +@@ -841,18 +899,25 @@ ga_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + return (PyObject *)self; + } + ++#if defined(MS_WINDOWS) && defined(_MSC_VER) && _MSC_VER < 1300 ++static PyNumberMethods ga_as_number = {0}; ++#else + static PyNumberMethods ga_as_number = { + .nb_or = _Py_union_type_or, // Add __or__ function + }; ++#endif + + static PyObject * + ga_iternext(gaiterobject *gi) { ++ gaobject *alias; ++ PyObject *starred_alias; ++ + if (gi->obj == NULL) { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } +- gaobject *alias = (gaobject *)gi->obj; +- PyObject *starred_alias = Py_GenericAlias(alias->origin, alias->args); ++ alias = (gaobject *)gi->obj; ++ starred_alias = Py_GenericAlias(alias->origin, alias->args); + if (starred_alias == NULL) { + return NULL; + } +@@ -905,6 +970,11 @@ static PyMethodDef ga_iter_methods[] = { + + // gh-91632: _Py_GenericAliasIterType is exported to be cleared + // in _PyTypes_FiniTypes. ++#if defined(MS_WINDOWS) && defined(_MSC_VER) && _MSC_VER < 1300 ++PyTypeObject _Py_GenericAliasIterType = { ++ PyVarObject_HEAD_INIT(&PyType_Type, 0) ++}; ++#else + PyTypeObject _Py_GenericAliasIterType = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + .tp_name = "generic_alias_iterator", +@@ -917,6 +987,7 @@ PyTypeObject _Py_GenericAliasIterType = { + .tp_clear = (inquiry)ga_iter_clear, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, + }; ++#endif + + static PyObject * + ga_iter(PyObject *self) { +@@ -932,6 +1003,11 @@ ga_iter(PyObject *self) { + // TODO: + // - argument clinic? + // - cache? ++#if defined(MS_WINDOWS) && defined(_MSC_VER) && _MSC_VER < 1300 ++PyTypeObject Py_GenericAliasType = { ++ PyVarObject_HEAD_INIT(&PyType_Type, 0) ++}; ++#else + PyTypeObject Py_GenericAliasType = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + .tp_name = "types.GenericAlias", +@@ -957,6 +1033,48 @@ PyTypeObject Py_GenericAliasType = { + .tp_iter = (getiterfunc)ga_iter, + .tp_vectorcall_offset = offsetof(gaobject, vectorcall), + }; ++#endif ++ ++#if defined(MS_WINDOWS) && defined(_MSC_VER) && _MSC_VER < 1300 ++void ++_Py_GenericAlias_InitTypes(void) ++{ ++ ga_as_number.nb_or = _Py_union_type_or; ++ ++ _Py_GenericAliasIterType.tp_name = "generic_alias_iterator"; ++ _Py_GenericAliasIterType.tp_basicsize = sizeof(gaiterobject); ++ _Py_GenericAliasIterType.tp_iter = PyObject_SelfIter; ++ _Py_GenericAliasIterType.tp_iternext = (iternextfunc)ga_iternext; ++ _Py_GenericAliasIterType.tp_traverse = (traverseproc)ga_iter_traverse; ++ _Py_GenericAliasIterType.tp_methods = ga_iter_methods; ++ _Py_GenericAliasIterType.tp_dealloc = (destructor)ga_iter_dealloc; ++ _Py_GenericAliasIterType.tp_clear = (inquiry)ga_iter_clear; ++ _Py_GenericAliasIterType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC; ++ ++ Py_GenericAliasType.tp_name = "types.GenericAlias"; ++ Py_GenericAliasType.tp_doc = genericalias__doc__; ++ Py_GenericAliasType.tp_basicsize = sizeof(gaobject); ++ Py_GenericAliasType.tp_dealloc = ga_dealloc; ++ Py_GenericAliasType.tp_repr = ga_repr; ++ Py_GenericAliasType.tp_as_number = &ga_as_number; ++ Py_GenericAliasType.tp_as_mapping = &ga_as_mapping; ++ Py_GenericAliasType.tp_hash = ga_hash; ++ Py_GenericAliasType.tp_call = ga_call; ++ Py_GenericAliasType.tp_getattro = ga_getattro; ++ Py_GenericAliasType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_VECTORCALL; ++ Py_GenericAliasType.tp_traverse = ga_traverse; ++ Py_GenericAliasType.tp_richcompare = ga_richcompare; ++ Py_GenericAliasType.tp_weaklistoffset = offsetof(gaobject, weakreflist); ++ Py_GenericAliasType.tp_methods = ga_methods; ++ Py_GenericAliasType.tp_members = ga_members; ++ Py_GenericAliasType.tp_alloc = PyType_GenericAlloc; ++ Py_GenericAliasType.tp_new = ga_new; ++ Py_GenericAliasType.tp_free = PyObject_GC_Del; ++ Py_GenericAliasType.tp_getset = ga_properties; ++ Py_GenericAliasType.tp_iter = (getiterfunc)ga_iter; ++ Py_GenericAliasType.tp_vectorcall_offset = offsetof(gaobject, vectorcall); ++} ++#endif + + PyObject * + Py_GenericAlias(PyObject *origin, PyObject *args) +diff --git a/Objects/genobject.c b/Objects/genobject.c +index b9a0c30..fba070e 100644 +--- a/Objects/genobject.c ++++ b/Objects/genobject.c +@@ -37,9 +37,10 @@ gen_traverse(PyGenObject *gen, visitproc visit, void *arg) + Py_VISIT(gen->gi_qualname); + if (gen->gi_frame_state < FRAME_CLEARED) { + _PyInterpreterFrame *frame = (_PyInterpreterFrame *)(gen->gi_iframe); ++ int err; + assert(frame->frame_obj == NULL || + frame->frame_obj->f_frame->owner == FRAME_OWNED_BY_GENERATOR); +- int err = _PyFrame_Traverse(frame, visit, arg); ++ err = _PyFrame_Traverse(frame, visit, arg); + if (err) { + return err; + } +@@ -348,6 +349,7 @@ PyObject * + _PyGen_yf(PyGenObject *gen) + { + PyObject *yf = NULL; ++ _Py_CODEUNIT next; + + if (gen->gi_frame_state < FRAME_CLEARED) { + _PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe; +@@ -359,7 +361,7 @@ _PyGen_yf(PyGenObject *gen) + assert(_Py_OPCODE(_PyCode_CODE(gen->gi_code)[0]) != SEND); + return NULL; + } +- _Py_CODEUNIT next = frame->prev_instr[1]; ++ next = frame->prev_instr[1]; + if (_PyOpcode_Deopt[_Py_OPCODE(next)] != RESUME || _Py_OPARG(next) < 2) + { + /* Not in a yield from */ +@@ -445,6 +447,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, + if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) { + /* `yf` is a generator or a coroutine. */ + PyThreadState *tstate = _PyThreadState_GET(); ++ PyFrameState state; + /* Since we are fast-tracking things by skipping the eval loop, + we need to update the current frame so the stack trace + will be reported correctly to the user. */ +@@ -455,7 +458,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, + tstate->cframe->current_frame = frame; + /* Close the generator that we are currently iterating with + 'yield from' or awaiting on with 'await'. */ +- PyFrameState state = gen->gi_frame_state; ++ state = gen->gi_frame_state; + gen->gi_frame_state = FRAME_EXECUTING; + ret = _gen_throw((PyGenObject *)yf, close_on_genexit, + typ, val, tb); +@@ -465,6 +468,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, + } else { + /* `yf` is an iterator or a coroutine-like object. */ + PyObject *meth; ++ PyFrameState state; + if (_PyObject_LookupAttr(yf, &_Py_ID(throw), &meth) < 0) { + Py_DECREF(yf); + return NULL; +@@ -473,7 +477,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, + Py_DECREF(yf); + goto throw_here; + } +- PyFrameState state = gen->gi_frame_state; ++ state = gen->gi_frame_state; + gen->gi_frame_state = FRAME_EXECUTING; + ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL); + gen->gi_frame_state = state; +@@ -482,6 +486,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, + Py_DECREF(yf); + if (!ret) { + PyObject *val; ++ int jump; + /* Pop subiterator from stack */ + assert(gen->gi_frame_state < FRAME_CLEARED); + ret = _PyFrame_StackPop((_PyInterpreterFrame *)gen->gi_iframe); +@@ -493,7 +498,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, + assert(_PyInterpreterFrame_LASTI(frame) >= 0); + /* Backup to SEND */ + assert(_Py_OPCODE(frame->prev_instr[-1]) == SEND); +- int jump = _Py_OPARG(frame->prev_instr[-1]); ++ jump = _Py_OPARG(frame->prev_instr[-1]); + frame->prev_instr += jump - 1; + if (_PyGen_FetchStopIterationValue(&val) == 0) { + ret = gen_send(gen, val); +@@ -810,8 +815,10 @@ static PyObject * + gen_sizeof(PyGenObject *gen, PyObject *Py_UNUSED(ignored)) + { + Py_ssize_t res; ++ PyCodeObject *code; ++ + res = offsetof(PyGenObject, gi_iframe) + offsetof(_PyInterpreterFrame, localsplus); +- PyCodeObject *code = gen->gi_code; ++ code = gen->gi_code; + res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *); + return PyLong_FromSsize_t(res); + } +@@ -918,7 +925,12 @@ compute_cr_origin(int origin_depth, _PyInterpreterFrame *current_frame); + PyObject * + _Py_MakeCoro(PyFunctionObject *func) + { +- int coro_flags = ((PyCodeObject *)func->func_code)->co_flags & ++ int coro_flags; ++ PyObject *coro; ++ PyThreadState *tstate; ++ int origin_depth; ++ ++ coro_flags = ((PyCodeObject *)func->func_code)->co_flags & + (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR); + assert(coro_flags); + if (coro_flags == CO_GENERATOR) { +@@ -937,18 +949,19 @@ _Py_MakeCoro(PyFunctionObject *func) + return (PyObject*)o; + } + assert (coro_flags == CO_COROUTINE); +- PyObject *coro = make_gen(&PyCoro_Type, func); ++ coro = make_gen(&PyCoro_Type, func); + if (!coro) { + return NULL; + } +- PyThreadState *tstate = _PyThreadState_GET(); +- int origin_depth = tstate->coroutine_origin_tracking_depth; ++ tstate = _PyThreadState_GET(); ++ origin_depth = tstate->coroutine_origin_tracking_depth; + + if (origin_depth == 0) { + ((PyCoroObject *)coro)->cr_origin_or_finalizer = NULL; + } else { ++ PyObject *cr_origin; + assert(_PyEval_GetFrame()); +- PyObject *cr_origin = compute_cr_origin(origin_depth, _PyEval_GetFrame()->previous); ++ cr_origin = compute_cr_origin(origin_depth, _PyEval_GetFrame()->previous); + ((PyCoroObject *)coro)->cr_origin_or_finalizer = cr_origin; + if (!cr_origin) { + Py_DECREF(coro); +@@ -965,6 +978,7 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f, + PyCodeObject *code = f->f_frame->f_code; + int size = code->co_nlocalsplus + code->co_stacksize; + PyGenObject *gen = PyObject_GC_NewVar(PyGenObject, type, size); ++ _PyInterpreterFrame *frame; + if (gen == NULL) { + Py_DECREF(f); + return NULL; +@@ -972,7 +986,7 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f, + /* Copy the frame */ + assert(f->f_frame->frame_obj == NULL); + assert(f->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT); +- _PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe; ++ frame = (_PyInterpreterFrame *)gen->gi_iframe; + _PyFrame_Copy((_PyInterpreterFrame *)f->_f_frame_data, frame); + gen->gi_frame_state = FRAME_CREATED; + assert(frame->frame_obj == f); +@@ -1327,20 +1341,25 @@ PyTypeObject _PyCoroWrapper_Type = { + static PyObject * + compute_cr_origin(int origin_depth, _PyInterpreterFrame *current_frame) + { +- _PyInterpreterFrame *frame = current_frame; ++ _PyInterpreterFrame *frame; ++ int frame_count; ++ PyObject *cr_origin; ++ int i; ++ ++ frame = current_frame; + /* First count how many frames we have */ +- int frame_count = 0; ++ frame_count = 0; + for (; frame && frame_count < origin_depth; ++frame_count) { + frame = frame->previous; + } + + /* Now collect them */ +- PyObject *cr_origin = PyTuple_New(frame_count); ++ cr_origin = PyTuple_New(frame_count); + if (cr_origin == NULL) { + return NULL; + } + frame = current_frame; +- for (int i = 0; i < frame_count; ++i) { ++ for (i = 0; i < frame_count; ++i) { + PyCodeObject *code = frame->f_code; + int line = _PyInterpreterFrame_GetLine(frame); + PyObject *frameinfo = Py_BuildValue("OiO", code->co_filename, line, +@@ -1359,18 +1378,24 @@ compute_cr_origin(int origin_depth, _PyInterpreterFrame *current_frame) + PyObject * + PyCoro_New(PyFrameObject *f, PyObject *name, PyObject *qualname) + { +- PyObject *coro = gen_new_with_qualname(&PyCoro_Type, f, name, qualname); ++ PyObject *coro; ++ PyThreadState *tstate; ++ int origin_depth; ++ ++ coro = gen_new_with_qualname(&PyCoro_Type, f, name, qualname); + if (!coro) { + return NULL; + } + +- PyThreadState *tstate = _PyThreadState_GET(); +- int origin_depth = tstate->coroutine_origin_tracking_depth; ++ tstate = _PyThreadState_GET(); ++ origin_depth = tstate->coroutine_origin_tracking_depth; + + if (origin_depth == 0) { + ((PyCoroObject *)coro)->cr_origin_or_finalizer = NULL; + } else { +- PyObject *cr_origin = compute_cr_origin(origin_depth, _PyEval_GetFrame()); ++ PyObject *cr_origin; ++ ++ cr_origin = compute_cr_origin(origin_depth, _PyEval_GetFrame()); + ((PyCoroObject *)coro)->cr_origin_or_finalizer = cr_origin; + if (!cr_origin) { + Py_DECREF(coro); +@@ -1724,11 +1749,14 @@ async_gen_unwrap_value(PyAsyncGenObject *gen, PyObject *result) + static void + async_gen_asend_dealloc(PyAsyncGenASend *o) + { ++#if _PyAsyncGen_MAXFREELIST > 0 ++ struct _Py_async_gen_state *state; ++#endif + _PyObject_GC_UNTRACK((PyObject *)o); + Py_CLEAR(o->ags_gen); + Py_CLEAR(o->ags_sendval); + #if _PyAsyncGen_MAXFREELIST > 0 +- struct _Py_async_gen_state *state = get_async_gen_state(); ++ state = get_async_gen_state(); + #ifdef Py_DEBUG + // async_gen_asend_dealloc() must not be called after _PyAsyncGen_Fini() + assert(state->asend_numfree != -1); +@@ -1931,10 +1959,13 @@ async_gen_asend_new(PyAsyncGenObject *gen, PyObject *sendval) + static void + async_gen_wrapped_val_dealloc(_PyAsyncGenWrappedValue *o) + { ++#if _PyAsyncGen_MAXFREELIST > 0 ++ struct _Py_async_gen_state *state; ++#endif + _PyObject_GC_UNTRACK((PyObject *)o); + Py_CLEAR(o->agw_val); + #if _PyAsyncGen_MAXFREELIST > 0 +- struct _Py_async_gen_state *state = get_async_gen_state(); ++ state = get_async_gen_state(); + #ifdef Py_DEBUG + // async_gen_wrapped_val_dealloc() must not be called after _PyAsyncGen_Fini() + assert(state->value_numfree != -1); +@@ -2008,10 +2039,13 @@ PyObject * + _PyAsyncGenValueWrapperNew(PyObject *val) + { + _PyAsyncGenWrappedValue *o; ++#if _PyAsyncGen_MAXFREELIST > 0 ++ struct _Py_async_gen_state *state; ++#endif + assert(val); + + #if _PyAsyncGen_MAXFREELIST > 0 +- struct _Py_async_gen_state *state = get_async_gen_state(); ++ state = get_async_gen_state(); + #ifdef Py_DEBUG + // _PyAsyncGenValueWrapperNew() must not be called after _PyAsyncGen_Fini() + assert(state->value_numfree != -1); +diff --git a/Objects/interpreteridobject.c b/Objects/interpreteridobject.c +index 7b3e31b..810bf2c 100644 +--- a/Objects/interpreteridobject.c ++++ b/Objects/interpreteridobject.c +@@ -14,7 +14,10 @@ typedef struct interpid { + static interpid * + newinterpid(PyTypeObject *cls, int64_t id, int force) + { +- PyInterpreterState *interp = _PyInterpreterState_LookUpID(id); ++ PyInterpreterState *interp; ++ interpid *self; ++ ++ interp = _PyInterpreterState_LookUpID(id); + if (interp == NULL) { + if (force) { + PyErr_Clear(); +@@ -30,7 +33,7 @@ newinterpid(PyTypeObject *cls, int64_t id, int force) + } + } + +- interpid *self = PyObject_New(interpid, cls); ++ self = PyObject_New(interpid, cls); + if (self == NULL) { + if (interp != NULL) { + _PyInterpreterState_IDDecref(interp); +@@ -88,8 +91,11 @@ interpid_new(PyTypeObject *cls, PyObject *args, PyObject *kwds) + static void + interpid_dealloc(PyObject *v) + { +- int64_t id = ((interpid *)v)->id; +- PyInterpreterState *interp = _PyInterpreterState_LookUpID(id); ++ int64_t id; ++ PyInterpreterState *interp; ++ ++ id = ((interpid *)v)->id; ++ interp = _PyInterpreterState_LookUpID(id); + if (interp != NULL) { + _PyInterpreterState_IDDecref(interp); + } +@@ -168,10 +174,12 @@ interpid_hash(PyObject *self) + { + interpid *id = (interpid *)self; + PyObject *obj = PyLong_FromLongLong(id->id); ++ Py_hash_t hash; ++ + if (obj == NULL) { + return -1; + } +- Py_hash_t hash = PyObject_Hash(obj); ++ hash = PyObject_Hash(obj); + Py_DECREF(obj); + return hash; + } +@@ -179,6 +187,9 @@ interpid_hash(PyObject *self) + static PyObject * + interpid_richcompare(PyObject *self, PyObject *other, int op) + { ++ interpid *id; ++ int equal; ++ + if (op != Py_EQ && op != Py_NE) { + Py_RETURN_NOTIMPLEMENTED; + } +@@ -187,8 +198,7 @@ interpid_richcompare(PyObject *self, PyObject *other, int op) + Py_RETURN_NOTIMPLEMENTED; + } + +- interpid *id = (interpid *)self; +- int equal; ++ id = (interpid *)self; + if (PyObject_TypeCheck(other, &_PyInterpreterID_Type)) { + interpid *otherid = (interpid *)other; + equal = (id->id == otherid->id); +@@ -196,18 +206,23 @@ interpid_richcompare(PyObject *self, PyObject *other, int op) + else if (PyLong_CheckExact(other)) { + /* Fast path */ + int overflow; +- long long otherid = PyLong_AsLongLongAndOverflow(other, &overflow); ++ PY_LONG_LONG otherid; ++ ++ otherid = PyLong_AsLongLongAndOverflow(other, &overflow); + if (otherid == -1 && PyErr_Occurred()) { + return NULL; + } + equal = !overflow && (otherid >= 0) && (id->id == otherid); + } + else if (PyNumber_Check(other)) { +- PyObject *pyid = PyLong_FromLongLong(id->id); ++ PyObject *pyid; ++ PyObject *res; ++ ++ pyid = PyLong_FromLongLong(id->id); + if (pyid == NULL) { + return NULL; + } +- PyObject *res = PyObject_RichCompare(pyid, other, op); ++ res = PyObject_RichCompare(pyid, other, op); + Py_DECREF(pyid); + return res; + } +@@ -273,10 +288,12 @@ PyObject *_PyInterpreterID_New(int64_t id) + PyObject * + _PyInterpreterState_GetIDObject(PyInterpreterState *interp) + { ++ int64_t id; ++ + if (_PyInterpreterState_IDInitref(interp) != 0) { + return NULL; + }; +- int64_t id = PyInterpreterState_GetID(interp); ++ id = PyInterpreterState_GetID(interp); + if (id < 0) { + return NULL; + } +diff --git a/Objects/iterobject.c b/Objects/iterobject.c +index 822f9e2..b783152 100644 +--- a/Objects/iterobject.c ++++ b/Objects/iterobject.c +@@ -325,18 +325,23 @@ anextawaitable_traverse(anextawaitableobject *obj, visitproc visit, void *arg) + static PyObject * + anextawaitable_getiter(anextawaitableobject *obj) + { ++ PyObject *awaitable; ++ + assert(obj->wrapped != NULL); +- PyObject *awaitable = _PyCoro_GetAwaitableIter(obj->wrapped); ++ awaitable = _PyCoro_GetAwaitableIter(obj->wrapped); + if (awaitable == NULL) { + return NULL; + } + if (Py_TYPE(awaitable)->tp_iternext == NULL) { ++ unaryfunc getter; ++ PyObject *new_awaitable; ++ + /* _PyCoro_GetAwaitableIter returns a Coroutine, a Generator, + * or an iterator. Of these, only coroutines lack tp_iternext. + */ + assert(PyCoro_CheckExact(awaitable)); +- unaryfunc getter = Py_TYPE(awaitable)->tp_as_async->am_await; +- PyObject *new_awaitable = getter(awaitable); ++ getter = Py_TYPE(awaitable)->tp_as_async->am_await; ++ new_awaitable = getter(awaitable); + if (new_awaitable == NULL) { + Py_DECREF(awaitable); + return NULL; +@@ -355,6 +360,9 @@ anextawaitable_getiter(anextawaitableobject *obj) + static PyObject * + anextawaitable_iternext(anextawaitableobject *obj) + { ++ PyObject *awaitable; ++ PyObject *result; ++ + /* Consider the following class: + * + * class A: +@@ -375,11 +383,11 @@ anextawaitable_iternext(anextawaitableobject *obj) + * Then `await anext(gen)` can just call + * gen.__anext__().__next__() + */ +- PyObject *awaitable = anextawaitable_getiter(obj); ++ awaitable = anextawaitable_getiter(obj); + if (awaitable == NULL) { + return NULL; + } +- PyObject *result = (*Py_TYPE(awaitable)->tp_iternext)(awaitable); ++ result = (*Py_TYPE(awaitable)->tp_iternext)(awaitable); + Py_DECREF(awaitable); + if (result != NULL) { + return result; +@@ -393,11 +401,14 @@ anextawaitable_iternext(anextawaitableobject *obj) + + static PyObject * + anextawaitable_proxy(anextawaitableobject *obj, char *meth, PyObject *arg) { +- PyObject *awaitable = anextawaitable_getiter(obj); ++ PyObject *awaitable; ++ PyObject *ret; ++ ++ awaitable = anextawaitable_getiter(obj); + if (awaitable == NULL) { + return NULL; + } +- PyObject *ret = PyObject_CallMethod(awaitable, meth, "O", arg); ++ ret = PyObject_CallMethod(awaitable, meth, "O", arg); + Py_DECREF(awaitable); + if (ret != NULL) { + return ret; +diff --git a/Objects/listobject.c b/Objects/listobject.c +index fa1f6a3..85f5d55 100644 +--- a/Objects/listobject.c ++++ b/Objects/listobject.c +@@ -97,6 +97,8 @@ list_resize(PyListObject *self, Py_ssize_t newsize) + static int + list_preallocate_exact(PyListObject *self, Py_ssize_t size) + { ++ PyObject **items; ++ + assert(self->ob_item == NULL); + assert(size > 0); + +@@ -106,7 +108,7 @@ list_preallocate_exact(PyListObject *self, Py_ssize_t size) + * allocated size up to the nearest even number. + */ + size = (size + 1) & ~(size_t)1; +- PyObject **items = PyMem_New(PyObject*, size); ++ items = PyMem_New(PyObject*, size); + if (items == NULL) { + PyErr_NoMemory(); + return -1; +@@ -155,6 +157,9 @@ PyObject * + PyList_New(Py_ssize_t size) + { + PyListObject *op; ++#if PyList_MAXFREELIST > 0 ++ struct _Py_list_state *state; ++#endif + + if (size < 0) { + PyErr_BadInternalCall(); +@@ -162,7 +167,7 @@ PyList_New(Py_ssize_t size) + } + + #if PyList_MAXFREELIST > 0 +- struct _Py_list_state *state = get_list_state(); ++ state = get_list_state(); + #ifdef Py_DEBUG + // PyList_New() must not be called after _PyList_Fini() + assert(state->numfree != -1); +@@ -200,8 +205,10 @@ PyList_New(Py_ssize_t size) + static PyObject * + list_new_prealloc(Py_ssize_t size) + { ++ PyListObject *op; ++ + assert(size > 0); +- PyListObject *op = (PyListObject *) PyList_New(0); ++ op = (PyListObject *) PyList_New(0); + if (op == NULL) { + return NULL; + } +@@ -345,6 +352,9 @@ static void + list_dealloc(PyListObject *op) + { + Py_ssize_t i; ++#if PyList_MAXFREELIST > 0 ++ struct _Py_list_state *state; ++#endif + PyObject_GC_UnTrack(op); + Py_TRASHCAN_BEGIN(op, list_dealloc) + if (op->ob_item != NULL) { +@@ -359,7 +369,7 @@ list_dealloc(PyListObject *op) + PyMem_Free(op->ob_item); + } + #if PyList_MAXFREELIST > 0 +- struct _Py_list_state *state = get_list_state(); ++ state = get_list_state(); + #ifdef Py_DEBUG + // list_dealloc() must not be called after _PyList_Fini() + assert(state->numfree != -1); +@@ -559,6 +569,12 @@ list_repeat(PyListObject *a, Py_ssize_t n) + { + Py_ssize_t size; + PyListObject *np; ++ PyObject **dest; ++ PyObject **dest_end; ++ PyObject *elem; ++ PyObject **src; ++ PyObject **src_end; ++ + if (n < 0) + n = 0; + if (n > 0 && Py_SIZE(a) > PY_SSIZE_T_MAX / n) +@@ -569,10 +585,10 @@ list_repeat(PyListObject *a, Py_ssize_t n) + np = (PyListObject *) list_new_prealloc(size); + if (np == NULL) + return NULL; +- PyObject **dest = np->ob_item; +- PyObject **dest_end = dest + size; ++ dest = np->ob_item; ++ dest_end = dest + size; + if (Py_SIZE(a) == 1) { +- PyObject *elem = a->ob_item[0]; ++ elem = a->ob_item[0]; + Py_SET_REFCNT(elem, Py_REFCNT(elem) + n); + #ifdef Py_REF_DEBUG + _Py_RefTotal += n; +@@ -582,8 +598,8 @@ list_repeat(PyListObject *a, Py_ssize_t n) + } + } + else { +- PyObject **src = a->ob_item; +- PyObject **src_end = src + Py_SIZE(a); ++ src = a->ob_item; ++ src_end = src + Py_SIZE(a); + while (src < src_end) { + Py_SET_REFCNT(*src, Py_REFCNT(*src) + n); + #ifdef Py_REF_DEBUG +@@ -1961,6 +1977,9 @@ static int + powerloop(Py_ssize_t s1, Py_ssize_t n1, Py_ssize_t n2, Py_ssize_t n) + { + int result = 0; ++ Py_ssize_t a; ++ Py_ssize_t b; ++ + assert(s1 >= 0); + assert(n1 > 0 && n2 > 0); + assert(s1 + n1 + n2 <= n); +@@ -1973,8 +1992,8 @@ powerloop(Py_ssize_t s1, Py_ssize_t n1, Py_ssize_t n2, Py_ssize_t n) + * difference to the outcome, since the bits in the expansion of (2*i)/n + * are merely shifted one position from those of i/n. + */ +- Py_ssize_t a = 2 * s1 + n1; /* 2*a */ +- Py_ssize_t b = a + n1 + n2; /* 2*b */ ++ a = 2 * s1 + n1; /* 2*a */ ++ b = a + n1 + n2; /* 2*b */ + /* Emulate a/n and b/n one bit a time, until bits differ. */ + for (;;) { + ++result; +@@ -2009,11 +2028,16 @@ found_new_run(MergeState *ms, Py_ssize_t n2) + { + assert(ms); + if (ms->n) { ++ struct s_slice *p; ++ Py_ssize_t s1; ++ Py_ssize_t n1; ++ int power; ++ + assert(ms->n > 0); +- struct s_slice *p = ms->pending; +- Py_ssize_t s1 = p[ms->n - 1].base.keys - ms->basekeys; /* start index */ +- Py_ssize_t n1 = p[ms->n - 1].len; +- int power = powerloop(s1, n1, n2, ms->listlen); ++ p = ms->pending; ++ s1 = p[ms->n - 1].base.keys - ms->basekeys; /* start index */ ++ n1 = p[ms->n - 1].len; ++ power = powerloop(s1, n1, n2, ms->listlen); + while (ms->n > 1 && p[ms->n - 2].power > power) { + if (merge_at(ms, ms->n - 2) < 0) + return -1; +@@ -2278,6 +2302,7 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, int reverse) + PyObject *result = NULL; /* guilty until proved innocent */ + Py_ssize_t i; + PyObject **keys; ++ PyObject *key; + + assert(self != NULL); + assert(PyList_Check(self)); +@@ -2359,7 +2384,7 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, int reverse) + /* Note: for lists of tuples, key is the first element of the tuple + * lo.keys[i], not lo.keys[i] itself! We verify type-homogeneity + * for lists of tuples in the if-statement directly above. */ +- PyObject *key = (keys_are_in_tuples ? ++ key = (keys_are_in_tuples ? + PyTuple_GET_ITEM(lo.keys[i], 0) : + lo.keys[i]); + +@@ -2614,8 +2639,10 @@ list_index_impl(PyListObject *self, PyObject *value, Py_ssize_t start, + } + for (i = start; i < stop && i < Py_SIZE(self); i++) { + PyObject *obj = self->ob_item[i]; ++ int cmp; ++ + Py_INCREF(obj); +- int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); ++ cmp = PyObject_RichCompareBool(obj, value, Py_EQ); + Py_DECREF(obj); + if (cmp > 0) + return PyLong_FromSsize_t(i); +@@ -2644,12 +2671,14 @@ list_count(PyListObject *self, PyObject *value) + + for (i = 0; i < Py_SIZE(self); i++) { + PyObject *obj = self->ob_item[i]; ++ int cmp; ++ + if (obj == value) { + count++; + continue; + } + Py_INCREF(obj); +- int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); ++ cmp = PyObject_RichCompareBool(obj, value, Py_EQ); + Py_DECREF(obj); + if (cmp > 0) + count++; +@@ -2678,8 +2707,10 @@ list_remove(PyListObject *self, PyObject *value) + + for (i = 0; i < Py_SIZE(self); i++) { + PyObject *obj = self->ob_item[i]; ++ int cmp; ++ + Py_INCREF(obj); +- int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); ++ cmp = PyObject_RichCompareBool(obj, value, Py_EQ); + Py_DECREF(obj); + if (cmp > 0) { + if (list_ass_slice(self, i, i+1, +@@ -2726,15 +2757,19 @@ list_richcompare(PyObject *v, PyObject *w, int op) + + /* Search for the first index where items are different */ + for (i = 0; i < Py_SIZE(vl) && i < Py_SIZE(wl); i++) { +- PyObject *vitem = vl->ob_item[i]; +- PyObject *witem = wl->ob_item[i]; ++ PyObject *vitem; ++ PyObject *witem; ++ int k; ++ ++ vitem = vl->ob_item[i]; ++ witem = wl->ob_item[i]; + if (vitem == witem) { + continue; + } + + Py_INCREF(vitem); + Py_INCREF(witem); +- int k = PyObject_RichCompareBool(vitem, witem, Py_EQ); ++ k = PyObject_RichCompareBool(vitem, witem, Py_EQ); + Py_DECREF(vitem); + Py_DECREF(witem); + if (k < 0) +@@ -2757,14 +2792,20 @@ list_richcompare(PyObject *v, PyObject *w, int op) + } + + /* Compare the final item again using the proper operator */ +- PyObject *vitem = vl->ob_item[i]; +- PyObject *witem = wl->ob_item[i]; +- Py_INCREF(vitem); +- Py_INCREF(witem); +- PyObject *result = PyObject_RichCompare(vl->ob_item[i], wl->ob_item[i], op); +- Py_DECREF(vitem); +- Py_DECREF(witem); +- return result; ++ { ++ PyObject *vitem; ++ PyObject *witem; ++ PyObject *result; ++ ++ vitem = vl->ob_item[i]; ++ witem = wl->ob_item[i]; ++ Py_INCREF(vitem); ++ Py_INCREF(witem); ++ result = PyObject_RichCompare(vl->ob_item[i], wl->ob_item[i], op); ++ Py_DECREF(vitem); ++ Py_DECREF(witem); ++ return result; ++ } + } + + /*[clinic input] +@@ -2806,15 +2847,18 @@ static PyObject * + list_vectorcall(PyObject *type, PyObject * const*args, + size_t nargsf, PyObject *kwnames) + { ++ Py_ssize_t nargs; ++ PyObject *list; ++ + if (!_PyArg_NoKwnames("list", kwnames)) { + return NULL; + } +- Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); ++ nargs = PyVectorcall_NARGS(nargsf); + if (!_PyArg_CheckPositional("list", nargs, 0, 1)) { + return NULL; + } + +- PyObject *list = PyType_GenericAlloc(_PyType_CAST(type), 0); ++ list = PyType_GenericAlloc(_PyType_CAST(type), 0); + if (list == NULL) { + return NULL; + } +@@ -3042,6 +3086,7 @@ list_ass_subscript(PyListObject* self, PyObject* item, PyObject* value) + PyObject *ins, *seq; + PyObject **garbage, **seqitems, **selfitems; + Py_ssize_t i; ++ Py_ssize_t slicelength; + size_t cur; + + /* protect against a[::-1] = a */ +@@ -3057,8 +3102,8 @@ list_ass_subscript(PyListObject* self, PyObject* item, PyObject* value) + if (!seq) + return -1; + +- Py_ssize_t slicelength = adjust_slice_indexes(self, &start, &stop, +- step); ++ slicelength = adjust_slice_indexes(self, &start, &stop, ++ step); + + if (step == 1) { + int res = list_ass_slice(self, start, stop, seq); +@@ -3166,7 +3211,16 @@ PyTypeObject PyList_Type = { + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +- .tp_vectorcall = list_vectorcall, ++ 0, /* tp_is_gc */ ++ 0, /* tp_bases */ ++ 0, /* tp_mro */ ++ 0, /* tp_cache */ ++ 0, /* tp_subclasses */ ++ 0, /* tp_weaklist */ ++ 0, /* tp_del */ ++ 0, /* tp_version_tag */ ++ 0, /* tp_finalize */ ++ list_vectorcall, /* tp_vectorcall */ + }; + + /*********************** List Iterator **************************/ +@@ -3480,6 +3534,8 @@ listiter_reduce_general(void *_it, int forward) + { + PyObject *list; + PyObject *iter; ++ listiterobject *it; ++ listreviterobject *rev_it; + + /* _PyEval_GetBuiltin can invoke arbitrary code, + * call must be before access of iterator pointers. +@@ -3491,7 +3547,7 @@ listiter_reduce_general(void *_it, int forward) + if (!iter) { + return NULL; + } +- listiterobject *it = (listiterobject *)_it; ++ it = (listiterobject *)_it; + if (it->it_seq) { + return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index); + } +@@ -3500,9 +3556,9 @@ listiter_reduce_general(void *_it, int forward) + if (!iter) { + return NULL; + } +- listreviterobject *it = (listreviterobject *)_it; +- if (it->it_seq) { +- return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index); ++ rev_it = (listreviterobject *)_it; ++ if (rev_it->it_seq) { ++ return Py_BuildValue("N(O)n", iter, rev_it->it_seq, rev_it->it_index); + } + } + /* empty iterator, create an empty list */ +diff --git a/Objects/longobject.c b/Objects/longobject.c +index 487c062..2ff2af4 100644 +--- a/Objects/longobject.c ++++ b/Objects/longobject.c +@@ -57,8 +57,9 @@ is_medium_int(stwodigits x) + static PyObject * + get_small_int(sdigit ival) + { ++ PyObject *v; + assert(IS_SMALL_INT(ival)); +- PyObject *v = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + ival]; ++ v = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + ival]; + Py_INCREF(v); + return v; + } +@@ -145,6 +146,7 @@ PyLongObject * + _PyLong_New(Py_ssize_t size) + { + PyLongObject *result; ++ Py_ssize_t ndigits; + if (size > (Py_ssize_t)MAX_LONG_DIGITS) { + PyErr_SetString(PyExc_OverflowError, + "too many digits in integer"); +@@ -152,7 +154,7 @@ _PyLong_New(Py_ssize_t size) + } + /* Fast operations for single digit integers (including zero) + * assume that there is always at least one digit present. */ +- Py_ssize_t ndigits = size ? size : 1; ++ ndigits = size ? size : 1; + /* Number of bytes needed is: offsetof(PyLongObject, ob_digit) + + sizeof(digit)*size. Previous incarnations of this code used + sizeof(PyVarObject) instead of the offsetof, but this risks being +@@ -197,16 +199,19 @@ _PyLong_Copy(PyLongObject *src) + static PyObject * + _PyLong_FromMedium(sdigit x) + { ++ PyLongObject *v; ++ Py_ssize_t sign; ++ digit abs_x; + assert(!IS_SMALL_INT(x)); + assert(is_medium_int(x)); + /* We could use a freelist here */ +- PyLongObject *v = PyObject_Malloc(sizeof(PyLongObject)); ++ v = PyObject_Malloc(sizeof(PyLongObject)); + if (v == NULL) { + PyErr_NoMemory(); + return NULL; + } +- Py_ssize_t sign = x < 0 ? -1: 1; +- digit abs_x = x < 0 ? -x : x; ++ sign = x < 0 ? -1: 1; ++ abs_x = x < 0 ? -x : x; + _PyObject_InitVar((PyVarObject*)v, &PyLong_Type, sign); + v->ob_digit[0] = abs_x; + return (PyObject*)v; +@@ -216,6 +221,10 @@ static PyObject * + _PyLong_FromLarge(stwodigits ival) + { + twodigits abs_ival; ++ twodigits t; ++ Py_ssize_t ndigits; ++ PyLongObject *v; ++ digit *p; + int sign; + assert(!is_medium_int(ival)); + +@@ -231,15 +240,15 @@ _PyLong_FromLarge(stwodigits ival) + } + /* Must be at least two digits */ + assert(abs_ival >> PyLong_SHIFT != 0); +- twodigits t = abs_ival >> (PyLong_SHIFT * 2); +- Py_ssize_t ndigits = 2; ++ t = abs_ival >> (PyLong_SHIFT * 2); ++ ndigits = 2; + while (t) { + ++ndigits; + t >>= PyLong_SHIFT; + } +- PyLongObject *v = _PyLong_New(ndigits); ++ v = _PyLong_New(ndigits); + if (v != NULL) { +- digit *p = v->ob_digit; ++ p = v->ob_digit; + Py_SET_SIZE(v, ndigits * sign); + t = abs_ival; + while (t) { +@@ -325,21 +334,25 @@ PyLong_FromLong(long ival) + + #define PYLONG_FROM_UINT(INT_TYPE, ival) \ + do { \ ++ Py_ssize_t ndigits; \ ++ INT_TYPE t; \ ++ PyLongObject *v; \ ++ digit *p; \ + if (IS_SMALL_UINT(ival)) { \ + return get_small_int((sdigit)(ival)); \ + } \ + /* Count the number of Python digits. */ \ +- Py_ssize_t ndigits = 0; \ +- INT_TYPE t = (ival); \ ++ ndigits = 0; \ ++ t = (ival); \ + while (t) { \ + ++ndigits; \ + t >>= PyLong_SHIFT; \ + } \ +- PyLongObject *v = _PyLong_New(ndigits); \ ++ v = _PyLong_New(ndigits); \ + if (v == NULL) { \ + return NULL; \ + } \ +- digit *p = v->ob_digit; \ ++ p = v->ob_digit; \ + while ((ival)) { \ + *p++ = (digit)((ival) & PyLong_MASK); \ + (ival) >>= PyLong_SHIFT; \ +@@ -355,12 +368,12 @@ PyLong_FromUnsignedLong(unsigned long ival) + PYLONG_FROM_UINT(unsigned long, ival); + } + +-/* Create a new int object from a C unsigned long long int. */ ++/* Create a new int object from a C unsigned PY_LONG_LONG int. */ + + PyObject * +-PyLong_FromUnsignedLongLong(unsigned long long ival) ++PyLong_FromUnsignedLongLong(unsigned PY_LONG_LONG ival) + { +- PYLONG_FROM_UINT(unsigned long long, ival); ++ PYLONG_FROM_UINT(unsigned PY_LONG_LONG, ival); + } + + /* Create a new int object from a C size_t. */ +@@ -386,14 +399,14 @@ PyLong_FromDouble(double dval) + * as double exactly (assuming FLT_RADIX is 2 or 16), so for simplicity + * check against [-(LONG_MAX + 1), LONG_MAX + 1). + */ ++ PyLongObject *v; ++ double frac; ++ int i, ndig, expo, neg; + const double int_max = (unsigned long)LONG_MAX + 1; + if (-int_max < dval && dval < int_max) { + return PyLong_FromLong((long)dval); + } + +- PyLongObject *v; +- double frac; +- int i, ndig, expo, neg; + neg = 0; + if (Py_IS_INFINITY(dval)) { + PyErr_SetString(PyExc_OverflowError, +@@ -1065,9 +1078,9 @@ PyLong_FromVoidPtr(void *p) + #else + + #if SIZEOF_LONG_LONG < SIZEOF_VOID_P +-# error "PyLong_FromVoidPtr: sizeof(long long) < sizeof(void*)" ++# error "PyLong_FromVoidPtr: sizeof(PY_LONG_LONG) < sizeof(void*)" + #endif +- return PyLong_FromUnsignedLongLong((unsigned long long)(uintptr_t)p); ++ return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)(uintptr_t)p); + #endif /* SIZEOF_VOID_P <= SIZEOF_LONG */ + + } +@@ -1087,9 +1100,9 @@ PyLong_AsVoidPtr(PyObject *vv) + #else + + #if SIZEOF_LONG_LONG < SIZEOF_VOID_P +-# error "PyLong_AsVoidPtr: sizeof(long long) < sizeof(void*)" ++# error "PyLong_AsVoidPtr: sizeof(PY_LONG_LONG) < sizeof(void*)" + #endif +- long long x; ++ PY_LONG_LONG x; + + if (PyLong_Check(vv) && _PyLong_Sign(vv) < 0) + x = PyLong_AsLongLong(vv); +@@ -1103,31 +1116,31 @@ PyLong_AsVoidPtr(PyObject *vv) + return (void *)x; + } + +-/* Initial long long support by Chris Herborth (chrish@qnx.com), later ++/* Initial PY_LONG_LONG support by Chris Herborth (chrish@qnx.com), later + * rewritten to use the newer PyLong_{As,From}ByteArray API. + */ + +-#define PY_ABS_LLONG_MIN (0-(unsigned long long)LLONG_MIN) ++#define PY_ABS_LLONG_MIN (0-(unsigned PY_LONG_LONG)PY_LLONG_MIN) + +-/* Create a new int object from a C long long int. */ ++/* Create a new int object from a C PY_LONG_LONG int. */ + + PyObject * +-PyLong_FromLongLong(long long ival) ++PyLong_FromLongLong(PY_LONG_LONG ival) + { + PyLongObject *v; +- unsigned long long abs_ival, t; ++ unsigned PY_LONG_LONG abs_ival, t; + int ndigits; + + /* Handle small and medium cases. */ + if (IS_SMALL_INT(ival)) { + return get_small_int((sdigit)ival); + } +- if (-(long long)PyLong_MASK <= ival && ival <= (long long)PyLong_MASK) { ++ if (-(PY_LONG_LONG)PyLong_MASK <= ival && ival <= (PY_LONG_LONG)PyLong_MASK) { + return _PyLong_FromMedium((sdigit)ival); + } + + /* Count digits (at least two - smaller cases were handled above). */ +- abs_ival = ival < 0 ? 0U-(unsigned long long)ival : (unsigned long long)ival; ++ abs_ival = ival < 0 ? 0U-(unsigned PY_LONG_LONG)ival : (unsigned PY_LONG_LONG)ival; + /* Do shift in two steps to avoid possible undefined behavior. */ + t = abs_ival >> PyLong_SHIFT >> PyLong_SHIFT; + ndigits = 2; +@@ -1193,14 +1206,14 @@ PyLong_FromSsize_t(Py_ssize_t ival) + return (PyObject *)v; + } + +-/* Get a C long long int from an int object or any object that has an ++/* Get a C PY_LONG_LONG int from an int object or any object that has an + __index__ method. Return -1 and set an error if overflow occurs. */ + +-long long ++PY_LONG_LONG + PyLong_AsLongLong(PyObject *vv) + { + PyLongObject *v; +- long long bytes; ++ PY_LONG_LONG bytes; + int res; + int do_decref = 0; /* if PyNumber_Index was called */ + +@@ -1238,30 +1251,30 @@ PyLong_AsLongLong(PyObject *vv) + Py_DECREF(v); + } + +- /* Plan 9 can't handle long long in ? : expressions */ ++ /* Plan 9 can't handle PY_LONG_LONG in ? : expressions */ + if (res < 0) +- return (long long)-1; ++ return (PY_LONG_LONG)-1; + else + return bytes; + } + +-/* Get a C unsigned long long int from an int object. ++/* Get a C unsigned PY_LONG_LONG int from an int object. + Return -1 and set an error if overflow occurs. */ + +-unsigned long long ++unsigned PY_LONG_LONG + PyLong_AsUnsignedLongLong(PyObject *vv) + { + PyLongObject *v; +- unsigned long long bytes; ++ unsigned PY_LONG_LONG bytes; + int res; + + if (vv == NULL) { + PyErr_BadInternalCall(); +- return (unsigned long long)-1; ++ return (unsigned PY_LONG_LONG)-1; + } + if (!PyLong_Check(vv)) { + PyErr_SetString(PyExc_TypeError, "an integer is required"); +- return (unsigned long long)-1; ++ return (unsigned PY_LONG_LONG)-1; + } + + v = (PyLongObject*)vv; +@@ -1273,9 +1286,9 @@ PyLong_AsUnsignedLongLong(PyObject *vv) + res = _PyLong_AsByteArray((PyLongObject *)vv, (unsigned char *)&bytes, + SIZEOF_LONG_LONG, PY_LITTLE_ENDIAN, 0); + +- /* Plan 9 can't handle long long in ? : expressions */ ++ /* Plan 9 can't handle PY_LONG_LONG in ? : expressions */ + if (res < 0) +- return (unsigned long long)res; ++ return (unsigned PY_LONG_LONG)res; + else + return bytes; + } +@@ -1283,17 +1296,17 @@ PyLong_AsUnsignedLongLong(PyObject *vv) + /* Get a C unsigned long int from an int object, ignoring the high bits. + Returns -1 and sets an error condition if an error occurs. */ + +-static unsigned long long ++static unsigned PY_LONG_LONG + _PyLong_AsUnsignedLongLongMask(PyObject *vv) + { + PyLongObject *v; +- unsigned long long x; ++ unsigned PY_LONG_LONG x; + Py_ssize_t i; + int sign; + + if (vv == NULL || !PyLong_Check(vv)) { + PyErr_BadInternalCall(); +- return (unsigned long long) -1; ++ return (unsigned PY_LONG_LONG) -1; + } + v = (PyLongObject *)vv; + switch(Py_SIZE(v)) { +@@ -1313,15 +1326,15 @@ _PyLong_AsUnsignedLongLongMask(PyObject *vv) + return x * sign; + } + +-unsigned long long ++unsigned PY_LONG_LONG + PyLong_AsUnsignedLongLongMask(PyObject *op) + { + PyLongObject *lo; +- unsigned long long val; ++ unsigned PY_LONG_LONG val; + + if (op == NULL) { + PyErr_BadInternalCall(); +- return (unsigned long long)-1; ++ return (unsigned PY_LONG_LONG)-1; + } + + if (PyLong_Check(op)) { +@@ -1330,14 +1343,14 @@ PyLong_AsUnsignedLongLongMask(PyObject *op) + + lo = (PyLongObject *)_PyNumber_Index(op); + if (lo == NULL) +- return (unsigned long long)-1; ++ return (unsigned PY_LONG_LONG)-1; + + val = _PyLong_AsUnsignedLongLongMask((PyObject *)lo); + Py_DECREF(lo); + return val; + } + +-/* Get a C long long int from an int object or any object that has an ++/* Get a C PY_LONG_LONG int from an int object or any object that has an + __index__ method. + + On overflow, return -1 and set *overflow to 1 or -1 depending on the sign of +@@ -1347,13 +1360,13 @@ PyLong_AsUnsignedLongLongMask(PyObject *op) + In this case *overflow will be 0. + */ + +-long long ++PY_LONG_LONG + PyLong_AsLongLongAndOverflow(PyObject *vv, int *overflow) + { + /* This version by Tim Peters */ + PyLongObject *v; +- unsigned long long x, prev; +- long long res; ++ unsigned PY_LONG_LONG x, prev; ++ PY_LONG_LONG res; + Py_ssize_t i; + int sign; + int do_decref = 0; /* if PyNumber_Index was called */ +@@ -1405,11 +1418,11 @@ PyLong_AsLongLongAndOverflow(PyObject *vv, int *overflow) + /* Haven't lost any bits, but casting to long requires extra + * care (see comment above). + */ +- if (x <= (unsigned long long)LLONG_MAX) { +- res = (long long)x * sign; ++ if (x <= (unsigned PY_LONG_LONG)PY_LLONG_MAX) { ++ res = (PY_LONG_LONG)x * sign; + } + else if (sign < 0 && x == PY_ABS_LLONG_MIN) { +- res = LLONG_MIN; ++ res = PY_LLONG_MIN; + } + else { + *overflow = sign; +@@ -1487,17 +1500,17 @@ _PyLong_UnsignedLong_Converter(PyObject *obj, void *ptr) + int + _PyLong_UnsignedLongLong_Converter(PyObject *obj, void *ptr) + { +- unsigned long long uval; ++ unsigned PY_LONG_LONG uval; + + if (PyLong_Check(obj) && _PyLong_Sign(obj) < 0) { + PyErr_SetString(PyExc_ValueError, "value must be positive"); + return 0; + } + uval = PyLong_AsUnsignedLongLong(obj); +- if (uval == (unsigned long long)-1 && PyErr_Occurred()) ++ if (uval == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) + return 0; + +- *(unsigned long long *)ptr = uval; ++ *(unsigned PY_LONG_LONG *)ptr = uval; + return 1; + } + +@@ -1643,8 +1656,9 @@ inplace_divrem1(digit *pout, digit *pin, Py_ssize_t size, digit n) + assert(n > 0 && n <= PyLong_MASK); + while (--size >= 0) { + twodigits dividend; +- dividend = ((twodigits)remainder << PyLong_SHIFT) | pin[size]; + digit quotient; ++ ++ dividend = ((twodigits)remainder << PyLong_SHIFT) | pin[size]; + quotient = (digit)(dividend / n); + remainder = dividend % n; + pout[size] = quotient; +@@ -1893,8 +1907,8 @@ long_to_decimal_string_internal(PyObject *aa, + WRITE_UNICODE_DIGITS(Py_UCS2); + } + else { +- assert (kind == PyUnicode_4BYTE_KIND); + Py_UCS4 *p; ++ assert (kind == PyUnicode_4BYTE_KIND); + WRITE_UNICODE_DIGITS(Py_UCS4); + } + } +@@ -2073,8 +2087,8 @@ long_format_binary(PyObject *aa, int base, int alternate, + WRITE_UNICODE_DIGITS(Py_UCS2); + } + else { +- assert (kind == PyUnicode_4BYTE_KIND); + Py_UCS4 *p; ++ assert (kind == PyUnicode_4BYTE_KIND); + WRITE_UNICODE_DIGITS(Py_UCS4); + } + } +@@ -2281,6 +2295,7 @@ PyLong_FromString(const char *str, char **pend, int base) + PyLongObject *z = NULL; + PyObject *strobj; + Py_ssize_t slen; ++ double fsize_z; + + if ((base != 0 && base < 2) || base > 36) { + PyErr_SetString(PyExc_ValueError, +@@ -2502,7 +2517,7 @@ digit beyond the first. + * need to initialize z->ob_digit -- no slot is read up before + * being stored into. + */ +- double fsize_z = (double)digits * log_base_BASE[base] + 1.0; ++ fsize_z = (double)digits * log_base_BASE[base] + 1.0; + if (fsize_z > (double)MAX_LONG_DIGITS) { + /* The same exception as in _PyLong_New(). */ + PyErr_SetString(PyExc_OverflowError, +@@ -2717,12 +2732,13 @@ long_divrem(PyLongObject *a, PyLongObject *b, + if (size_a < size_b || + (size_a == size_b && + a->ob_digit[size_a-1] < b->ob_digit[size_b-1])) { ++ PyObject *zero; + /* |a| < |b|. */ + *prem = (PyLongObject *)long_long((PyObject *)a); + if (*prem == NULL) { + return -1; + } +- PyObject *zero = _PyLong_GetZero(); ++ zero = _PyLong_GetZero(); + Py_INCREF(zero); + *pdiv = (PyLongObject*)zero; + return 0; +@@ -3286,11 +3302,12 @@ x_sub(PyLongObject *a, PyLongObject *b) + PyObject * + _PyLong_Add(PyLongObject *a, PyLongObject *b) + { ++ PyLongObject *z; ++ + if (IS_MEDIUM_VALUE(a) && IS_MEDIUM_VALUE(b)) { + return _PyLong_FromSTwoDigits(medium_value(a) + medium_value(b)); + } + +- PyLongObject *z; + if (Py_SIZE(a) < 0) { + if (Py_SIZE(b) < 0) { + z = x_add(a, b); +@@ -4380,6 +4397,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x) + + PyLongObject *z = NULL; /* accumulated result */ + Py_ssize_t i, j; /* counters */ ++ digit bi, bit; + PyLongObject *temp = NULL; + PyLongObject *a2 = NULL; /* may temporarily hold a**2 % c */ + +@@ -4526,8 +4544,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x) + } while(0) + + i = Py_SIZE(b); +- digit bi = i ? b->ob_digit[i-1] : 0; +- digit bit; ++ bi = i ? b->ob_digit[i-1] : 0; + if (i <= 1 && bi <= 3) { + /* aim for minimal overhead */ + if (bi >= 2) { +@@ -4579,6 +4596,8 @@ long_pow(PyObject *v, PyObject *w, PyObject *x) + } + } + else { ++ int pending; ++ int blen; + /* Left-to-right k-ary sliding window exponentiation + * (Handbook of Applied Cryptography (HAC) Algorithm 14.85) + */ +@@ -4598,7 +4617,8 @@ long_pow(PyObject *v, PyObject *w, PyObject *x) + * into `pending`, starting with the next 1 bit. The current bit + * length of `pending` is `blen`. + */ +- int pending = 0, blen = 0; ++ pending = 0; ++ blen = 0; + #define ABSORB_PENDING do { \ + int ntz = 0; /* number of trailing zeroes in `pending` */ \ + assert(pending && blen); \ +@@ -4709,9 +4729,12 @@ long_bool(PyLongObject *v) + static int + divmod_shift(PyObject *shiftby, Py_ssize_t *wordshift, digit *remshift) + { ++ Py_ssize_t lshiftby; ++ PyLongObject *wordshift_obj; ++ + assert(PyLong_Check(shiftby)); + assert(Py_SIZE(shiftby) >= 0); +- Py_ssize_t lshiftby = PyLong_AsSsize_t((PyObject *)shiftby); ++ lshiftby = PyLong_AsSsize_t((PyObject *)shiftby); + if (lshiftby >= 0) { + *wordshift = lshiftby / PyLong_SHIFT; + *remshift = lshiftby % PyLong_SHIFT; +@@ -4721,7 +4744,7 @@ divmod_shift(PyObject *shiftby, Py_ssize_t *wordshift, digit *remshift) + be that PyLong_AsSsize_t raised an OverflowError. */ + assert(PyErr_ExceptionMatches(PyExc_OverflowError)); + PyErr_Clear(); +- PyLongObject *wordshift_obj = divrem1((PyLongObject *)shiftby, PyLong_SHIFT, remshift); ++ wordshift_obj = divrem1((PyLongObject *)shiftby, PyLong_SHIFT, remshift); + if (wordshift_obj == NULL) { + return -1; + } +@@ -4795,6 +4818,7 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) + + accum = a->ob_digit[wordshift]; + if (a_negative) { ++ digit sticky; + /* + For a positive integer a and nonnegative shift, we have: + +@@ -4808,18 +4832,26 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) + */ + Py_SET_SIZE(z, -newsize); + +- digit sticky = 0; +- for (Py_ssize_t j = 0; j < wordshift; j++) { +- sticky |= a->ob_digit[j]; ++ sticky = 0; ++ { ++ Py_ssize_t j; ++ for (j = 0; j < wordshift; j++) { ++ sticky |= a->ob_digit[j]; ++ } + } + accum += (PyLong_MASK >> hishift) + (digit)(sticky != 0); + } + + accum >>= remshift; +- for (Py_ssize_t i = 0, j = wordshift + 1; j < size_a; i++, j++) { +- accum += (twodigits)a->ob_digit[j] << hishift; +- z->ob_digit[i] = (digit)(accum & PyLong_MASK); +- accum >>= PyLong_SHIFT; ++ { ++ Py_ssize_t i, j; ++ i = 0; ++ j = wordshift + 1; ++ for (; j < size_a; i++, j++) { ++ accum += (twodigits)a->ob_digit[j] << hishift; ++ z->ob_digit[i] = (digit)(accum & PyLong_MASK); ++ accum >>= PyLong_SHIFT; ++ } + } + assert(accum <= PyLong_MASK); + z->ob_digit[newsize - 1] = (digit)accum; +@@ -5084,9 +5116,12 @@ long_bitwise(PyLongObject *a, + static PyObject * + long_and(PyObject *a, PyObject *b) + { ++ PyLongObject *x; ++ PyLongObject *y; ++ + CHECK_BINOP(a, b); +- PyLongObject *x = (PyLongObject*)a; +- PyLongObject *y = (PyLongObject*)b; ++ x = (PyLongObject*)a; ++ y = (PyLongObject*)b; + if (IS_MEDIUM_VALUE(x) && IS_MEDIUM_VALUE(y)) { + return _PyLong_FromSTwoDigits(medium_value(x) & medium_value(y)); + } +@@ -5096,9 +5131,12 @@ long_and(PyObject *a, PyObject *b) + static PyObject * + long_xor(PyObject *a, PyObject *b) + { ++ PyLongObject *x; ++ PyLongObject *y; ++ + CHECK_BINOP(a, b); +- PyLongObject *x = (PyLongObject*)a; +- PyLongObject *y = (PyLongObject*)b; ++ x = (PyLongObject*)a; ++ y = (PyLongObject*)b; + if (IS_MEDIUM_VALUE(x) && IS_MEDIUM_VALUE(y)) { + return _PyLong_FromSTwoDigits(medium_value(x) ^ medium_value(y)); + } +@@ -5108,9 +5146,12 @@ long_xor(PyObject *a, PyObject *b) + static PyObject * + long_or(PyObject *a, PyObject *b) + { ++ PyLongObject *x; ++ PyLongObject *y; ++ + CHECK_BINOP(a, b); +- PyLongObject *x = (PyLongObject*)a; +- PyLongObject *y = (PyLongObject*)b; ++ x = (PyLongObject*)a; ++ y = (PyLongObject*)b; + if (IS_MEDIUM_VALUE(x) && IS_MEDIUM_VALUE(y)) { + return _PyLong_FromSTwoDigits(medium_value(x) | medium_value(y)); + } +@@ -5303,7 +5344,7 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg) + /* a fits into a long, so b must too */ + x = PyLong_AsLong((PyObject *)a); + y = PyLong_AsLong((PyObject *)b); +-#elif LLONG_MAX >> PyLong_SHIFT >> PyLong_SHIFT ++#elif PY_LLONG_MAX >> PyLong_SHIFT >> PyLong_SHIFT + x = PyLong_AsLongLong((PyObject *)a); + y = PyLong_AsLongLong((PyObject *)b); + #else +@@ -5322,7 +5363,7 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg) + } + #if LONG_MAX >> PyLong_SHIFT >> PyLong_SHIFT + return PyLong_FromLong(x); +-#elif LLONG_MAX >> PyLong_SHIFT >> PyLong_SHIFT ++#elif PY_LLONG_MAX >> PyLong_SHIFT >> PyLong_SHIFT + return PyLong_FromLongLong(x); + #else + # error "_PyLong_GCD" +@@ -5500,6 +5541,7 @@ _PyLong_DivmodNear(PyObject *a, PyObject *b) + { + PyLongObject *quo = NULL, *rem = NULL; + PyObject *twice_rem, *result, *temp; ++ PyObject *one; + int quo_is_odd, quo_is_neg; + Py_ssize_t cmp; + +@@ -5532,7 +5574,7 @@ _PyLong_DivmodNear(PyObject *a, PyObject *b) + + /* compare twice the remainder with the divisor, to see + if we need to adjust the quotient and remainder */ +- PyObject *one = _PyLong_GetOne(); // borrowed reference ++ one = _PyLong_GetOne(); // borrowed reference + twice_rem = long_lshift((PyObject *)rem, one); + if (twice_rem == NULL) + goto error; +@@ -5770,33 +5812,40 @@ static PyObject * + int_bit_count_impl(PyObject *self) + /*[clinic end generated code: output=2e571970daf1e5c3 input=7e0adef8e8ccdf2e]*/ + { ++ PyLongObject *z; ++ Py_ssize_t ndigits, bit_count, ndigits_fast, i; ++ PyObject *result; ++ + assert(self != NULL); + assert(PyLong_Check(self)); + +- PyLongObject *z = (PyLongObject *)self; +- Py_ssize_t ndigits = Py_ABS(Py_SIZE(z)); +- Py_ssize_t bit_count = 0; ++ z = (PyLongObject *)self; ++ ndigits = Py_ABS(Py_SIZE(z)); ++ bit_count = 0; + + /* Each digit has up to PyLong_SHIFT ones, so the accumulated bit count + from the first PY_SSIZE_T_MAX/PyLong_SHIFT digits can't overflow a + Py_ssize_t. */ +- Py_ssize_t ndigits_fast = Py_MIN(ndigits, PY_SSIZE_T_MAX/PyLong_SHIFT); +- for (Py_ssize_t i = 0; i < ndigits_fast; i++) { ++ ndigits_fast = Py_MIN(ndigits, PY_SSIZE_T_MAX/PyLong_SHIFT); ++ for (i = 0; i < ndigits_fast; i++) { + bit_count += popcount_digit(z->ob_digit[i]); + } + +- PyObject *result = PyLong_FromSsize_t(bit_count); ++ result = PyLong_FromSsize_t(bit_count); + if (result == NULL) { + return NULL; + } + + /* Use Python integers if bit_count would overflow. */ +- for (Py_ssize_t i = ndigits_fast; i < ndigits; i++) { +- PyObject *x = PyLong_FromLong(popcount_digit(z->ob_digit[i])); ++ for (i = ndigits_fast; i < ndigits; i++) { ++ PyObject *x; ++ PyObject *y; ++ ++ x = PyLong_FromLong(popcount_digit(z->ob_digit[i])); + if (x == NULL) { + goto error; + } +- PyObject *y = long_add((PyLongObject *)result, (PyLongObject *)x); ++ y = long_add((PyLongObject *)result, (PyLongObject *)x); + Py_DECREF(x); + if (y == NULL) { + goto error; +diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c +index e958ab4..1db6045 100644 +--- a/Objects/memoryobject.c ++++ b/Objects/memoryobject.c +@@ -388,8 +388,9 @@ copy_rec(const Py_ssize_t *shape, Py_ssize_t ndim, Py_ssize_t itemsize, + static int + copy_single(PyMemoryViewObject *self, const Py_buffer *dest, const Py_buffer *src) + { ++ char *mem; + CHECK_RELEASED_INT_AGAIN(self); +- char *mem = NULL; ++ mem = NULL; + + assert(dest->ndim == 1); + +@@ -1131,11 +1132,11 @@ get_native_fmtchar(char *result, const char *fmt) + case 'h': case 'H': size = sizeof(short); break; + case 'i': case 'I': size = sizeof(int); break; + case 'l': case 'L': size = sizeof(long); break; +- case 'q': case 'Q': size = sizeof(long long); break; ++ case 'q': case 'Q': size = sizeof(PY_LONG_LONG); break; + case 'n': case 'N': size = sizeof(Py_ssize_t); break; + case 'f': size = sizeof(float); break; + case 'd': size = sizeof(double); break; +- case '?': size = sizeof(_Bool); break; ++ case '?': size = sizeof(unsigned char); break; + case 'P': size = sizeof(void *); break; + } + +@@ -1609,11 +1610,11 @@ pylong_as_lu(PyObject *item) + return lu; + } + +-static long long ++static PY_LONG_LONG + pylong_as_lld(PyObject *item) + { + PyObject *tmp; +- long long lld; ++ PY_LONG_LONG lld; + + tmp = _PyNumber_Index(item); + if (tmp == NULL) +@@ -1624,15 +1625,15 @@ pylong_as_lld(PyObject *item) + return lld; + } + +-static unsigned long long ++static unsigned PY_LONG_LONG + pylong_as_llu(PyObject *item) + { + PyObject *tmp; +- unsigned long long llu; ++ unsigned PY_LONG_LONG llu; + + tmp = _PyNumber_Index(item); + if (tmp == NULL) +- return (unsigned long long)-1; ++ return (unsigned PY_LONG_LONG)-1; + + llu = PyLong_AsUnsignedLongLong(tmp); + Py_DECREF(tmp); +@@ -1685,10 +1686,10 @@ pylong_as_zu(PyObject *item) + static inline PyObject * + unpack_single(PyMemoryViewObject *self, const char *ptr, const char *fmt) + { +- unsigned long long llu; ++ unsigned PY_LONG_LONG llu; + unsigned long lu; + size_t zu; +- long long lld; ++ PY_LONG_LONG lld; + long ld; + Py_ssize_t zd; + double d; +@@ -1707,7 +1708,7 @@ unpack_single(PyMemoryViewObject *self, const char *ptr, const char *fmt) + case 'l': UNPACK_SINGLE(ld, ptr, long); goto convert_ld; + + /* boolean */ +- case '?': UNPACK_SINGLE(ld, ptr, _Bool); goto convert_bool; ++ case '?': UNPACK_SINGLE(ld, ptr, unsigned char); goto convert_bool; + + /* unsigned integers */ + case 'H': UNPACK_SINGLE(lu, ptr, unsigned short); goto convert_lu; +@@ -1715,8 +1716,8 @@ unpack_single(PyMemoryViewObject *self, const char *ptr, const char *fmt) + case 'L': UNPACK_SINGLE(lu, ptr, unsigned long); goto convert_lu; + + /* native 64-bit */ +- case 'q': UNPACK_SINGLE(lld, ptr, long long); goto convert_lld; +- case 'Q': UNPACK_SINGLE(llu, ptr, unsigned long long); goto convert_llu; ++ case 'q': UNPACK_SINGLE(lld, ptr, PY_LONG_LONG); goto convert_lld; ++ case 'Q': UNPACK_SINGLE(llu, ptr, unsigned PY_LONG_LONG); goto convert_llu; + + /* ssize_t and size_t */ + case 'n': UNPACK_SINGLE(zd, ptr, Py_ssize_t); goto convert_zd; +@@ -1777,10 +1778,10 @@ unpack_single(PyMemoryViewObject *self, const char *ptr, const char *fmt) + static int + pack_single(PyMemoryViewObject *self, char *ptr, PyObject *item, const char *fmt) + { +- unsigned long long llu; ++ unsigned PY_LONG_LONG llu; + unsigned long lu; + size_t zu; +- long long lld; ++ PY_LONG_LONG lld; + long ld; + Py_ssize_t zd; + double d; +@@ -1835,14 +1836,14 @@ pack_single(PyMemoryViewObject *self, char *ptr, PyObject *item, const char *fmt + if (lld == -1 && PyErr_Occurred()) + goto err_occurred; + CHECK_RELEASED_INT_AGAIN(self); +- PACK_SINGLE(ptr, lld, long long); ++ PACK_SINGLE(ptr, lld, PY_LONG_LONG); + break; + case 'Q': + llu = pylong_as_llu(item); +- if (llu == (unsigned long long)-1 && PyErr_Occurred()) ++ if (llu == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) + goto err_occurred; + CHECK_RELEASED_INT_AGAIN(self); +- PACK_SINGLE(ptr, llu, unsigned long long); ++ PACK_SINGLE(ptr, llu, unsigned PY_LONG_LONG); + break; + + /* ssize_t and size_t */ +@@ -1881,7 +1882,7 @@ pack_single(PyMemoryViewObject *self, char *ptr, PyObject *item, const char *fmt + if (ld < 0) + return -1; /* preserve original error */ + CHECK_RELEASED_INT_AGAIN(self); +- PACK_SINGLE(ptr, ld, _Bool); ++ PACK_SINGLE(ptr, ld, unsigned char); + break; + + /* bytes object */ +@@ -2735,7 +2736,7 @@ unpack_cmp(const char *p, const char *q, char fmt, + case 'l': CMP_SINGLE(p, q, long); return equal; + + /* boolean */ +- case '?': CMP_SINGLE(p, q, _Bool); return equal; ++ case '?': CMP_SINGLE(p, q, unsigned char); return equal; + + /* unsigned integers */ + case 'H': CMP_SINGLE(p, q, unsigned short); return equal; +@@ -2743,8 +2744,8 @@ unpack_cmp(const char *p, const char *q, char fmt, + case 'L': CMP_SINGLE(p, q, unsigned long); return equal; + + /* native 64-bit */ +- case 'q': CMP_SINGLE(p, q, long long); return equal; +- case 'Q': CMP_SINGLE(p, q, unsigned long long); return equal; ++ case 'q': CMP_SINGLE(p, q, PY_LONG_LONG); return equal; ++ case 'Q': CMP_SINGLE(p, q, unsigned PY_LONG_LONG); return equal; + + /* ssize_t and size_t */ + case 'n': CMP_SINGLE(p, q, Py_ssize_t); return equal; +@@ -3208,9 +3209,12 @@ memoryiter_next(memoryiterobject *it) + } + + if (it->it_index < it->it_length) { ++ Py_buffer *view; ++ char *ptr; ++ + CHECK_RELEASED(seq); +- Py_buffer *view = &(seq->view); +- char *ptr = (char *)seq->view.buf; ++ view = &(seq->view); ++ ptr = (char *)seq->view.buf; + + ptr += view->strides[0] * it->it_index++; + ptr = ADJUST_PTR(ptr, view->suboffsets, 0); +@@ -3228,12 +3232,17 @@ memoryiter_next(memoryiterobject *it) + static PyObject * + memory_iter(PyObject *seq) + { ++ PyMemoryViewObject *obj; ++ int ndims; ++ const char *fmt; ++ memoryiterobject *it; ++ + if (!PyMemoryView_Check(seq)) { + PyErr_BadInternalCall(); + return NULL; + } +- PyMemoryViewObject *obj = (PyMemoryViewObject *)seq; +- int ndims = obj->view.ndim; ++ obj = (PyMemoryViewObject *)seq; ++ ndims = obj->view.ndim; + if (ndims == 0) { + PyErr_SetString(PyExc_TypeError, "invalid indexing of 0-dim memory"); + return NULL; +@@ -3244,12 +3253,11 @@ memory_iter(PyObject *seq) + return NULL; + } + +- const char *fmt = adjust_fmt(&obj->view); ++ fmt = adjust_fmt(&obj->view); + if (fmt == NULL) { + return NULL; + } + +- memoryiterobject *it; + it = PyObject_GC_New(memoryiterobject, &_PyMemoryIter_Type); + if (it == NULL) { + return NULL; +@@ -3265,15 +3273,32 @@ memory_iter(PyObject *seq) + + PyTypeObject _PyMemoryIter_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) +- .tp_name = "memory_iterator", +- .tp_basicsize = sizeof(memoryiterobject), +- // methods +- .tp_dealloc = (destructor)memoryiter_dealloc, +- .tp_getattro = PyObject_GenericGetAttr, +- .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, +- .tp_traverse = (traverseproc)memoryiter_traverse, +- .tp_iter = PyObject_SelfIter, +- .tp_iternext = (iternextfunc)memoryiter_next, ++ "memory_iterator", ++ sizeof(memoryiterobject), ++ 0, ++ (destructor)memoryiter_dealloc, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ PyObject_GenericGetAttr, ++ 0, ++ 0, ++ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, ++ 0, ++ (traverseproc)memoryiter_traverse, ++ 0, ++ 0, ++ 0, ++ PyObject_SelfIter, ++ (iternextfunc)memoryiter_next, + }; + + PyTypeObject PyMemoryView_Type = { +diff --git a/Objects/methodobject.c b/Objects/methodobject.c +index 953cf46..d6691ac 100644 +--- a/Objects/methodobject.c ++++ b/Objects/methodobject.c +@@ -45,6 +45,8 @@ PyCMethod_New(PyMethodDef *ml, PyObject *self, PyObject *module, PyTypeObject *c + { + /* Figure out correct vectorcall function to use */ + vectorcallfunc vectorcall; ++ PyCFunctionObject *op; ++ PyCMethodObject *om; + switch (ml->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | + METH_O | METH_KEYWORDS | METH_METHOD)) + { +@@ -75,7 +77,7 @@ PyCMethod_New(PyMethodDef *ml, PyObject *self, PyObject *module, PyTypeObject *c + return NULL; + } + +- PyCFunctionObject *op = NULL; ++ op = NULL; + + if (ml->ml_flags & METH_METHOD) { + if (!cls) { +@@ -84,7 +86,7 @@ PyCMethod_New(PyMethodDef *ml, PyObject *self, PyObject *module, PyTypeObject *c + "flag but no class"); + return NULL; + } +- PyCMethodObject *om = PyObject_GC_New(PyCMethodObject, &PyCMethod_Type); ++ om = PyObject_GC_New(PyCMethodObject, &PyCMethod_Type); + if (om == NULL) { + return NULL; + } +@@ -369,9 +371,36 @@ PyTypeObject PyCFunction_Type = { + + PyTypeObject PyCMethod_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) +- .tp_name = "builtin_method", +- .tp_basicsize = sizeof(PyCMethodObject), +- .tp_base = &PyCFunction_Type, ++ "builtin_method", ++ sizeof(PyCMethodObject), ++ 0, /* tp_itemsize */ ++ 0, /* tp_dealloc */ ++ 0, /* tp_vectorcall_offset */ ++ 0, /* tp_getattr */ ++ 0, /* tp_setattr */ ++ 0, /* tp_as_async */ ++ 0, /* tp_repr */ ++ 0, /* tp_as_number */ ++ 0, /* tp_as_sequence */ ++ 0, /* tp_as_mapping */ ++ 0, /* tp_hash */ ++ 0, /* tp_call */ ++ 0, /* tp_str */ ++ 0, /* tp_getattro */ ++ 0, /* tp_setattro */ ++ 0, /* tp_as_buffer */ ++ 0, /* tp_flags */ ++ 0, /* tp_doc */ ++ 0, /* tp_traverse */ ++ 0, /* tp_clear */ ++ 0, /* tp_richcompare */ ++ 0, /* tp_weaklistoffset */ ++ 0, /* tp_iter */ ++ 0, /* tp_iternext */ ++ 0, /* tp_methods */ ++ 0, /* tp_members */ ++ 0, /* tp_getset */ ++ &PyCFunction_Type, /* tp_base */ + }; + + /* Vectorcall functions for each of the PyCFunction calling conventions, +@@ -414,17 +443,22 @@ static PyObject * + cfunction_vectorcall_FASTCALL( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) + { +- PyThreadState *tstate = _PyThreadState_GET(); ++ PyThreadState *tstate; ++ Py_ssize_t nargs; ++ _PyCFunctionFast meth; ++ PyObject *result; ++ ++ tstate = _PyThreadState_GET(); + if (cfunction_check_kwargs(tstate, func, kwnames)) { + return NULL; + } +- Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); +- _PyCFunctionFast meth = (_PyCFunctionFast) ++ nargs = PyVectorcall_NARGS(nargsf); ++ meth = (_PyCFunctionFast) + cfunction_enter_call(tstate, func); + if (meth == NULL) { + return NULL; + } +- PyObject *result = meth(PyCFunction_GET_SELF(func), args, nargs); ++ result = meth(PyCFunction_GET_SELF(func), args, nargs); + _Py_LeaveRecursiveCallTstate(tstate); + return result; + } +@@ -433,14 +467,19 @@ static PyObject * + cfunction_vectorcall_FASTCALL_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) + { +- PyThreadState *tstate = _PyThreadState_GET(); +- Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); +- _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords) ++ PyThreadState *tstate; ++ Py_ssize_t nargs; ++ _PyCFunctionFastWithKeywords meth; ++ PyObject *result; ++ ++ tstate = _PyThreadState_GET(); ++ nargs = PyVectorcall_NARGS(nargsf); ++ meth = (_PyCFunctionFastWithKeywords) + cfunction_enter_call(tstate, func); + if (meth == NULL) { + return NULL; + } +- PyObject *result = meth(PyCFunction_GET_SELF(func), args, nargs, kwnames); ++ result = meth(PyCFunction_GET_SELF(func), args, nargs, kwnames); + _Py_LeaveRecursiveCallTstate(tstate); + return result; + } +@@ -449,6 +488,7 @@ static PyObject * + cfunction_vectorcall_FASTCALL_KEYWORDS_METHOD( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) + { ++ PyObject *result; + PyThreadState *tstate = _PyThreadState_GET(); + PyTypeObject *cls = PyCFunction_GET_CLASS(func); + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); +@@ -456,7 +496,7 @@ cfunction_vectorcall_FASTCALL_KEYWORDS_METHOD( + if (meth == NULL) { + return NULL; + } +- PyObject *result = meth(PyCFunction_GET_SELF(func), cls, args, nargs, kwnames); ++ result = meth(PyCFunction_GET_SELF(func), cls, args, nargs, kwnames); + _Py_LeaveRecursiveCallTstate(tstate); + return result; + } +@@ -465,11 +505,14 @@ static PyObject * + cfunction_vectorcall_NOARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) + { ++ Py_ssize_t nargs; ++ PyCFunction meth; ++ PyObject *result; + PyThreadState *tstate = _PyThreadState_GET(); + if (cfunction_check_kwargs(tstate, func, kwnames)) { + return NULL; + } +- Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); ++ nargs = PyVectorcall_NARGS(nargsf); + if (nargs != 0) { + PyObject *funcstr = _PyObject_FunctionStr(func); + if (funcstr != NULL) { +@@ -479,11 +522,11 @@ cfunction_vectorcall_NOARGS( + } + return NULL; + } +- PyCFunction meth = (PyCFunction)cfunction_enter_call(tstate, func); ++ meth = (PyCFunction)cfunction_enter_call(tstate, func); + if (meth == NULL) { + return NULL; + } +- PyObject *result = _PyCFunction_TrampolineCall( ++ result = _PyCFunction_TrampolineCall( + meth, PyCFunction_GET_SELF(func), NULL); + _Py_LeaveRecursiveCallTstate(tstate); + return result; +@@ -493,11 +536,14 @@ static PyObject * + cfunction_vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) + { ++ Py_ssize_t nargs; ++ PyCFunction meth; ++ PyObject *result; + PyThreadState *tstate = _PyThreadState_GET(); + if (cfunction_check_kwargs(tstate, func, kwnames)) { + return NULL; + } +- Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); ++ nargs = PyVectorcall_NARGS(nargsf); + if (nargs != 1) { + PyObject *funcstr = _PyObject_FunctionStr(func); + if (funcstr != NULL) { +@@ -507,11 +553,11 @@ cfunction_vectorcall_O( + } + return NULL; + } +- PyCFunction meth = (PyCFunction)cfunction_enter_call(tstate, func); ++ meth = (PyCFunction)cfunction_enter_call(tstate, func); + if (meth == NULL) { + return NULL; + } +- PyObject *result = _PyCFunction_TrampolineCall( ++ result = _PyCFunction_TrampolineCall( + meth, PyCFunction_GET_SELF(func), args[0]); + _Py_LeaveRecursiveCallTstate(tstate); + return result; +@@ -521,12 +567,18 @@ cfunction_vectorcall_O( + static PyObject * + cfunction_call(PyObject *func, PyObject *args, PyObject *kwargs) + { ++ PyThreadState *tstate; ++ int flags; ++ PyCFunction meth; ++ PyObject *self; ++ PyObject *result; ++ + assert(kwargs == NULL || PyDict_Check(kwargs)); + +- PyThreadState *tstate = _PyThreadState_GET(); ++ tstate = _PyThreadState_GET(); + assert(!_PyErr_Occurred(tstate)); + +- int flags = PyCFunction_GET_FLAGS(func); ++ flags = PyCFunction_GET_FLAGS(func); + if (!(flags & METH_VARARGS)) { + /* If this is not a METH_VARARGS function, delegate to vectorcall */ + return PyVectorcall_Call(func, args, kwargs); +@@ -534,10 +586,8 @@ cfunction_call(PyObject *func, PyObject *args, PyObject *kwargs) + + /* For METH_VARARGS, we cannot use vectorcall as the vectorcall pointer + * is NULL. This is intentional, since vectorcall would be slower. */ +- PyCFunction meth = PyCFunction_GET_FUNCTION(func); +- PyObject *self = PyCFunction_GET_SELF(func); +- +- PyObject *result; ++ meth = PyCFunction_GET_FUNCTION(func); ++ self = PyCFunction_GET_SELF(func); + if (flags & METH_KEYWORDS) { + result = _PyCFunctionWithKeywords_TrampolineCall( + (*(PyCFunctionWithKeywords)(void(*)(void))meth), +diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c +index d18f206..1104e46 100644 +--- a/Objects/moduleobject.c ++++ b/Objects/moduleobject.c +@@ -28,12 +28,15 @@ PyTypeObject PyModuleDef_Type = { + int + _PyModule_IsExtension(PyObject *obj) + { ++ PyModuleObject *module; ++ PyModuleDef *def; ++ + if (!PyModule_Check(obj)) { + return 0; + } +- PyModuleObject *module = (PyModuleObject*)obj; ++ module = (PyModuleObject*)obj; + +- PyModuleDef *def = module->md_def; ++ def = module->md_def; + return (def != NULL && def->m_methods != NULL); + } + +@@ -737,16 +740,19 @@ _PyModuleSpec_IsInitializing(PyObject *spec) + int + _PyModuleSpec_IsUninitializedSubmodule(PyObject *spec, PyObject *name) + { ++ PyObject *value; ++ int is_uninitialized; ++ + if (spec == NULL) { + return 0; + } + +- PyObject *value = PyObject_GetAttr(spec, &_Py_ID(_uninitialized_submodules)); ++ value = PyObject_GetAttr(spec, &_Py_ID(_uninitialized_submodules)); + if (value == NULL) { + return 0; + } + +- int is_uninitialized = PySequence_Contains(value, name); ++ is_uninitialized = PySequence_Contains(value, name); + Py_DECREF(value); + if (is_uninitialized == -1) { + return 0; +@@ -757,7 +763,7 @@ _PyModuleSpec_IsUninitializedSubmodule(PyObject *spec, PyObject *name) + static PyObject* + module_getattro(PyModuleObject *m, PyObject *name) + { +- PyObject *attr, *mod_name, *getattr; ++ PyObject *attr, *mod_name, *getattr, *spec; + attr = PyObject_GenericGetAttr((PyObject *)m, name); + if (attr || !PyErr_ExceptionMatches(PyExc_AttributeError)) { + return attr; +@@ -774,7 +780,7 @@ module_getattro(PyModuleObject *m, PyObject *name) + mod_name = PyDict_GetItemWithError(m->md_dict, &_Py_ID(__name__)); + if (mod_name && PyUnicode_Check(mod_name)) { + Py_INCREF(mod_name); +- PyObject *spec = PyDict_GetItemWithError(m->md_dict, &_Py_ID(__spec__)); ++ spec = PyDict_GetItemWithError(m->md_dict, &_Py_ID(__spec__)); + if (spec == NULL && PyErr_Occurred()) { + Py_DECREF(mod_name); + return NULL; +@@ -880,7 +886,11 @@ static PyMethodDef module_methods[] = { + static PyObject * + module_get_annotations(PyModuleObject *m, void *Py_UNUSED(ignored)) + { +- PyObject *dict = PyObject_GetAttr((PyObject *)m, &_Py_ID(__dict__)); ++ PyObject *dict; ++ PyObject *annotations; ++ int result; ++ ++ dict = PyObject_GetAttr((PyObject *)m, &_Py_ID(__dict__)); + if (dict == NULL) { + return NULL; + } +@@ -890,14 +900,14 @@ module_get_annotations(PyModuleObject *m, void *Py_UNUSED(ignored)) + return NULL; + } + +- PyObject *annotations = PyDict_GetItemWithError(dict, &_Py_ID(__annotations__)); ++ annotations = PyDict_GetItemWithError(dict, &_Py_ID(__annotations__)); + if (annotations) { + Py_INCREF(annotations); + } + else if (!PyErr_Occurred()) { + annotations = PyDict_New(); + if (annotations) { +- int result = PyDict_SetItem( ++ result = PyDict_SetItem( + dict, &_Py_ID(__annotations__), annotations); + if (result) { + Py_CLEAR(annotations); diff --git a/Objects/object.c b/Objects/object.c -index c4f2786..72ba7d5 100644 +index c4f2786..8e2a684 100644 --- a/Objects/object.c +++ b/Objects/object.c -@@ -30,6 +30,13 @@ extern "C" { +@@ -30,6 +30,14 @@ extern "C" { /* Defined in tracemalloc.c */ extern void _PyMem_DumpTraceback(int fd, const void *ptr); @@ -18114,12 +21196,111 @@ index c4f2786..72ba7d5 100644 +extern void _PyDesc_InitTypes(void); +extern void _PyDict_InitTypes(void); +extern void _PyEnum_InitTypes(void); ++extern void _Py_GenericAlias_InitTypes(void); +#endif + int _PyObject_CheckConsistency(PyObject *op, int check_content) -@@ -1980,6 +1987,13 @@ _PyTypes_InitTypes(PyInterpreterState *interp) +@@ -214,6 +222,8 @@ PyObject_CallFinalizer(PyObject *self) + int + PyObject_CallFinalizerFromDealloc(PyObject *self) + { ++ Py_ssize_t refcnt; ++ + if (Py_REFCNT(self) != 0) { + _PyObject_ASSERT_FAILED_MSG(self, + "PyObject_CallFinalizerFromDealloc called " +@@ -238,7 +248,7 @@ PyObject_CallFinalizerFromDealloc(PyObject *self) + + /* tp_finalize resurrected it! Make it look like the original Py_DECREF + * never happened. */ +- Py_ssize_t refcnt = Py_REFCNT(self); ++ refcnt = Py_REFCNT(self); + _Py_NewReference(self); + Py_SET_REFCNT(self, refcnt); + +@@ -360,6 +370,10 @@ _PyObject_IsFreed(PyObject *op) + void + _PyObject_Dump(PyObject* op) + { ++ PyTypeObject *type; ++ PyGILState_STATE gil; ++ PyObject *error_type, *error_value, *error_traceback; ++ + if (_PyObject_IsFreed(op)) { + /* It seems like the object memory has been freed: + don't access it to prevent a segmentation fault. */ +@@ -375,7 +389,7 @@ _PyObject_Dump(PyObject* op) + fprintf(stderr, "object refcount : %ld\n", (long)Py_REFCNT(op)); + fflush(stderr); + +- PyTypeObject *type = Py_TYPE(op); ++ type = Py_TYPE(op); + fprintf(stderr, "object type : %p\n", type); + fprintf(stderr, "object type name: %s\n", + type==NULL ? "NULL" : type->tp_name); +@@ -384,8 +398,7 @@ _PyObject_Dump(PyObject* op) + fprintf(stderr, "object repr : "); + fflush(stderr); + +- PyGILState_STATE gil = PyGILState_Ensure(); +- PyObject *error_type, *error_value, *error_traceback; ++ gil = PyGILState_Ensure(); + PyErr_Fetch(&error_type, &error_value, &error_traceback); + + (void)PyObject_Print(op, stderr, 0); +@@ -402,6 +415,7 @@ PyObject * + PyObject_Repr(PyObject *v) + { + PyObject *res; ++ PyThreadState *tstate; + if (PyErr_CheckSignals()) + return NULL; + #ifdef USE_STACKCHECK +@@ -416,7 +430,7 @@ PyObject_Repr(PyObject *v) + return PyUnicode_FromFormat("<%s object at %p>", + Py_TYPE(v)->tp_name, v); + +- PyThreadState *tstate = _PyThreadState_GET(); ++ tstate = _PyThreadState_GET(); + #ifdef Py_DEBUG + /* PyObject_Repr() must not be called with an exception set, + because it can clear it (directly or indirectly) and so the +@@ -455,6 +469,7 @@ PyObject * + PyObject_Str(PyObject *v) + { + PyObject *res; ++ PyThreadState *tstate; + if (PyErr_CheckSignals()) + return NULL; + #ifdef USE_STACKCHECK +@@ -476,7 +491,7 @@ PyObject_Str(PyObject *v) + if (Py_TYPE(v)->tp_str == NULL) + return PyObject_Repr(v); + +- PyThreadState *tstate = _PyThreadState_GET(); ++ tstate = _PyThreadState_GET(); + #ifdef Py_DEBUG + /* PyObject_Str() must not be called with an exception set, + because it can clear it (directly or indirectly) and so the +@@ -589,9 +604,13 @@ def _PyObject_FunctionStr(x): + PyObject * + _PyObject_FunctionStr(PyObject *x) + { +- assert(!PyErr_Occurred()); + PyObject *qualname; +- int ret = _PyObject_LookupAttr(x, &_Py_ID(__qualname__), &qualname); ++ PyObject *module; ++ PyObject *result; ++ int ret; ++ ++ assert(!PyErr_Occurred()); ++ ret = _PyObject_LookupAttr(x, &_Py_ID(__qualname__), &qualname); + if (qualname == NULL) { + if (ret < 0) { + return NULL; +@@ -1980,6 +1999,14 @@ _PyTypes_InitTypes(PyInterpreterState *interp) return _PyStatus_OK(); } @@ -18128,6 +21309,7 @@ index c4f2786..72ba7d5 100644 + _PyDesc_InitTypes(); + _PyDict_InitTypes(); + _PyEnum_InitTypes(); ++ _Py_GenericAlias_InitTypes(); +#endif + // All other static types (unless initialized elsewhere) @@ -21367,3 +24549,38 @@ index a451166..f26d2c2 100644 if (base == &tstate->datastack_chunk->data[0]) { _PyStackChunk *chunk = tstate->datastack_chunk; _PyStackChunk *previous = chunk->previous; +diff --git a/Include/internal/pycore_win9x_loadlibrary.h b/Include/internal/pycore_win9x_loadlibrary.h +new file mode 100644 +index 0000000..47fd5c3 +--- /dev/null ++++ b/Include/internal/pycore_win9x_loadlibrary.h +@@ -0,0 +1,29 @@ ++#ifndef PyCORE_WIN9X_LOADLIBRARY_H ++#define PyCORE_WIN9X_LOADLIBRARY_H ++ ++/* ++ * The VC98 SDK predates the restricted DLL-search flags introduced by newer ++ * Windows SDKs. Define their documented bit values so Windows sources can be ++ * compiled with MSVC 6.0. This is compile-time compatibility only: Windows 9x ++ * does not implement these LoadLibraryEx search semantics, so runtime loader ++ * call sites still require a legacy-compatible path. ++ */ ++#if defined(_MSC_VER) && _MSC_VER < 1300 ++# ifndef LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR ++# define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100 ++# endif ++# ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR ++# define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x00000200 ++# endif ++# ifndef LOAD_LIBRARY_SEARCH_USER_DIRS ++# define LOAD_LIBRARY_SEARCH_USER_DIRS 0x00000400 ++# endif ++# ifndef LOAD_LIBRARY_SEARCH_SYSTEM32 ++# define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 ++# endif ++# ifndef LOAD_LIBRARY_SEARCH_DEFAULT_DIRS ++# define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000 ++# endif ++#endif ++ ++#endif /* PyCORE_WIN9X_LOADLIBRARY_H */ diff --git a/scripts/vc6-frameobject-c89.test.py b/scripts/vc6-frameobject-c89.test.py index d79d74f..e51e0b5 100644 --- a/scripts/vc6-frameobject-c89.test.py +++ b/scripts/vc6-frameobject-c89.test.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Guard C89 local ordering in frame_getlocals.""" +"""Guard C89 local ordering and frame operation order.""" import sys from pathlib import Path from typing import NoReturn @@ -92,4 +92,479 @@ if " int result, i;" not in firstline or "for (int i =" in firstline: fail("first_line_not_before loop index must be declared at function entry") if not firstline.find("result = INT_MAX;") < firstline.find("for (i = 0; i < len; i++)"): fail("first_line_not_before must initialize its minimum before scanning") + +setlineno = extract_function(source, 'frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignored))') +entry_declarations = ( + 'PyFrameState state;', + 'int i;', + 'int new_lineno;', + 'int overflow;', + 'long l_new_lineno;', + 'int len;', + 'int *lines;', + 'int64_t *stacks;', + 'int64_t best_stack;', + 'int best_addr;', + 'int64_t start_stack;', + 'int err;', + 'const char *msg;', +) +remaining = setlineno.lstrip() +for declaration in entry_declarations: + if not remaining.startswith(declaration + "\n"): + fail("frame_setlineno function-entry declaration missing or out of order: " + declaration) + remaining = remaining[len(declaration):].lstrip() + +# Freeze the execution body, including validation, cleanup, error selection, +# loop order, frame stack operations, and unchanged nested block declarations. +# Only blank lines and indentation are ignored; no executable text is omitted. +expected_execution = r""" + + if (p_new_lineno == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } + /* f_lineno must be an integer. */ + if (!PyLong_CheckExact(p_new_lineno)) { + PyErr_SetString(PyExc_ValueError, + "lineno must be an integer"); + return -1; + } + + state = _PyFrame_GetState(f); + /* + * This code preserves the historical restrictions on + * setting the line number of a frame. + * Jumps are forbidden on a 'return' trace event (except after a yield). + * Jumps from 'call' trace events are also forbidden. + * In addition, jumps are forbidden when not tracing, + * as this is a debugging feature. + */ + switch(PyThreadState_GET()->tracing_what) { + case PyTrace_EXCEPTION: + PyErr_SetString(PyExc_ValueError, + "can only jump from a 'line' trace event"); + return -1; + case PyTrace_CALL: + PyErr_Format(PyExc_ValueError, + "can't jump from the 'call' trace event of a new frame"); + return -1; + case PyTrace_LINE: + break; + case PyTrace_RETURN: + if (state == FRAME_SUSPENDED) { + break; + } + /* fall through */ + default: + PyErr_SetString(PyExc_ValueError, + "can only jump from a 'line' trace event"); + return -1; + } + if (!f->f_trace) { + PyErr_Format(PyExc_ValueError, + "f_lineno can only be set by a trace function"); + return -1; + } + + + /* Fail if the line falls outside the code block and + select first line with actual code. */ + l_new_lineno = PyLong_AsLongAndOverflow(p_new_lineno, &overflow); + if (overflow +#if SIZEOF_LONG > SIZEOF_INT + || l_new_lineno > INT_MAX + || l_new_lineno < INT_MIN +#endif + ) { + PyErr_SetString(PyExc_ValueError, + "lineno out of range"); + return -1; + } + new_lineno = (int)l_new_lineno; + + if (new_lineno < f->f_frame->f_code->co_firstlineno) { + PyErr_Format(PyExc_ValueError, + "line %d comes before the current code block", + new_lineno); + return -1; + } + + /* PyCode_NewWithPosOnlyArgs limits co_code to be under INT_MAX so this + * should never overflow. */ + len = (int)Py_SIZE(f->f_frame->f_code); + lines = marklines(f->f_frame->f_code, len); + if (lines == NULL) { + return -1; + } + + new_lineno = first_line_not_before(lines, len, new_lineno); + if (new_lineno < 0) { + PyErr_Format(PyExc_ValueError, + "line %d comes after the current code block", + (int)l_new_lineno); + PyMem_Free(lines); + return -1; + } + + stacks = mark_stacks(f->f_frame->f_code, len); + if (stacks == NULL) { + PyMem_Free(lines); + return -1; + } + + best_stack = OVERFLOWED; + best_addr = -1; + start_stack = stacks[_PyInterpreterFrame_LASTI(f->f_frame)]; + err = -1; + msg = "cannot find bytecode for specified line"; + for (i = 0; i < len; i++) { + if (lines[i] == new_lineno) { + int64_t target_stack = stacks[i]; + if (compatible_stack(start_stack, target_stack)) { + err = 0; + if (target_stack > best_stack) { + best_stack = target_stack; + best_addr = i; + } + } + else if (err < 0) { + if (start_stack == OVERFLOWED) { + msg = "stack to deep to analyze"; + } + else if (start_stack == UNINITIALIZED) { + msg = "can't jump from unreachable code"; + } + else { + msg = explain_incompatible_stack(target_stack); + err = 1; + } + } + } + } + PyMem_Free(stacks); + PyMem_Free(lines); + if (err) { + PyErr_SetString(PyExc_ValueError, msg); + return -1; + } + if (state == FRAME_SUSPENDED) { + /* Account for value popped by yield */ + start_stack = pop_value(start_stack); + } + while (start_stack > best_stack) { + if (top_of_stack(start_stack) == Except) { + /* Pop exception stack as well as the evaluation stack */ + PyThreadState *tstate = _PyThreadState_GET(); + _PyErr_StackItem *exc_info = tstate->exc_info; + PyObject *value = exc_info->exc_value; + PyObject *exc = _PyFrame_StackPop(f->f_frame); + assert(PyExceptionInstance_Check(exc) || exc == Py_None); + exc_info->exc_value = exc; + Py_XDECREF(value); + } + else { + PyObject *v = _PyFrame_StackPop(f->f_frame); + Py_XDECREF(v); + } + start_stack = pop_value(start_stack); + } + /* Finally set the new lasti and return OK. */ + f->f_lineno = 0; + f->f_frame->prev_instr = _PyCode_CODE(f->f_frame->f_code) + best_addr; + return 0; +""" +def execution_lines(text: str) -> list: + return [line.strip() for line in text.splitlines() if line.strip()] + +if execution_lines(remaining) != execution_lines(expected_execution): + fail("frame_setlineno execution body changed or contains a late function-scope declaration") + +frame_dealloc = extract_function(source, "frame_dealloc(PyFrameObject *f)") +expected_dealloc_declarations = ( + "PyCodeObject *co;", + "_PyInterpreterFrame *frame;", + "PyObject **locals;", + "int i;", +) +remaining = frame_dealloc.lstrip() +for declaration in expected_dealloc_declarations: + if not remaining.startswith(declaration + "\n"): + fail("frame_dealloc entry declaration missing or out of order: " + declaration) + remaining = remaining[len(declaration):].lstrip() +if "PyCodeObject *co =" in frame_dealloc or "_PyInterpreterFrame *frame =" in frame_dealloc or "PyObject **locals =" in frame_dealloc or "for (int i =" in frame_dealloc: + fail("frame_dealloc must not declare locals after statements") +ordered_dealloc = ( + "Py_TRASHCAN_BEGIN(f, frame_dealloc);", + "co = NULL;", + "frame = (_PyInterpreterFrame *)f->_f_frame_data;", + "if (f->f_frame == frame && frame->owner == FRAME_OWNED_BY_FRAME_OBJECT)", + "co = frame->f_code;", + "frame->f_code = NULL;", + "Py_CLEAR(frame->f_func);", + "Py_CLEAR(frame->f_locals);", + "locals = _PyFrame_GetLocalsArray(frame);", + "for (i = 0; i < frame->stacktop; i++)", + "Py_CLEAR(f->f_back);", + "Py_CLEAR(f->f_trace);", + "PyObject_GC_Del(f);", + "Py_XDECREF(co);", + "Py_TRASHCAN_END;", +) +positions = [frame_dealloc.find(token) for token in ordered_dealloc] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("frame_dealloc must preserve trashcan, owned-frame cleanup, and decref order") + +frame_clear_locals = extract_function(source, "frame_tp_clear(PyFrameObject *f)") +remaining = frame_clear_locals.lstrip() +for declaration in ("PyObject **locals;", "int i;"): + if not remaining.startswith(declaration + "\n"): + fail("frame_tp_clear entry declaration missing or out of order: " + declaration) + remaining = remaining[len(declaration):].lstrip() +if "PyObject **locals =" in frame_clear_locals or "for (int i =" in frame_clear_locals: + fail("frame_tp_clear must not declare locals after statements") +ordered_clear = ( + "Py_CLEAR(f->f_trace);", + "locals = _PyFrame_GetLocalsArray(f->f_frame);", + "assert(f->f_frame->stacktop >= 0);", + "for (i = 0; i < f->f_frame->stacktop; i++)", + "Py_CLEAR(locals[i]);", + "f->f_frame->stacktop = 0;", +) +positions = [frame_clear_locals.find(token) for token in ordered_clear] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("frame_tp_clear must preserve trace/local/stack cleanup order") + +frame_sizeof = extract_function(source, "frame_sizeof(PyFrameObject *f, PyObject *Py_UNUSED(ignored))") +remaining = frame_sizeof.lstrip() +for declaration in ("Py_ssize_t res;", "PyCodeObject *code;"): + if not remaining.startswith(declaration + "\n"): + fail("frame_sizeof entry declaration missing or out of order: " + declaration) + remaining = remaining[len(declaration):].lstrip() +if "PyCodeObject *code =" in frame_sizeof: + fail("frame_sizeof code pointer must not be declared after initialization") +ordered_sizeof = ( + "res = offsetof(PyFrameObject, _f_frame_data) + offsetof(_PyInterpreterFrame, localsplus);", + "code = f->f_frame->f_code;", + "res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *);", + "return PyLong_FromSsize_t(res);", +) +positions = [frame_sizeof.find(token) for token in ordered_sizeof] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("frame_sizeof must preserve size calculation and return order") + +init_frame = extract_function(source, "init_frame(_PyInterpreterFrame *frame, PyFunctionObject *func, PyObject *locals)") +remaining = init_frame.lstrip() +for declaration in ("PyCodeObject *code;", "Py_ssize_t i;"): + if not remaining.startswith(declaration + "\n"): + fail("init_frame entry declaration missing or out of order: " + declaration) + remaining = remaining[len(declaration):].lstrip() +if "PyCodeObject *code =" in init_frame or "for (Py_ssize_t i =" in init_frame: + fail("init_frame must not declare locals after statements") +ordered_init_frame = ( + "Py_INCREF(func);", + "code = (PyCodeObject *)func->func_code;", + "_PyFrame_InitializeSpecials(frame, func, locals, code->co_nlocalsplus);", + "frame->previous = NULL;", + "for (i = 0; i < code->co_nlocalsplus; i++)", + "frame->localsplus[i] = NULL;", +) +positions = [init_frame.find(token) for token in ordered_init_frame] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("init_frame must preserve reference and frame initialization order") + +new_no_track = extract_function(source, "_PyFrame_New_NoTrack(PyCodeObject *code)") +remaining = new_no_track.lstrip() +for declaration in ("int slots;", "PyFrameObject *f;"): + if not remaining.startswith(declaration + "\n"): + fail("_PyFrame_New_NoTrack entry declaration missing or out of order: " + declaration) + remaining = remaining[len(declaration):].lstrip() +if "int slots =" in new_no_track or "PyFrameObject *f =" in new_no_track: + fail("_PyFrame_New_NoTrack locals must not be initialized after statements") +ordered_new_no_track = ( + "CALL_STAT_INC(frame_objects_created);", + "slots = code->co_nlocalsplus + code->co_stacksize;", + "f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, slots);", + "if (f == NULL)", + "f->f_back = NULL;", + "f->f_lineno = 0;", + "return f;", +) +positions = [new_no_track.find(token) for token in ordered_new_no_track] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("_PyFrame_New_NoTrack must preserve stat, allocation, and initialization order") + +frame_new = extract_function(source, "PyFrame_New(PyThreadState *tstate, PyCodeObject *code,") +remaining = frame_new.lstrip() +for declaration in ( + "PyObject *builtins;", + "PyFrameConstructor desc;", + "PyFunctionObject *func;", + "PyFrameObject *f;", +): + if not remaining.startswith(declaration + "\n"): + fail("PyFrame_New entry declaration missing or out of order: " + declaration) + remaining = remaining[len(declaration):].lstrip() +if "PyFrameConstructor desc =" in frame_new or "PyFunctionObject *func =" in frame_new or "PyFrameObject *f =" in frame_new: + fail("PyFrame_New must not use initialized late declarations") +if "\n .fc_globals =" in frame_new or "\n .fc_builtins =" in frame_new: + fail("PyFrame_New constructor must not use designated initializers") +ordered_frame_new = ( + "builtins = _PyEval_BuiltinsFromGlobals(tstate, globals);", + "if (builtins == NULL)", + "desc.fc_globals = globals;", + "desc.fc_builtins = builtins;", + "desc.fc_name = code->co_name;", + "desc.fc_qualname = code->co_name;", + "desc.fc_code = (PyObject *)code;", + "desc.fc_defaults = NULL;", + "desc.fc_kwdefaults = NULL;", + "desc.fc_closure = NULL;", + "func = _PyFunction_FromConstructor(&desc);", + "if (func == NULL)", + "f = _PyFrame_New_NoTrack(code);", + "if (f == NULL)", + "init_frame((_PyInterpreterFrame *)f->_f_frame_data, func, locals);", + "Py_DECREF(func);", + "return f;", +) +positions = [frame_new.rfind(token) if token == "Py_DECREF(func);" else frame_new.find(token) for token in ordered_frame_new] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("PyFrame_New must preserve validation, construction, and cleanup order") + +op_already_ran = extract_function(source, "_PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, int opcode, int oparg)") +remaining = op_already_ran.lstrip() +for declaration in ("int check_oparg;", "_Py_CODEUNIT *instruction;"): + if not remaining.startswith(declaration + "\n"): + fail("_PyFrame_OpAlreadyRan entry declaration missing or out of order: " + declaration) + remaining = remaining[len(declaration):].lstrip() +if "int check_oparg =" in op_already_ran or "for (_Py_CODEUNIT *instruction =" in op_already_ran: + fail("_PyFrame_OpAlreadyRan must not declare locals after statements") +ordered_op_check = ( + "assert(_PyOpcode_Deopt[opcode] == opcode);", + "check_oparg = 0;", + "for (instruction = _PyCode_CODE(frame->f_code);", + "instruction < frame->prev_instr; instruction++)", + "int check_opcode = _PyOpcode_Deopt[_Py_OPCODE(*instruction)];", + "check_oparg |= _Py_OPARG(*instruction);", + "instruction += _PyOpcode_Caches[check_opcode];", + "return 0;", +) +positions = [op_already_ran.find(token) for token in ordered_op_check] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("_PyFrame_OpAlreadyRan must preserve assert, opcode scan, and return order") + +fast_to_locals = extract_function(source, "_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame)") +entry_statements = fast_to_locals.find("locals = frame->f_locals;") +for declaration in ("int lasti;", "int i;"): + if fast_to_locals.find(declaration) < 0 or fast_to_locals.find(declaration) > entry_statements: + fail("_PyFrame_FastToLocalsWithError loop/state locals must be declared at entry: " + declaration) +if "int lasti =" in fast_to_locals or "for (int i =" in fast_to_locals: + fail("_PyFrame_FastToLocalsWithError must not use late or loop declarations") +if not fast_to_locals.find("lasti = _PyInterpreterFrame_LASTI(frame);") < fast_to_locals.find("if (lasti < 0"): + fail("_PyFrame_FastToLocalsWithError must preserve instruction-state lookup order") +if "PyObject *name;" not in fast_to_locals or "PyObject *value;" not in fast_to_locals: + fail("_PyFrame_FastToLocalsWithError loop item locals must be declared before loop statements") +if not fast_to_locals.find("if (kind & CO_FAST_FREE && !(co->co_flags & CO_OPTIMIZED))") < fast_to_locals.find("name = PyTuple_GET_ITEM(co->co_localsplusnames, i);") < fast_to_locals.find("value = fast[i];"): + fail("_PyFrame_FastToLocalsWithError must preserve skip and local lookup order") + +public_fast_to_locals = extract_function(source, "PyFrame_FastToLocalsWithError(PyFrameObject *f)") +if not public_fast_to_locals.lstrip().startswith("int err;") or "int err =" in public_fast_to_locals: + fail("PyFrame_FastToLocalsWithError result must be declared at function entry") +ordered_public_fast = ( + "assert(!_PyFrame_IsIncomplete(f->f_frame));", + "if (f == NULL)", + "err = _PyFrame_FastToLocalsWithError(f->f_frame);", + "if (err == 0)", + "f->f_fast_as_locals = 1;", + "return err;", +) +positions = [public_fast_to_locals.find(token) for token in ordered_public_fast] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("PyFrame_FastToLocalsWithError must preserve guard and status-update order") + +locals_to_fast = extract_function(source, "_PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)") +entry_statement = locals_to_fast.find("locals = frame->f_locals;") +if "int i;" not in locals_to_fast or locals_to_fast.find("int i;") > entry_statement or "for (int i =" in locals_to_fast: + fail("_PyFrame_LocalsToFast index must be declared before execution") +if "PyObject *name;" not in locals_to_fast or "PyObject *value;" not in locals_to_fast or "PyObject *oldvalue;" not in locals_to_fast or "PyObject *cell;" not in locals_to_fast: + fail("_PyFrame_LocalsToFast loop temporaries must be declared at loop entry") +if "PyObject *name =" in locals_to_fast or "PyObject *value =" in locals_to_fast or "PyObject *oldvalue =" in locals_to_fast or "PyObject *cell =" in locals_to_fast: + fail("_PyFrame_LocalsToFast must not use late initialized declarations") +ordered_locals_to_fast = ( + "PyErr_Fetch(&error_type, &error_value, &error_traceback);", + "for (i = 0; i < co->co_nlocalsplus; i++)", + "kind = _PyLocals_GetKind(co->co_localspluskinds, i);", + "if (kind & CO_FAST_FREE && !(co->co_flags & CO_OPTIMIZED))", + "name = PyTuple_GET_ITEM(co->co_localsplusnames, i);", + "value = PyObject_GetItem(locals, name);", + "if (value == NULL)", + "oldvalue = fast[i];", + "cell = NULL;", + "Py_XDECREF(value);", + "PyErr_Restore(error_type, error_value, error_traceback);", +) +positions = [locals_to_fast.find(token) for token in ordered_locals_to_fast] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("_PyFrame_LocalsToFast must preserve error, lookup, and refcount order") + +get_code = extract_function(source, "PyFrame_GetCode(PyFrameObject *frame)") +if not get_code.lstrip().startswith("PyCodeObject *code;") or "PyCodeObject *code =" in get_code: + fail("PyFrame_GetCode local must be declared at function entry") +ordered_get_code = ( + "assert(frame != NULL);", + "assert(!_PyFrame_IsIncomplete(frame->f_frame));", + "code = frame->f_frame->f_code;", + "assert(code != NULL);", + "Py_INCREF(code);", + "return code;", +) +positions = [get_code.find(token) for token in ordered_get_code] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("PyFrame_GetCode must preserve validation and reference ownership order") + +get_back = extract_function(source, "PyFrame_GetBack(PyFrameObject *frame)") +if not get_back.lstrip().startswith("PyFrameObject *back;") or "PyFrameObject *back =" in get_back: + fail("PyFrame_GetBack local must be declared at function entry") +ordered_get_back = ( + "assert(frame != NULL);", + "assert(!_PyFrame_IsIncomplete(frame->f_frame));", + "back = frame->f_back;", + "if (back == NULL)", + "_PyInterpreterFrame *prev = frame->f_frame->previous;", + "back = _PyFrame_GetFrameObject(prev);", + "Py_XINCREF(back);", + "return back;", +) +positions = [get_back.find(token) for token in ordered_get_back] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("PyFrame_GetBack must preserve frame-chain lookup and incref order") + +get_lasti = extract_function(source, "PyFrame_GetLasti(PyFrameObject *frame)") +if not get_lasti.lstrip().startswith("int lasti;") or "int lasti =" in get_lasti: + fail("PyFrame_GetLasti local must be declared at function entry") +ordered_get_lasti = ( + "assert(!_PyFrame_IsIncomplete(frame->f_frame));", + "lasti = _PyInterpreterFrame_LASTI(frame->f_frame);", + "if (lasti < 0)", + "return lasti * sizeof(_Py_CODEUNIT);", +) +positions = [get_lasti.find(token) for token in ordered_get_lasti] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("PyFrame_GetLasti must preserve assertion and bytecode-offset order") + +get_generator = extract_function(source, "PyFrame_GetGenerator(PyFrameObject *frame)") +if not get_generator.lstrip().startswith("PyGenObject *gen;") or "PyGenObject *gen =" in get_generator: + fail("PyFrame_GetGenerator local must be declared at function entry") +ordered_get_generator = ( + "assert(!_PyFrame_IsIncomplete(frame->f_frame));", + "if (frame->f_frame->owner != FRAME_OWNED_BY_GENERATOR)", + "return NULL;", + "gen = _PyFrame_GetGenerator(frame->f_frame);", + "return Py_NewRef(gen);", +) +positions = [get_generator.find(token) for token in ordered_get_generator] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("PyFrame_GetGenerator must preserve owner guard and new-reference order") + print("frameobject C89 declarations and frame locals ownership order: PASS") diff --git a/scripts/vc6-funcobject-c89.test.py b/scripts/vc6-funcobject-c89.test.py new file mode 100644 index 0000000..c3adcd8 --- /dev/null +++ b/scripts/vc6-funcobject-c89.test.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +"""Guard C89 declarations and cleanup order in function construction.""" +import sys +from pathlib import Path +from typing import NoReturn + + +def fail(message: str) -> NoReturn: + print(message, file=sys.stderr) + raise SystemExit(1) + + +def extract_function(source: str, signature: str) -> str: + start = source.find(signature) + if start < 0: + fail(f"missing function signature: {signature}") + brace = source.find("{", start + len(signature)) + if brace < 0: + fail("missing function body") + depth = 0 + for end in range(brace, len(source)): + if source[end] == "{": + depth += 1 + elif source[end] == "}": + depth -= 1 + if depth == 0: + return source[brace + 1:end] + fail("unterminated function body") + + +source = Path(sys.argv[1]).read_text(encoding="utf-8") +body = extract_function( + source, + "PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname)", +) +declarations = ( + "PyThreadState *tstate;", + "PyCodeObject *code_obj;", + "PyObject *name;", + "PyObject *consts;", + "PyObject *doc;", + "PyObject *module;", + "PyObject *builtins;", + "PyFunctionObject *op;", +) +remaining = body.lstrip() +for declaration in declarations: + if not remaining.startswith(declaration + "\n"): + fail("PyFunction_NewWithQualName entry declaration missing or out of order: " + declaration) + remaining = remaining[len(declaration):].lstrip() + +for late_declaration in ( + "PyThreadState *tstate =", + "PyCodeObject *code_obj =", + "PyObject *name =", + "PyObject *consts =", + "PyObject *doc;\n if", + "PyObject *module =", + "PyObject *builtins =", + "PyFunctionObject *op =", +): + if late_declaration in body: + fail("PyFunction_NewWithQualName contains a late declaration: " + late_declaration) + +ordered = ( + "assert(globals != NULL);", + "assert(PyDict_Check(globals));", + "Py_INCREF(globals);", + "tstate = _PyThreadState_GET();", + "code_obj = (PyCodeObject *)code;", + "Py_INCREF(code_obj);", + "name = code_obj->co_name;", + "assert(name != NULL);", + "Py_INCREF(name);", + "if (!qualname)", + "assert(qualname != NULL);", + "Py_INCREF(qualname);", + "consts = code_obj->co_consts;", + "assert(PyTuple_Check(consts));", + "if (PyTuple_Size(consts) >= 1)", + "doc = PyTuple_GetItem(consts, 0);", + "Py_INCREF(doc);", + "module = PyDict_GetItemWithError(globals, &_Py_ID(__name__));", + "builtins = NULL;", + "if (module == NULL && _PyErr_Occurred(tstate))", + "Py_XINCREF(module);", + "builtins = _PyEval_BuiltinsFromGlobals(tstate, globals);", + "Py_INCREF(builtins);", + "op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type);", + "if (op == NULL)", + "op->func_globals = globals;", + "op->func_qualname = qualname;", + "op->func_doc = doc;", + "_PyObject_GC_TRACK(op);", + "return (PyObject *)op;", + "error:", + "Py_DECREF(globals);", + "Py_DECREF(code_obj);", + "Py_DECREF(doc);", + "Py_XDECREF(module);", + "Py_XDECREF(builtins);", + "return NULL;", +) +positions = [body.find(token) for token in ordered] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("PyFunction_NewWithQualName must preserve acquisition, failure cleanup, and ownership order") + +version = extract_function(source, "_PyFunction_GetVersionForCurrentState(PyFunctionObject *func)") +if not version.lstrip().startswith("uint32_t v;") or "uint32_t v =" in version: + fail("_PyFunction_GetVersionForCurrentState local must be declared at function entry") +ordered_version = ( + "if (func->func_version != 0)", + "if (next_func_version == 0)", + "v = next_func_version++;", + "func->func_version = v;", + "return v;", +) +positions = [version.find(token) for token in ordered_version] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("function-version allocation must preserve guard and update order") + +annotation_dict = extract_function(source, "func_get_annotation_dict(PyFunctionObject *op)") +if "if (PyTuple_CheckExact(op->func_annotations)) {\n Py_ssize_t i;" not in annotation_dict: + fail("func_get_annotation_dict loop index must be declared at the owning block entry") +if "for (Py_ssize_t i =" in annotation_dict: + fail("func_get_annotation_dict must not declare the loop index in its for initializer") +if not annotation_dict.find("assert(PyTuple_GET_SIZE(ann_tuple) % 2 == 0);") < annotation_dict.find("for (i = 0;") < annotation_dict.find("Py_SETREF(op->func_annotations, ann_dict);"): + fail("func_get_annotation_dict must preserve tuple validation, iteration, and replacement order") + +get_annotations = extract_function(source, "func_get_annotations(PyFunctionObject *op, void *Py_UNUSED(ignored))") +if not get_annotations.lstrip().startswith("PyObject *d;") or "PyObject *d =" in get_annotations: + fail("func_get_annotations result local must be declared at function entry") +ordered_get_annotations = ( + "if (op->func_annotations == NULL)", + "op->func_annotations = PyDict_New();", + "d = func_get_annotation_dict(op);", + "if (d)", + "Py_INCREF(d);", + "return d;", +) +positions = [get_annotations.find(token) for token in ordered_get_annotations] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("func_get_annotations must preserve lazy allocation and incref order") + +copy_attr = extract_function(source, "functools_copy_attr(PyObject *wrapper, PyObject *wrapped, PyObject *name)") +remaining = copy_attr.lstrip() +for declaration in ("PyObject *value;", "int res;"): + if not remaining.startswith(declaration + "\n"): + fail("functools_copy_attr entry declaration missing or out of order: " + declaration) + remaining = remaining[len(declaration):].lstrip() +if "PyObject *value =" in copy_attr or "int res =" in copy_attr: + fail("functools_copy_attr must not declare initialized locals after statements") +ordered_copy_attr = ( + "value = PyObject_GetAttr(wrapped, name);", + "if (value == NULL)", + "res = PyObject_SetAttr(wrapper, name, value);", + "Py_DECREF(value);", + "return res;", +) +positions = [copy_attr.find(token) for token in ordered_copy_attr] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("functools_copy_attr must preserve attribute lookup, error handling, and decref order") +print("funcobject C89 declarations and function-construction ownership order: PASS") diff --git a/scripts/vc6-genericaliasobject-c89.test.py b/scripts/vc6-genericaliasobject-c89.test.py new file mode 100644 index 0000000..ee7259b --- /dev/null +++ b/scripts/vc6-genericaliasobject-c89.test.py @@ -0,0 +1,344 @@ +#!/usr/bin/env python3 +"""Guard C89 declarations and writer error order in GenericAlias repr.""" +import sys +from pathlib import Path +from typing import NoReturn + + +def fail(message: str) -> NoReturn: + print(message, file=sys.stderr) + raise SystemExit(1) + + +def extract_function(source: str, signature: str) -> str: + start = source.find(signature) + if start < 0: + fail(f"missing function signature: {signature}") + brace = source.find("{", start + len(signature)) + if brace < 0: + fail("missing function body") + depth = 0 + for end in range(brace, len(source)): + if source[end] == "{": + depth += 1 + elif source[end] == "}": + depth -= 1 + if depth == 0: + return source[brace + 1:end] + fail("unterminated function body") + + +source = Path(sys.argv[1]).read_text(encoding="utf-8") +if "static PyMappingMethods ga_as_mapping = {\n 0,\n ga_getitem,\n 0,\n};" not in source: + fail("ga_as_mapping must use VC6-compatible positional fields in PyMappingMethods order") +body = extract_function(source, "ga_repr(PyObject *self)") +if "Py_ssize_t i;" not in body or "for (Py_ssize_t i =" in body: + fail("ga_repr loop index must be declared before executable statements") +if "PyObject *p;" not in body or "PyObject *p =" in body: + fail("ga_repr tuple item must be declared at the loop-block entry") +ordered = ( + "_PyUnicodeWriter_Init(&writer);", + "if (alias->starred)", + "if (ga_repr_item(&writer, alias->origin) < 0)", + "if (_PyUnicodeWriter_WriteASCIIString(&writer, \"[\", 1) < 0)", + "for (i = 0; i < len; i++)", + "if (i > 0)", + "p = PyTuple_GET_ITEM(alias->args, i);", + "if (ga_repr_item(&writer, p) < 0)", + "if (len == 0)", + "if (_PyUnicodeWriter_WriteASCIIString(&writer, \"]\", 1) < 0)", + "return _PyUnicodeWriter_Finish(&writer);", + "_PyUnicodeWriter_Dealloc(&writer);", +) +positions = [body.find(token) for token in ordered] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("ga_repr must preserve writer and tuple-iteration error order") + +tuple_index = extract_function(source, "tuple_index(PyObject *self, Py_ssize_t len, PyObject *item)") +if not tuple_index.lstrip().startswith("Py_ssize_t i;") or "for (Py_ssize_t i =" in tuple_index: + fail("tuple_index loop variable must be declared at function entry") +if not tuple_index.find("for (i = 0; i < len; i++)") < tuple_index.find("if (PyTuple_GET_ITEM(self, i) == item)") < tuple_index.find("return -1;"): + fail("tuple_index must preserve scan and not-found behavior") + +tuple_extend = extract_function(source, "tuple_extend(PyObject **dst, Py_ssize_t dstindex,\n PyObject **src, Py_ssize_t count)") +if not tuple_extend.lstrip().startswith("Py_ssize_t i;") or "for (Py_ssize_t i =" in tuple_extend: + fail("tuple_extend loop index must be declared at function entry") +if "PyObject *item;" not in tuple_extend or "PyObject *item =" in tuple_extend: + fail("tuple_extend item local must be declared at loop-block entry") +ordered_extend = ( + "assert(count >= 0);", + "if (_PyTuple_Resize(dst, PyTuple_GET_SIZE(*dst) + count - 1) != 0)", + "assert(dstindex + count <= PyTuple_GET_SIZE(*dst));", + "for (i = 0; i < count; ++i)", + "item = src[i];", + "Py_INCREF(item);", + "PyTuple_SET_ITEM(*dst, dstindex + i, item);", + "return dstindex + count;", +) +positions = [tuple_extend.find(token) for token in ordered_extend] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("tuple_extend must preserve resize, retain, and write order") + +make_parameters = extract_function(source, "_Py_make_parameters(PyObject *args)") +entry_declarations = ( + "Py_ssize_t nargs;", + "Py_ssize_t len;", + "PyObject *parameters;", + "Py_ssize_t iparam;", + "Py_ssize_t iarg;", +) +remaining = make_parameters.lstrip() +for declaration in entry_declarations: + if not remaining.startswith(declaration + "\n"): + fail("_Py_make_parameters entry declaration missing or out of order: " + declaration) + remaining = remaining[len(declaration):].lstrip() +if "for (Py_ssize_t iarg =" in make_parameters or "Py_ssize_t iparam =" in make_parameters: + fail("_Py_make_parameters must not declare function-scope locals after statements") +if "Py_ssize_t j;" not in make_parameters or "for (Py_ssize_t j =" in make_parameters: + fail("_Py_make_parameters nested loop index must be declared at its block entry") + +subs_tvars = extract_function(source, "subs_tvars(PyObject *obj, PyObject *params,\n PyObject **argitems, Py_ssize_t nargs)") +if "Py_ssize_t j;" not in subs_tvars or "Py_ssize_t i;" not in subs_tvars: + fail("subs_tvars loop indices must be declared at the tuple-processing block entry") +if "Py_ssize_t j =" in subs_tvars or "for (Py_ssize_t i =" in subs_tvars: + fail("subs_tvars must not initialize indices after statements or in for headers") +ordered_subs = ( + "Py_ssize_t nparams = PyTuple_GET_SIZE(params);", + "Py_ssize_t nsubargs = PyTuple_GET_SIZE(subparams);", + "PyObject *subargs = PyTuple_New(nsubargs);", + "if (subargs == NULL)", + "j = 0;", + "for (i = 0; i < nsubargs; ++i)", + "PyObject *arg = PyTuple_GET_ITEM(subparams, i);", + "Py_ssize_t iparam = tuple_index(params, nparams, arg);", + "if (iparam >= 0)", + "Py_INCREF(arg);", + "PyTuple_SET_ITEM(subargs, j, arg);", + "Py_DECREF(subargs);", + "Py_XDECREF(subparams);", + "return obj;", +) +positions = [subs_tvars.find(token) for token in ordered_subs] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("subs_tvars must preserve tuple allocation, substitution, and cleanup order") + +is_unpacked = extract_function(source, "_is_unpacked_typevartuple(PyObject *arg)") +if not is_unpacked.lstrip().startswith("PyObject *tmp;\n int res;") or "int res =" in is_unpacked: + fail("_is_unpacked_typevartuple locals must be declared at function entry") +ordered_is_unpacked = ( + "if (PyType_Check(arg))", + "res = _PyObject_LookupAttr(arg, &_Py_ID(__typing_is_unpacked_typevartuple__), &tmp);", + "if (res > 0)", + "res = PyObject_IsTrue(tmp);", + "Py_DECREF(tmp);", + "return res;", +) +positions = [is_unpacked.find(token) for token in ordered_is_unpacked] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("_is_unpacked_typevartuple must preserve type-check, lookup, and cleanup order") + +unpack_args = extract_function(source, "_unpack_args(PyObject *item)") +entry_unpacked = ("PyObject *newargs;", "int is_tuple;", "Py_ssize_t nitems;", "PyObject **argitems;", "Py_ssize_t i;") +remaining = unpack_args.lstrip() +for declaration in entry_unpacked: + if not remaining.startswith(declaration + "\n"): + fail("_unpack_args entry declaration missing or out of order: " + declaration) + remaining = remaining[len(declaration):].lstrip() +if "for (Py_ssize_t i =" in unpack_args: + fail("_unpack_args must not declare its index in the for initializer") +ordered_unpack = ( + "newargs = PyList_New(0);", + "if (newargs == NULL)", + "is_tuple = PyTuple_Check(item);", + "nitems = is_tuple ? PyTuple_GET_SIZE(item) : 1;", + "argitems = is_tuple ? &PyTuple_GET_ITEM(item, 0) : &item;", + "for (i = 0; i < nitems; i++)", + "item = argitems[i];", + "if (!PyType_Check(item))", + "PyList_Append(newargs, item)", + "Py_SETREF(newargs, PySequence_Tuple(newargs));", + "return newargs;", +) +positions = [unpack_args.find(token) for token in ordered_unpack] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("_unpack_args must preserve list allocation, element processing, and tuple conversion order") + +subs_parameters = extract_function(source, "_Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObject *item)") +entry_subs = ("Py_ssize_t nparams;", "Py_ssize_t i;", "int is_tuple;", "Py_ssize_t nitems;", "PyObject **argitems;", "Py_ssize_t nargs;", "PyObject *newargs;", "Py_ssize_t iarg;", "Py_ssize_t jarg;") +remaining = subs_parameters.lstrip() +for declaration in entry_subs: + if not remaining.startswith(declaration + "\n"): + fail("_Py_subs_parameters entry declaration missing or out of order: " + declaration) + remaining = remaining[len(declaration):].lstrip() +if "for (Py_ssize_t i =" in subs_parameters or "for (Py_ssize_t iarg =" in subs_parameters: + fail("_Py_subs_parameters must not declare loop indices in for initializers") +ordered_subs_parameters = ( + "nparams = PyTuple_GET_SIZE(parameters);", + "if (nparams == 0)", + "item = _unpack_args(item);", + "for (i = 0; i < nparams; i++)", + "is_tuple = PyTuple_Check(item);", + "nitems = is_tuple ? PyTuple_GET_SIZE(item) : 1;", + "argitems = is_tuple ? &PyTuple_GET_ITEM(item, 0) : &item;", + "if (nitems != nparams)", + "nargs = PyTuple_GET_SIZE(args);", + "newargs = PyTuple_New(nargs);", + "for (iarg = 0, jarg = 0; iarg < nargs; iarg++)", + "Py_DECREF(item);", + "return newargs;", +) +positions = [subs_parameters.rfind(token) if token == "Py_DECREF(item);" else subs_parameters.find(token) for token in ordered_subs_parameters] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("_Py_subs_parameters must preserve validation, unpacking, substitution, and cleanup order") + +ga_getitem = extract_function(source, "ga_getitem(PyObject *self, PyObject *item)") +if not ga_getitem.lstrip().startswith("gaobject *alias = (gaobject *)self;\n PyObject *newargs;\n PyObject *res;"): + fail("ga_getitem result locals must be declared before executable statements") +if "PyObject *newargs =" in ga_getitem or "PyObject *res =" in ga_getitem: + fail("ga_getitem must not declare initialized result locals late") +ordered_getitem = ( + "if (alias->parameters == NULL)", + "alias->parameters = _Py_make_parameters(alias->args);", + "newargs = _Py_subs_parameters(self, alias->args, alias->parameters, item);", + "if (newargs == NULL)", + "res = Py_GenericAlias(alias->origin, newargs);", + "((gaobject *)res)->starred = alias->starred;", + "Py_DECREF(newargs);", + "return res;", +) +positions = [ga_getitem.find(token) for token in ordered_getitem] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("ga_getitem must preserve lazy parameters, allocation, starred flag, and decref order") + +ga_hash = extract_function(source, "ga_hash(PyObject *self)") +if "Py_hash_t h1;" not in ga_hash or "Py_hash_t h1 =" in ga_hash: + fail("ga_hash second hash local must be declared at function entry") +ordered_hash = ( + "Py_hash_t h0 = PyObject_Hash(alias->origin);", + "if (h0 == -1)", + "h1 = PyObject_Hash(alias->args);", + "if (h1 == -1)", + "return h0 ^ h1;", +) +positions = [ga_hash.find(token) for token in ordered_hash] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("ga_hash must preserve origin-first hashing and error checks") + +ga_getattro = extract_function(source, "ga_getattro(PyObject *self, PyObject *name)") +if "const char * const *p;" not in ga_getattro or "for (const char * const *p =" in ga_getattro: + fail("ga_getattro attribute-table iterator must be declared before the loop") +ordered_getattro = ( + "if (PyUnicode_Check(name))", + "for (p = attr_exceptions; ; p++)", + "if (*p == NULL)", + "return PyObject_GetAttr(alias->origin, name);", + "if (_PyUnicode_EqualToASCIIString(name, *p))", + "return PyObject_GenericGetAttr(self, name);", +) +positions = [ga_getattro.find(token) for token in ordered_getattro] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("ga_getattro must preserve exception-name scan and origin fallback order") + +ga_richcompare = extract_function(source, "ga_richcompare(PyObject *a, PyObject *b, int op)") +if "gaobject *aa;" not in ga_richcompare or "gaobject *bb;" not in ga_richcompare or "int eq;" not in ga_richcompare: + fail("ga_richcompare locals must be declared before execution") +if "gaobject *aa =" in ga_richcompare or "gaobject *bb =" in ga_richcompare or "int eq =" in ga_richcompare: + fail("ga_richcompare must not declare locals after control flow") +ordered_richcompare = ( + "if (!_PyGenericAlias_Check(b)", + "if (op == Py_NE)", + "aa = (gaobject *)a;", + "bb = (gaobject *)b;", + "if (aa->starred != bb->starred)", + "eq = PyObject_RichCompareBool(aa->origin, bb->origin, Py_EQ);", + "if (eq < 0)", + "return PyObject_RichCompare(aa->args, bb->args, Py_EQ);", +) +positions = [ga_richcompare.find(token) for token in ordered_richcompare] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("ga_richcompare must preserve comparison dispatch and error order") +ga_dir = extract_function(source, "ga_dir(PyObject *self, PyObject *Py_UNUSED(ignored))") +if "PyObject *dir_entry;" not in ga_dir or "const char * const *p;" not in ga_dir or "int contains;" not in ga_dir: + fail("ga_dir working locals must be declared before execution") +if "PyObject *dir_entry =" in ga_dir or "for (const char * const *p =" in ga_dir or "int contains =" in ga_dir: + fail("ga_dir must not declare locals after the origin lookup") +ordered_dir = ( + "dir = PyObject_Dir(alias->origin);", + "if (dir == NULL)", + "dir_entry = NULL;", + "for (p = attr_exceptions; ; p++)", + "dir_entry = PyUnicode_FromString(*p);", + "contains = PySequence_Contains(dir, dir_entry);", + "Py_CLEAR(dir_entry);", + "return dir;", + "Py_DECREF(dir);", + "Py_XDECREF(dir_entry);", +) +positions = [ga_dir.find(token) for token in ordered_dir] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("ga_dir must preserve directory lookup, append, and cleanup order") + +ga_new = extract_function(source, "ga_new(PyTypeObject *type, PyObject *args, PyObject *kwds)") +if "PyObject *origin;" not in ga_new or "PyObject *arguments;" not in ga_new or "gaobject *self;" not in ga_new: + fail("ga_new allocation locals must be declared before validation") +if "PyObject *origin =" in ga_new or "PyObject *arguments =" in ga_new or "gaobject *self =" in ga_new: + fail("ga_new must not declare initialized locals after argument checks") +ordered_new = ( + "if (!_PyArg_NoKeywords(\"GenericAlias\", kwds))", + "if (!_PyArg_CheckPositional(\"GenericAlias\", PyTuple_GET_SIZE(args), 2, 2))", + "origin = PyTuple_GET_ITEM(args, 0);", + "arguments = PyTuple_GET_ITEM(args, 1);", + "self = (gaobject *)type->tp_alloc(type, 0);", + "if (self == NULL)", + "if (!setup_ga(self, origin, arguments))", + "Py_DECREF(self);", + "return (PyObject *)self;", +) +positions = [ga_new.find(token) for token in ordered_new] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("ga_new must preserve argument validation, allocation, and failure cleanup order") + +ga_iternext = extract_function(source, "ga_iternext(gaiterobject *gi)") +if "gaobject *alias;" not in ga_iternext or "PyObject *starred_alias;" not in ga_iternext: + fail("ga_iternext result locals must be declared before the exhausted-iterator guard") +if "gaobject *alias =" in ga_iternext or "PyObject *starred_alias =" in ga_iternext: + fail("ga_iternext must not declare initialized locals after the guard") +ordered_next = ( + "if (gi->obj == NULL)", + "alias = (gaobject *)gi->obj;", + "starred_alias = Py_GenericAlias(alias->origin, alias->args);", + "if (starred_alias == NULL)", + "((gaobject *)starred_alias)->starred = true;", + "Py_SETREF(gi->obj, NULL);", + "return starred_alias;", +) +positions = [ga_iternext.find(token) for token in ordered_next] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("ga_iternext must preserve exhausted-state, allocation, and ownership order") + +if "#if defined(MS_WINDOWS) && defined(_MSC_VER) && _MSC_VER < 1300\nstatic PyNumberMethods ga_as_number = {0};" not in source: + fail("VC6 ga_as_number initializer must use C89-compatible zero initialization") +if "PyTypeObject _Py_GenericAliasIterType = {\n PyVarObject_HEAD_INIT(&PyType_Type, 0)\n};" not in source: + fail("VC6 GenericAlias iterator type must defer designated slots to runtime setup") +if "PyTypeObject Py_GenericAliasType = {\n PyVarObject_HEAD_INIT(&PyType_Type, 0)\n};" not in source: + fail("VC6 GenericAlias type must defer designated slots to runtime setup") +if "_Py_GenericAlias_InitTypes(void)" not in source: + fail("VC6 GenericAlias type-slot initializer missing") +init_types = extract_function(source, "_Py_GenericAlias_InitTypes(void)") +for field in ("tp_name", "tp_doc", "tp_basicsize", "tp_dealloc", "tp_repr", "tp_as_number", "tp_as_mapping", "tp_hash", "tp_call", "tp_getattro", "tp_flags", "tp_traverse", "tp_richcompare", "tp_weaklistoffset", "tp_methods", "tp_members", "tp_alloc", "tp_new", "tp_free", "tp_getset", "tp_iter", "tp_vectorcall_offset"): + if "Py_GenericAliasType." + field + " =" not in init_types: + fail("VC6 GenericAlias runtime setup missing slot: " + field) +for field in ("tp_name", "tp_basicsize", "tp_iter", "tp_iternext", "tp_traverse", "tp_methods", "tp_dealloc", "tp_clear", "tp_flags"): + if "_Py_GenericAliasIterType." + field + " =" not in init_types: + fail("VC6 GenericAlias iterator runtime setup missing slot: " + field) +if "ga_as_number.nb_or = _Py_union_type_or;" not in init_types: + fail("VC6 GenericAlias number methods runtime setup missing nb_or") +if len(sys.argv) > 2: + object_source = Path(sys.argv[2]).read_text(encoding="utf-8") + if "_Py_GenericAlias_InitTypes();" not in object_source: + fail("object.c must initialize GenericAlias type slots before PyType_Ready") + type_loop = object_source.find("// All other static types (unless initialized elsewhere)") + setup_call = object_source.find("_Py_GenericAlias_InitTypes();") + if setup_call < 0 or type_loop < 0 or setup_call > type_loop: + fail("GenericAlias type-slot initialization must precede static type readiness") +print("genericaliasobject C89 declaration and writer order: PASS") diff --git a/scripts/vc6-genobject-c89.test.py b/scripts/vc6-genobject-c89.test.py new file mode 100644 index 0000000..7d996f7 --- /dev/null +++ b/scripts/vc6-genobject-c89.test.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python3 +"""Guard C89 declarations and frame traversal order in genobject.c.""" +import sys +from pathlib import Path +from typing import NoReturn + + +def fail(message: str) -> NoReturn: + print(message, file=sys.stderr) + raise SystemExit(1) + + +def extract_function(source: str, signature: str) -> str: + start = source.find(signature) + if start < 0: + fail(f"missing function signature: {signature}") + brace = source.find("{", start + len(signature)) + if brace < 0: + fail("missing function body") + depth = 0 + for end in range(brace, len(source)): + if source[end] == "{": + depth += 1 + elif source[end] == "}": + depth -= 1 + if depth == 0: + return source[brace + 1:end] + fail("unterminated function body") + + +source = Path(sys.argv[1]).read_text(encoding="utf-8") +body = extract_function(source, "gen_traverse(PyGenObject *gen, visitproc visit, void *arg)") +if "int err;" not in body or "int err =" in body: + fail("gen_traverse frame result local must be declared before traversal assertions") +ordered = ( + "if (gen->gi_frame_state < FRAME_CLEARED)", + "_PyInterpreterFrame *frame = (_PyInterpreterFrame *)(gen->gi_iframe);", + "assert(frame->frame_obj == NULL ||", + "err = _PyFrame_Traverse(frame, visit, arg);", + "if (err)", + "return exc_state_traverse(&gen->gi_exc_state, visit, arg);", +) +positions = [body.find(token) for token in ordered] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("gen_traverse must preserve frame validation, visit, and error return order") + +gen_yf = extract_function(source, "_PyGen_yf(PyGenObject *gen)") +if "_Py_CODEUNIT next;" not in gen_yf or "_Py_CODEUNIT next =" in gen_yf: + fail("_PyGen_yf instruction local must be declared at function entry") +ordered_yf = ( + "PyObject *yf = NULL;", + "if (gen->gi_frame_state < FRAME_CLEARED)", + "if (gen->gi_frame_state == FRAME_CREATED)", + "return NULL;", + "next = frame->prev_instr[1];", + "if (_PyOpcode_Deopt[_Py_OPCODE(next)] != RESUME", + "yf = _PyFrame_StackPeek(frame);", + "Py_INCREF(yf);", + "return yf;", +) +positions = [gen_yf.find(token) for token in ordered_yf] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("_PyGen_yf must preserve created-frame guard and yield-from inspection order") + +gen_throw = extract_function(source, "_gen_throw(PyGenObject *gen, int close_on_genexit,\n PyObject *typ, PyObject *val, PyObject *tb)") +if "PyFrameState state;" not in gen_throw: + fail("_gen_throw nested generator-state locals must be declared at branch entry") +recursive_branch = gen_throw.split("if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) {", 1)[1].split("} else {", 1)[0] +if "PyFrameState state;" not in recursive_branch or "PyFrameState state =" in recursive_branch: + fail("_gen_throw recursive branch state local must be declared before current-frame updates") +ordered_recursive = ( + "PyThreadState *tstate = _PyThreadState_GET();", + "_PyInterpreterFrame *prev = tstate->cframe->current_frame;", + "frame->previous = prev;", + "tstate->cframe->current_frame = frame;", + "state = gen->gi_frame_state;", + "gen->gi_frame_state = FRAME_EXECUTING;", + "ret = _gen_throw((PyGenObject *)yf, close_on_genexit,", + "gen->gi_frame_state = state;", + "tstate->cframe->current_frame = prev;", + "frame->previous = NULL;", +) +positions = [recursive_branch.find(token) for token in ordered_recursive] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("_gen_throw must preserve recursive frame/state restoration order") +iterator_branch = gen_throw.split("} else {", 1)[1] +if "PyFrameState state;" not in iterator_branch or "PyFrameState state =" in iterator_branch: + fail("_gen_throw iterator branch state local must be declared at branch entry") +ordered_iterator = ( + "PyObject *meth;", + "if (_PyObject_LookupAttr(yf, &_Py_ID(throw), &meth) < 0)", + "if (meth == NULL)", + "state = gen->gi_frame_state;", + "gen->gi_frame_state = FRAME_EXECUTING;", + "ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL);", + "gen->gi_frame_state = state;", +) +positions = [iterator_branch.find(token) for token in ordered_iterator] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("_gen_throw must preserve iterator lookup and state restoration order") + +throw_failure = gen_throw.split("if (!ret) {", 1)[1] +if "int jump;" not in throw_failure or "int jump =" in throw_failure: + fail("_gen_throw SEND jump local must be declared at its failure-branch entry") +ordered_jump = ( + "PyObject *val;", + "assert(gen->gi_frame_state < FRAME_CLEARED);", + "ret = _PyFrame_StackPop((_PyInterpreterFrame *)gen->gi_iframe);", + "assert(_PyInterpreterFrame_LASTI(frame) >= 0);", + "assert(_Py_OPCODE(frame->prev_instr[-1]) == SEND);", + "jump = _Py_OPARG(frame->prev_instr[-1]);", + "frame->prev_instr += jump - 1;", + "if (_PyGen_FetchStopIterationValue(&val) == 0)", +) +positions = [throw_failure.find(token) for token in ordered_jump] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("_gen_throw must preserve SEND validation and jump/stop-value handling order") + +gen_sizeof = extract_function(source, "gen_sizeof(PyGenObject *gen, PyObject *Py_UNUSED(ignored))") +if "PyCodeObject *code;" not in gen_sizeof or "PyCodeObject *code =" in gen_sizeof: + fail("gen_sizeof code local must be declared before size calculation") +ordered_sizeof = ( + "res = offsetof(PyGenObject, gi_iframe) + offsetof(_PyInterpreterFrame, localsplus);", + "code = gen->gi_code;", + "res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *);", + "return PyLong_FromSsize_t(res);", +) +positions = [gen_sizeof.find(token) for token in ordered_sizeof] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("gen_sizeof must preserve base-size, code-size, and result order") + +make_coro = extract_function(source, "_Py_MakeCoro(PyFunctionObject *func)") +entry_coro = ("int coro_flags;", "PyObject *coro;", "PyThreadState *tstate;", "int origin_depth;") +remaining = make_coro.lstrip() +for declaration in entry_coro: + if not remaining.startswith(declaration + "\n"): + fail("_Py_MakeCoro entry declaration missing or out of order: " + declaration) + remaining = remaining[len(declaration):].lstrip() +if "int coro_flags =" in make_coro or "PyObject *coro =" in make_coro or "PyThreadState *tstate =" in make_coro or "int origin_depth =" in make_coro: + fail("_Py_MakeCoro must not declare initialized locals after control flow") +if "PyObject *cr_origin;" not in make_coro or "PyObject *cr_origin =" in make_coro: + fail("_Py_MakeCoro origin local must be declared at the fallback branch entry") +ordered_coro = ( + "coro_flags = ((PyCodeObject *)func->func_code)->co_flags &", + "if (coro_flags == CO_GENERATOR)", + "if (coro_flags == CO_ASYNC_GENERATOR)", + "assert (coro_flags == CO_COROUTINE);", + "coro = make_gen(&PyCoro_Type, func);", + "tstate = _PyThreadState_GET();", + "origin_depth = tstate->coroutine_origin_tracking_depth;", + "if (origin_depth == 0)", + "cr_origin = compute_cr_origin(origin_depth, _PyEval_GetFrame()->previous);", + "return coro;", +) +positions = [make_coro.find(token) for token in ordered_coro] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("_Py_MakeCoro must preserve coroutine selection, creation, and origin tracking order") + +compute_origin_start = source.rfind("static PyObject *\ncompute_cr_origin(int origin_depth, _PyInterpreterFrame *current_frame)") +if compute_origin_start < 0: + fail("missing compute_cr_origin definition") +compute_origin = extract_function(source[compute_origin_start:], "compute_cr_origin(int origin_depth, _PyInterpreterFrame *current_frame)") +for declaration in ("_PyInterpreterFrame *frame;", "int frame_count;", "PyObject *cr_origin;", "int i;"): + if declaration not in compute_origin: + fail("compute_cr_origin local must be declared before frame traversal: " + declaration) +if "int frame_count =" in compute_origin or "PyObject *cr_origin =" in compute_origin or "for (int i =" in compute_origin: + fail("compute_cr_origin must not declare initialized locals after traversal starts") +ordered_origin = ( + "frame = current_frame;", + "frame_count = 0;", + "for (; frame && frame_count < origin_depth; ++frame_count)", + "cr_origin = PyTuple_New(frame_count);", + "if (cr_origin == NULL)", + "frame = current_frame;\n for (i = 0; i < frame_count; ++i)", + "PyTuple_SET_ITEM(cr_origin, i, frameinfo);", + "return cr_origin;", +) +positions = [compute_origin.find(token) for token in ordered_origin] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("compute_cr_origin must preserve frame counting, tuple creation, and collection order") + +pycoro_new = extract_function(source, "PyCoro_New(PyFrameObject *f, PyObject *name, PyObject *qualname)") +for declaration in ("PyObject *coro;", "PyThreadState *tstate;", "int origin_depth;"): + if declaration not in pycoro_new: + fail("PyCoro_New local must be declared at function entry: " + declaration) +if "PyObject *coro =" in pycoro_new or "PyThreadState *tstate =" in pycoro_new or "int origin_depth =" in pycoro_new: + fail("PyCoro_New must not declare initialized locals after allocation") +if "PyObject *cr_origin;" not in pycoro_new or "PyObject *cr_origin =" in pycoro_new: + fail("PyCoro_New origin local must be declared at the fallback branch entry") +ordered_pycoro = ( + "coro = gen_new_with_qualname(&PyCoro_Type, f, name, qualname);", + "if (!coro)", + "tstate = _PyThreadState_GET();", + "origin_depth = tstate->coroutine_origin_tracking_depth;", + "if (origin_depth == 0)", + "cr_origin = compute_cr_origin(origin_depth, _PyEval_GetFrame());", + "return coro;", +) +positions = [pycoro_new.find(token) for token in ordered_pycoro] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("PyCoro_New must preserve allocation and origin-tracking order") + +gen_new_qualname = extract_function(source, "gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f,\n PyObject *name, PyObject *qualname)") +if "_PyInterpreterFrame *frame;" not in gen_new_qualname or "_PyInterpreterFrame *frame =" in gen_new_qualname: + fail("gen_new_with_qualname frame local must be declared before allocation checks") +ordered_gen_new = ( + "PyCodeObject *code = f->f_frame->f_code;", + "int size = code->co_nlocalsplus + code->co_stacksize;", + "PyGenObject *gen = PyObject_GC_NewVar(PyGenObject, type, size);", + "if (gen == NULL)", + "assert(f->f_frame->frame_obj == NULL);", + "assert(f->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT);", + "frame = (_PyInterpreterFrame *)gen->gi_iframe;", + "_PyFrame_Copy((_PyInterpreterFrame *)f->_f_frame_data, frame);", + "gen->gi_frame_state = FRAME_CREATED;", + "return (PyObject *)gen;", +) +positions = [gen_new_qualname.find(token) for token in ordered_gen_new] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("gen_new_with_qualname must preserve frame validation, copy, and ownership order") + +asend_dealloc = extract_function(source, "async_gen_asend_dealloc(PyAsyncGenASend *o)") +if not asend_dealloc.lstrip().startswith("#if _PyAsyncGen_MAXFREELIST > 0\n struct _Py_async_gen_state *state;\n#endif"): + fail("async_gen_asend_dealloc state local must be declared at function entry under its feature guard") +if "struct _Py_async_gen_state *state =" in asend_dealloc: + fail("async_gen_asend_dealloc must not declare state after object cleanup") +ordered_asend_dealloc = ( + "_PyObject_GC_UNTRACK((PyObject *)o);", + "Py_CLEAR(o->ags_gen);", + "Py_CLEAR(o->ags_sendval);", + "state = get_async_gen_state();", + "assert(state->asend_numfree != -1);", + "if (state->asend_numfree < _PyAsyncGen_MAXFREELIST)", + "state->asend_freelist[state->asend_numfree++] = o;", + "PyObject_GC_Del(o);", +) +positions = [asend_dealloc.find(token) for token in ordered_asend_dealloc] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("async_gen_asend_dealloc must preserve untracking, clearing, freelist, and free order") + +wrapped_dealloc = extract_function(source, "async_gen_wrapped_val_dealloc(_PyAsyncGenWrappedValue *o)") +if not wrapped_dealloc.lstrip().startswith("#if _PyAsyncGen_MAXFREELIST > 0\n struct _Py_async_gen_state *state;\n#endif"): + fail("wrapped-value freelist state must be declared at function entry under its feature guard") +ordered_wrapped = ( + "_PyObject_GC_UNTRACK((PyObject *)o);", + "Py_CLEAR(o->agw_val);", + "state = get_async_gen_state();", + "assert(state->value_numfree != -1);", + "if (state->value_numfree < _PyAsyncGen_MAXFREELIST)", + "state->value_freelist[state->value_numfree++] = o;", + "PyObject_GC_Del(o);", +) +positions = [wrapped_dealloc.find(token) for token in ordered_wrapped] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("wrapped-value deallocator must preserve untrack, clear, freelist, and free order") + +wrapped_new = extract_function(source, "_PyAsyncGenValueWrapperNew(PyObject *val)") +state_decl = "#if _PyAsyncGen_MAXFREELIST > 0\n struct _Py_async_gen_state *state;\n#endif" +if state_decl not in wrapped_new or wrapped_new.find(state_decl) > wrapped_new.find("assert(val);"): + fail("_PyAsyncGenValueWrapperNew freelist state must be declared before executable statements") +if "struct _Py_async_gen_state *state =" in wrapped_new: + fail("_PyAsyncGenValueWrapperNew must not initialize state in a late declaration") +ordered_wrapped_new = ( + "assert(val);", + "state = get_async_gen_state();", + "assert(state->value_numfree != -1);", + "if (state->value_numfree)", + "o = PyObject_GC_New(_PyAsyncGenWrappedValue,", + "o->agw_val = val;", + "Py_INCREF(val);", + "_PyObject_GC_TRACK((PyObject*)o);", + "return (PyObject*)o;", +) +positions = [wrapped_new.find(token) for token in ordered_wrapped_new] +if any(position < 0 for position in positions) or positions != sorted(positions): + fail("_PyAsyncGenValueWrapperNew must preserve freelist, allocation, and value ownership order") +print("genobject C89 declaration and generator ownership order: PASS") diff --git a/scripts/vc6-interpreteridobject-c89.test.py b/scripts/vc6-interpreteridobject-c89.test.py new file mode 100644 index 0000000..061071b --- /dev/null +++ b/scripts/vc6-interpreteridobject-c89.test.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +"""Guard C89 declarations and ownership/order invariants in interpreteridobject.c.""" +import sys +from pathlib import Path +from typing import NoReturn + + +def fail(message: str) -> NoReturn: + print(message, file=sys.stderr) + raise SystemExit(1) + + +def extract_function(source: str, signature: str) -> str: + start = source.find(signature) + if start < 0: + fail(f"missing function signature: {signature}") + brace = source.find("{", start + len(signature)) + if brace < 0: + fail("missing function body") + depth = 0 + for end in range(brace, len(source)): + if source[end] == "{": + depth += 1 + elif source[end] == "}": + depth -= 1 + if depth == 0: + return source[brace + 1:end] + fail("unterminated function body") + + +def ordered(body: str, tokens: tuple[str, ...], message: str) -> None: + positions = [body.find(token) for token in tokens] + if any(position < 0 for position in positions) or positions != sorted(positions): + fail(message) + + +source = Path(sys.argv[1]).read_text(encoding="utf-8") + +newinterpid = extract_function(source, "newinterpid(PyTypeObject *cls, int64_t id, int force)") +for declaration in ("PyInterpreterState *interp;", "interpid *self;"): + if declaration not in newinterpid: + fail("newinterpid local must be declared at function entry: " + declaration) +if "PyInterpreterState *interp =" in newinterpid or "interpid *self =" in newinterpid: + fail("newinterpid must not initialize locals in late declarations") +ordered(newinterpid, ( + "interp = _PyInterpreterState_LookUpID(id);", + "if (interp == NULL)", + "if (interp != NULL)", + "_PyInterpreterState_IDIncref(interp)", + "self = PyObject_New(interpid, cls);", + "if (self == NULL)", + "_PyInterpreterState_IDDecref(interp);", + "self->id = id;", + "return self;", +), "newinterpid must preserve lookup, incref, allocation, rollback, and assignment order") + +dealloc = extract_function(source, "interpid_dealloc(PyObject *v)") +for declaration in ("int64_t id;", "PyInterpreterState *interp;"): + if declaration not in dealloc: + fail("interpid_dealloc local must be declared at function entry: " + declaration) +ordered(dealloc, ( + "id = ((interpid *)v)->id;", + "interp = _PyInterpreterState_LookUpID(id);", + "_PyInterpreterState_IDDecref(interp);", + "PyErr_Clear();", + "Py_TYPE(v)->tp_free(v);", +), "interpid_dealloc must preserve ID lookup, reference release, and free order") + +hash_body = extract_function(source, "interpid_hash(PyObject *self)") +if "Py_hash_t hash;" not in hash_body or "Py_hash_t hash =" in hash_body: + fail("interpid_hash hash result must be declared before object hashing") +ordered(hash_body, ( + "PyObject *obj = PyLong_FromLongLong(id->id);", + "if (obj == NULL)", + "hash = PyObject_Hash(obj);", + "Py_DECREF(obj);", + "return hash;", +), "interpid_hash must preserve allocation, hashing, decref, and return order") + +richcompare = extract_function(source, "interpid_richcompare(PyObject *self, PyObject *other, int op)") +for declaration in ("interpid *id;", "int equal;"): + if declaration not in richcompare: + fail("interpid_richcompare local must be declared at function entry: " + declaration) +ordered(richcompare, ( + "if (op != Py_EQ && op != Py_NE)", + "if (!PyObject_TypeCheck(self, &_PyInterpreterID_Type))", + "id = (interpid *)self;", + "if (PyObject_TypeCheck(other, &_PyInterpreterID_Type))", + "interpid *otherid = (interpid *)other;", + "equal = (id->id == otherid->id);", + "else if (PyLong_CheckExact(other))", + "int overflow;", + "PY_LONG_LONG otherid;", + "otherid = PyLong_AsLongLongAndOverflow(other, &overflow);", + "equal = !overflow && (otherid >= 0) && (id->id == otherid);", + "else if (PyNumber_Check(other))", + "pyid = PyLong_FromLongLong(id->id);", + "res = PyObject_RichCompare(pyid, other, op);", + "Py_DECREF(pyid);", + "return res;", + "if ((op == Py_EQ && equal) || (op == Py_NE && !equal))", + "Py_RETURN_TRUE;", + "Py_RETURN_FALSE;", +), "interpid_richcompare must preserve dispatch, overflow, and reference ordering") + +get_id = extract_function(source, "_PyInterpreterState_GetIDObject(PyInterpreterState *interp)") +if "int64_t id;" not in get_id or "int64_t id =" in get_id: + fail("_PyInterpreterState_GetIDObject ID local must be declared at function entry") +ordered(get_id, ( + "if (_PyInterpreterState_IDInitref(interp) != 0)", + "id = PyInterpreterState_GetID(interp);", + "if (id < 0)", + "return (PyObject *)newinterpid(&_PyInterpreterID_Type, id, 0);", +), "_PyInterpreterState_GetIDObject must preserve initref and ID validation order") + +print("interpreteridobject C89 declarations and ownership order: PASS") diff --git a/scripts/vc6-iterobject-c89.test.py b/scripts/vc6-iterobject-c89.test.py new file mode 100644 index 0000000..70716aa --- /dev/null +++ b/scripts/vc6-iterobject-c89.test.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +"""Guard C89 declaration placement and reference order in iterobject.c.""" +import sys +from pathlib import Path +from typing import NoReturn + + +def fail(message: str) -> NoReturn: + print(message, file=sys.stderr) + raise SystemExit(1) + + +def extract_function(source: str, signature: str) -> str: + start = source.find(signature) + if start < 0: + fail(f"missing function signature: {signature}") + brace = source.find("{", start + len(signature)) + if brace < 0: + fail("missing function body") + depth = 0 + for end in range(brace, len(source)): + if source[end] == "{": + depth += 1 + elif source[end] == "}": + depth -= 1 + if depth == 0: + return source[brace + 1:end] + fail("unterminated function body") + + +def ordered(body: str, tokens: tuple[str, ...], message: str) -> None: + positions = [body.find(token) for token in tokens] + if any(position < 0 for position in positions) or positions != sorted(positions): + fail(message) + + +source = Path(sys.argv[1]).read_text(encoding="utf-8") + +getiter = extract_function(source, "anextawaitable_getiter(anextawaitableobject *obj)") +for declaration in ("PyObject *awaitable;", "unaryfunc getter;", "PyObject *new_awaitable;"): + if declaration not in getiter: + fail("anextawaitable_getiter local must be declared before use: " + declaration) +for late in ("PyObject *awaitable =", "unaryfunc getter =", "PyObject *new_awaitable ="): + if late in getiter: + fail("anextawaitable_getiter must not initialize a local in a late declaration: " + late) +branch = getiter.split("if (Py_TYPE(awaitable)->tp_iternext == NULL) {", 1)[1] +if branch.find("unaryfunc getter;") > branch.find("getter = Py_TYPE(awaitable)->tp_as_async->am_await;"): + fail("anextawaitable_getiter branch locals must be declared before statements") +if branch.find("PyObject *new_awaitable;") > branch.find("getter = Py_TYPE(awaitable)->tp_as_async->am_await;"): + fail("anextawaitable_getiter branch locals must be declared before statements") +ordered(getiter, ( + "assert(obj->wrapped != NULL);", + "awaitable = _PyCoro_GetAwaitableIter(obj->wrapped);", + "if (awaitable == NULL)", + "if (Py_TYPE(awaitable)->tp_iternext == NULL)", + "getter = Py_TYPE(awaitable)->tp_as_async->am_await;", + "new_awaitable = getter(awaitable);", + "if (new_awaitable == NULL)", + "Py_SETREF(awaitable, new_awaitable);", + "if (!PyIter_Check(awaitable))", + "return awaitable;", +), "anextawaitable_getiter must preserve awaitable conversion and cleanup order") + +iternext = extract_function(source, "anextawaitable_iternext(anextawaitableobject *obj)") +for declaration in ("PyObject *awaitable;", "PyObject *result;"): + if declaration not in iternext: + fail("anextawaitable_iternext local must be declared at function entry: " + declaration) +if "PyObject *result =" in iternext: + fail("anextawaitable_iternext result must not be declared after the NULL guard") +ordered(iternext, ( + "awaitable = anextawaitable_getiter(obj);", + "if (awaitable == NULL)", + "result = (*Py_TYPE(awaitable)->tp_iternext)(awaitable);", + "Py_DECREF(awaitable);", + "if (result != NULL)", + "if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration))", + "_PyGen_SetStopIterationValue(obj->default_value);", + "\n return NULL;", +), "anextawaitable_iternext must preserve call, decref, and StopAsyncIteration handling order") + +proxy = extract_function(source, "anextawaitable_proxy(anextawaitableobject *obj, char *meth, PyObject *arg)") +for declaration in ("PyObject *awaitable;", "PyObject *ret;"): + if declaration not in proxy: + fail("anextawaitable_proxy local must be declared at function entry: " + declaration) +if "PyObject *ret =" in proxy: + fail("anextawaitable_proxy result must not be declared after the NULL guard") +ordered(proxy, ( + "awaitable = anextawaitable_getiter(obj);", + "if (awaitable == NULL)", + "ret = PyObject_CallMethod(awaitable, meth, \"O\", arg);", + "Py_DECREF(awaitable);", + "if (ret != NULL)", + "if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration))", + "_PyGen_SetStopIterationValue(obj->default_value);", + "\n return NULL;", +), "anextawaitable_proxy must preserve call, decref, and exception handling order") + +print("iterobject anextawaitable C89 declarations and reference order: PASS") diff --git a/scripts/vc6-listobject-c89.test.py b/scripts/vc6-listobject-c89.test.py new file mode 100644 index 0000000..a4b0ce7 --- /dev/null +++ b/scripts/vc6-listobject-c89.test.py @@ -0,0 +1,306 @@ +#!/usr/bin/env python3 +"""Guard C89 list-allocation declarations and ownership order in listobject.c.""" +import sys +from pathlib import Path +from typing import NoReturn + + +def fail(message: str) -> NoReturn: + print(message, file=sys.stderr) + raise SystemExit(1) + + +def extract_function(source: str, signature: str) -> str: + start = source.find(signature) + if start < 0: + fail(f"missing function signature: {signature}") + brace = source.find("{", start + len(signature)) + if brace < 0: + fail("missing function body") + depth = 0 + for end in range(brace, len(source)): + if source[end] == "{": + depth += 1 + elif source[end] == "}": + depth -= 1 + if depth == 0: + return source[brace + 1:end] + fail("unterminated function body") + + +def ordered(body: str, tokens: tuple[str, ...], message: str) -> None: + positions = [body.find(token) for token in tokens] + if any(position < 0 for position in positions) or positions != sorted(positions): + fail(message) + + +source = Path(sys.argv[1]).read_text(encoding="utf-8") + +preallocate = extract_function(source, "list_preallocate_exact(PyListObject *self, Py_ssize_t size)") +if "PyObject **items;" not in preallocate or "PyObject **items =" in preallocate: + fail("list_preallocate_exact items local must be declared before size adjustment") +ordered(preallocate, ( + "assert(self->ob_item == NULL);", + "assert(size > 0);", + "size = (size + 1) & ~(size_t)1;", + "items = PyMem_New(PyObject*, size);", + "if (items == NULL)", + "PyErr_NoMemory();", + "self->ob_item = items;", + "self->allocated = size;", + "return 0;", +), "list_preallocate_exact must preserve validation, rounding, allocation, and assignment order") + +list_new = extract_function(source, "PyList_New(Py_ssize_t size)") +entry = "PyListObject *op;\n#if PyList_MAXFREELIST > 0\n struct _Py_list_state *state;\n#endif" +if entry not in list_new: + fail("PyList_New freelist state must be declared at function entry under its feature guard") +if "struct _Py_list_state *state =" in list_new: + fail("PyList_New must not initialize state in a late declaration") +ordered(list_new, ( + "if (size < 0)", + "PyErr_BadInternalCall();", + "return NULL;", + "state = get_list_state();", + "assert(state->numfree != -1);", + "if (PyList_MAXFREELIST && state->numfree)", + "state->numfree--;", + "op = state->free_list[state->numfree];", + "_Py_NewReference((PyObject *)op);", + "op = PyObject_GC_New(PyListObject, &PyList_Type);", + "if (op == NULL)", + "if (size <= 0)", + "op->ob_item = (PyObject **) PyMem_Calloc(size, sizeof(PyObject *));", + "Py_DECREF(op);", + "Py_SET_SIZE(op, size);", + "op->allocated = size;", + "_PyObject_GC_TRACK(op);", + "return (PyObject *) op;", +), "PyList_New must preserve validation, freelist, allocation, cleanup, and tracking order") + +new_prealloc = extract_function(source, "list_new_prealloc(Py_ssize_t size)") +if "PyListObject *op;" not in new_prealloc or "PyListObject *op =" in new_prealloc: + fail("list_new_prealloc object local must be declared before validation") +ordered(new_prealloc, ( + "assert(size > 0);", + "op = (PyListObject *) PyList_New(0);", + "if (op == NULL)", + "assert(op->ob_item == NULL);", + "op->ob_item = PyMem_New(PyObject *, size);", + "if (op->ob_item == NULL)", + "Py_DECREF(op);", + "op->allocated = size;", + "return (PyObject *) op;", +), "list_new_prealloc must preserve validation, allocation, cleanup, and assignment order") + +list_dealloc = extract_function(source, "list_dealloc(PyListObject *op)") +entry = "Py_ssize_t i;\n#if PyList_MAXFREELIST > 0\n struct _Py_list_state *state;\n#endif" +if entry not in list_dealloc: + fail("list_dealloc freelist state must be declared before trashcan/deallocation operations") +if "struct _Py_list_state *state =" in list_dealloc: + fail("list_dealloc must not initialize state after item cleanup") +ordered(list_dealloc, ( + "Py_ssize_t i;", + "PyObject_GC_UnTrack(op);", + "Py_TRASHCAN_BEGIN(op, list_dealloc)", + "i = Py_SIZE(op);", + "Py_XDECREF(op->ob_item[i]);", + "PyMem_Free(op->ob_item);", + "state = get_list_state();", + "assert(state->numfree != -1);", + "if (state->numfree < PyList_MAXFREELIST && PyList_CheckExact(op))", + "state->free_list[state->numfree++] = op;", + "Py_TYPE(op)->tp_free((PyObject *)op);", + "Py_TRASHCAN_END", +), "list_dealloc must preserve untrack, item cleanup, freelist, and free order") + +list_repeat = extract_function(source, "list_repeat(PyListObject *a, Py_ssize_t n)") +for declaration in ("PyObject **dest;", "PyObject **dest_end;", "PyObject *elem;", "PyObject **src;", "PyObject **src_end;"): + if declaration not in list_repeat: + fail("list_repeat local must be declared at function entry: " + declaration) +for late in ("PyObject **dest =", "PyObject **dest_end =", "PyObject *elem =", "PyObject **src =", "PyObject **src_end ="): + if late in list_repeat: + fail("list_repeat must not initialize a local in a late declaration: " + late) +ordered(list_repeat, ( + "if (n < 0)", + "if (n > 0 && Py_SIZE(a) > PY_SSIZE_T_MAX / n)", + "size = Py_SIZE(a) * n;", + "if (size == 0)", + "np = (PyListObject *) list_new_prealloc(size);", + "if (np == NULL)", + "dest = np->ob_item;", + "dest_end = dest + size;", + "if (Py_SIZE(a) == 1)", + "elem = a->ob_item[0];", + "Py_SET_REFCNT(elem, Py_REFCNT(elem) + n);", + "src = a->ob_item;", + "src_end = src + Py_SIZE(a);", + "Py_SET_REFCNT(*src, Py_REFCNT(*src) + n);", + "src = np->ob_item;", + "Py_SET_SIZE(np, size);", + "return (PyObject *) np;", +), "list_repeat must preserve size, pointer, and reference-count update order") + +powerloop = extract_function(source, "powerloop(Py_ssize_t s1, Py_ssize_t n1, Py_ssize_t n2, Py_ssize_t n)") +for declaration in ("Py_ssize_t a;", "Py_ssize_t b;"): + if declaration not in powerloop: + fail("powerloop midpoint local must be declared at function entry: " + declaration) +if "Py_ssize_t a =" in powerloop or "Py_ssize_t b =" in powerloop: + fail("powerloop midpoint locals must not be declared after assertions") +ordered(powerloop, ( + "int result = 0;", + "assert(s1 >= 0);", + "assert(n1 > 0 && n2 > 0);", + "assert(s1 + n1 + n2 <= n);", + "a = 2 * s1 + n1;", + "b = a + n1 + n2;", + "for (;;) {", + "++result;", + "return result;", +), "powerloop must preserve midpoint calculations and iteration order") + +new_run = extract_function(source, "found_new_run(MergeState *ms, Py_ssize_t n2)") +branch = new_run.split("if (ms->n) {", 1)[1].split("\n }", 1)[0] +for declaration in ("struct s_slice *p;", "Py_ssize_t s1;", "Py_ssize_t n1;", "int power;"): + if declaration not in branch: + fail("found_new_run locals must be declared at the run branch entry: " + declaration) +for late in ("struct s_slice *p =", "Py_ssize_t s1 =", "Py_ssize_t n1 =", "int power ="): + if late in branch: + fail("found_new_run must not initialize a local after the branch assertion: " + late) +ordered(branch, ( + "assert(ms->n > 0);", + "p = ms->pending;", + "s1 = p[ms->n - 1].base.keys - ms->basekeys;", + "n1 = p[ms->n - 1].len;", + "power = powerloop(s1, n1, n2, ms->listlen);", + "while (ms->n > 1 && p[ms->n - 2].power > power)", + "p[ms->n - 1].power = power;", +), "found_new_run must preserve assertion, run lookup, power computation, and merge order") + +sort_impl = extract_function(source, "list_sort_impl(PyListObject *self, PyObject *keyfunc, int reverse)") +if "PyObject *key;" not in sort_impl or "PyObject *key =" in sort_impl: + fail("list_sort_impl key local must be declared before sort-time checks") +ordered(sort_impl, ( + "if (keyfunc == Py_None)", + "saved_ob_size = Py_SIZE(self);", + "Py_SET_SIZE(self, 0);", + "if (keyfunc == NULL)", + "if (saved_ob_size > 1)", + "for (i=0; i < saved_ob_size; i++)", + "key = (keys_are_in_tuples ?", + "if (!Py_IS_TYPE(key, key_type))", +), "list_sort_impl must preserve key preparation and list mutation order") + +for name, signature in ( + ("list_index_impl", "list_index_impl(PyListObject *self, PyObject *value, Py_ssize_t start,\n Py_ssize_t stop)"), + ("list_count", "list_count(PyListObject *self, PyObject *value)"), + ("list_remove", "list_remove(PyListObject *self, PyObject *value)"), +): + body = extract_function(source, signature) + loop = body.split("for (i =", 1)[1].split("\n }", 1)[0] + if "int cmp;" not in loop or "int cmp =" in loop: + fail(name + " comparison result must be declared before incref/comparison") + if "cmp = PyObject_RichCompareBool(obj, value, Py_EQ);" not in loop: + fail(name + " must assign comparison result after acquiring obj reference") + ordered(loop, ( + "PyObject *obj = self->ob_item[i];", + "int cmp;", + "Py_INCREF(obj);", + "cmp = PyObject_RichCompareBool(obj, value, Py_EQ);", + "Py_DECREF(obj);", + ), name + " must preserve item reference and comparison ordering") + +richcompare = extract_function(source, "list_richcompare(PyObject *v, PyObject *w, int op)") +loop = richcompare.split("for (i =", 1)[1].split("\n }", 1)[0] +for declaration in ("PyObject *vitem;", "PyObject *witem;", "int k;"): + if declaration not in loop: + fail("list_richcompare loop local must be declared before item comparisons: " + declaration) +ordered(loop, ( + "vitem = vl->ob_item[i];", + "witem = wl->ob_item[i];", + "if (vitem == witem)", + "Py_INCREF(vitem);", + "Py_INCREF(witem);", + "k = PyObject_RichCompareBool(vitem, witem, Py_EQ);", + "Py_DECREF(vitem);", + "Py_DECREF(witem);", +), "list_richcompare must preserve item comparison/reference ordering") +if "PyObject *vitem;" not in richcompare or "PyObject *result;" not in richcompare: + fail("list_richcompare final comparison locals must be declared in its final block") + +vectorcall = extract_function(source, "list_vectorcall(PyObject *type, PyObject * const*args,\n size_t nargsf, PyObject *kwnames)") +for declaration in ("Py_ssize_t nargs;", "PyObject *list;"): + if declaration not in vectorcall: + fail("list_vectorcall local must be declared at function entry: " + declaration) +ordered(vectorcall, ( + "if (!_PyArg_NoKwnames(\"list\", kwnames))", + "nargs = PyVectorcall_NARGS(nargsf);", + "if (!_PyArg_CheckPositional(\"list\", nargs, 0, 1))", + "list = PyType_GenericAlloc(_PyType_CAST(type), 0);", + "if (list == NULL)", + "if (nargs)", + "return list;", +), "list_vectorcall must preserve argument checks and allocation order") + +ass_subscript = extract_function(source, "list_ass_subscript(PyListObject* self, PyObject* item, PyObject* value)") +assign_branch = ass_subscript.split("else {\n /* assign slice */", 1)[1] +if "Py_ssize_t slicelength;" not in assign_branch or "Py_ssize_t slicelength =" in assign_branch: + fail("extended-slice assignment length must be declared before sequence lookup") +ordered(assign_branch, ( + "Py_ssize_t slicelength;", + "if (self == (PyListObject*)value)", + "seq = PySequence_Fast(value,", + "if (!seq)", + "slicelength = adjust_slice_indexes(self, &start, &stop,", + "if (step == 1)", + "if (PySequence_Fast_GET_SIZE(seq) != slicelength)", +), "extended-slice assignment must preserve input validation and sequence handling order") + +type_start = source.find("PyTypeObject PyList_Type = {") +if type_start < 0: + fail("missing PyList_Type initializer") +type_end = source.find("\n};", type_start) +if type_end < 0: + fail("unterminated PyList_Type initializer") +type_init = source[type_start:type_end] +if ".tp_vectorcall" in type_init: + fail("PyList_Type vectorcall must use a VC6-compatible positional initializer") +ordered(type_init, ( + "PyObject_GC_Del,", + "0, /* tp_is_gc */", + "0, /* tp_bases */", + "0, /* tp_mro */", + "0, /* tp_cache */", + "0, /* tp_subclasses */", + "0, /* tp_weaklist */", + "0, /* tp_del */", + "0, /* tp_version_tag */", + "0, /* tp_finalize */", + "list_vectorcall, /* tp_vectorcall */", +), "PyList_Type positional tail must match PyTypeObject field order") + +reduce_signature = "listiter_reduce_general(void *_it, int forward)" +reduce_start = source.rfind(reduce_signature) +if reduce_start < 0: + fail("missing listiter_reduce_general definition") +reduce_general = extract_function(source[reduce_start:], reduce_signature) +for declaration in ("listiterobject *it;", "listreviterobject *rev_it;"): + if declaration not in reduce_general: + fail("listiter_reduce_general iterator local must be declared at function entry: " + declaration) +ordered(reduce_general, ( + "if (forward)", + "iter = _PyEval_GetBuiltin(&_Py_ID(iter));", + "if (!iter)", + "it = (listiterobject *)_it;", + "} else {", + "iter = _PyEval_GetBuiltin(&_Py_ID(reversed));", + "rev_it = (listreviterobject *)_it;", +), "listiter_reduce_general must preserve builtin lookup before iterator access") +forward_guard = reduce_general.find("if (!iter)") +reverse_guard = reduce_general.rfind("if (!iter)") +if not (forward_guard < reduce_general.find("it = (listiterobject *)_it;") < + reduce_general.find("} else {") < + reduce_general.find("iter = _PyEval_GetBuiltin(&_Py_ID(reversed));") < + reverse_guard < reduce_general.find("rev_it = (listreviterobject *)_it;")): + fail("listiter_reduce_general must check both builtin lookups before touching iterators") +print("listobject C89 declarations and allocation order: PASS") diff --git a/scripts/vc6-longobject-c89.test.py b/scripts/vc6-longobject-c89.test.py new file mode 100644 index 0000000..a7a6813 --- /dev/null +++ b/scripts/vc6-longobject-c89.test.py @@ -0,0 +1,339 @@ +#!/usr/bin/env python3 +"""Focused VC6 C89 regression checks for Objects/longobject.c.""" +import pathlib +import re +import sys + +source = pathlib.Path(sys.argv[1]).read_text(encoding="utf-8") +match = re.search(r"static PyObject \*\s*get_small_int\(sdigit ival\)\s*\{(?P.*?)\n\}", source, re.S) +assert match, "get_small_int definition not found" +body = match.group("body") +assert re.search(r"\A\s*PyObject \*v;\s*assert\(IS_SMALL_INT\(ival\)\);\s*v = \(PyObject \*\)&_PyLong_SMALL_INTS\[_PY_NSMALLNEGINTS \+ ival\];\s*Py_INCREF\(v\);\s*return v;", body), "get_small_int must declare v at function entry and retain assertion, lookup, incref, and return order" +print("longobject get_small_int C89 declaration and evaluation order: PASS") + +match = re.search(r"PyLongObject \*\s*_PyLong_New\(Py_ssize_t size\)\s*\{(?P.*?)\n\}", source, re.S) +assert match, "_PyLong_New definition not found" +body = re.sub(r"/\*.*?\*/", "", match.group("body"), flags=re.S) +expected = """ + PyLongObject *result; + Py_ssize_t ndigits; + if (size > (Py_ssize_t)MAX_LONG_DIGITS) { + PyErr_SetString(PyExc_OverflowError, + "too many digits in integer"); + return NULL; + } + ndigits = size ? size : 1; + result = PyObject_Malloc(offsetof(PyLongObject, ob_digit) + + ndigits*sizeof(digit)); + if (!result) { + PyErr_NoMemory(); + return NULL; + } + _PyObject_InitVar((PyVarObject*)result, &PyLong_Type, size); + return result; +""" +assert body.split() == expected.split(), "_PyLong_New must declare ndigits at function entry and retain overflow check, digit count, allocation, error, initialization, and return order" +print("longobject _PyLong_New C89 declaration and allocation/error order: PASS") + +match = re.search(r"static PyObject \*\s*_PyLong_FromMedium\(sdigit x\)\s*\{(?P.*?)\n\}", source, re.S) +assert match, "_PyLong_FromMedium definition not found" +body = re.sub(r"/\*.*?\*/", "", match.group("body"), flags=re.S) +expected = """ + PyLongObject *v; + Py_ssize_t sign; + digit abs_x; + assert(!IS_SMALL_INT(x)); + assert(is_medium_int(x)); + v = PyObject_Malloc(sizeof(PyLongObject)); + if (v == NULL) { + PyErr_NoMemory(); + return NULL; + } + sign = x < 0 ? -1: 1; + abs_x = x < 0 ? -x : x; + _PyObject_InitVar((PyVarObject*)v, &PyLong_Type, sign); + v->ob_digit[0] = abs_x; + return (PyObject*)v; +""" +assert body.split() == expected.split(), "_PyLong_FromMedium must declare v, sign, and abs_x at function entry and retain assertions, allocation, NULL/error/return, sign, abs_x, initialization, digit write, and return order" +print("longobject _PyLong_FromMedium C89 declarations and evaluation/error order: PASS") + +match = re.search(r"static PyObject \*\s*_PyLong_FromLarge\(stwodigits ival\)\s*\{(?P.*?)\n\}", source, re.S) +assert match, "_PyLong_FromLarge definition not found" +body = re.sub(r"/\*.*?\*/", "", match.group("body"), flags=re.S) +expected = """ + twodigits abs_ival; + twodigits t; + Py_ssize_t ndigits; + PyLongObject *v; + digit *p; + int sign; + assert(!is_medium_int(ival)); + if (ival < 0) { + abs_ival = 0U-(twodigits)ival; + sign = -1; + } + else { + abs_ival = (twodigits)ival; + sign = 1; + } + assert(abs_ival >> PyLong_SHIFT != 0); + t = abs_ival >> (PyLong_SHIFT * 2); + ndigits = 2; + while (t) { + ++ndigits; + t >>= PyLong_SHIFT; + } + v = _PyLong_New(ndigits); + if (v != NULL) { + p = v->ob_digit; + Py_SET_SIZE(v, ndigits * sign); + t = abs_ival; + while (t) { + *p++ = Py_SAFE_DOWNCAST( + t & PyLong_MASK, twodigits, digit); + t >>= PyLong_SHIFT; + } + } + return (PyObject *)v; +""" +assert body.split() == expected.split(), "_PyLong_FromLarge must declare abs_ival, t, ndigits, v, p, and sign at function entry and retain assertions, sign/absolute-value branches, digit count, allocation, initialization, digit writes, and return order" +print("longobject _PyLong_FromLarge C89 declarations and evaluation order: PASS") + +match = re.search(r"^#define PYLONG_FROM_UINT\(INT_TYPE, ival\) (?P.*?)^ \} while\(0\)$", source, re.S | re.M) +assert match, "PYLONG_FROM_UINT definition not found" +body = match.group("body") + " } while(0)" +body = re.sub(r"\\\n", "\n", body) +body = re.sub(r"/\*.*?\*/", "", body, flags=re.S) +assert re.search(r"\A\s*do\s*\{\s*Py_ssize_t ndigits;\s*INT_TYPE t;\s*PyLongObject \*v;\s*digit \*p;", body), "PYLONG_FROM_UINT must declare ndigits, t, v, and p at do-block entry" +expected = """ + do { + Py_ssize_t ndigits; + INT_TYPE t; + PyLongObject *v; + digit *p; + if (IS_SMALL_UINT(ival)) { + return get_small_int((sdigit)(ival)); + } + ndigits = 0; + t = (ival); + while (t) { + ++ndigits; + t >>= PyLong_SHIFT; + } + v = _PyLong_New(ndigits); + if (v == NULL) { + return NULL; + } + p = v->ob_digit; + while ((ival)) { + *p++ = (digit)((ival) & PyLong_MASK); + (ival) >>= PyLong_SHIFT; + } + return (PyObject *)v; + } while(0) +""" +assert body.split() == expected.split(), "PYLONG_FROM_UINT must retain small-int early return, ndigits/t evaluation, digit count, allocation, NULL return, p initialization, ival evaluation/mutation, digit writes, and return order" +print("longobject PYLONG_FROM_UINT C89 declarations and evaluation/error order: PASS") + +match = re.search(r"PyObject \*\s*PyLong_FromUnsignedLongLong\((?P[^)]*)\)\s*\{(?P.*?)\n\}", source, re.S) +assert match, "PyLong_FromUnsignedLongLong definition not found" +assert match.group("parameter").split() == "unsigned PY_LONG_LONG ival".split(), "PyLong_FromUnsignedLongLong must use unsigned PY_LONG_LONG to match the public declaration and VC6 __int64 spelling" +assert match.group("body").split() == "PYLONG_FROM_UINT(unsigned PY_LONG_LONG, ival);".split(), "PyLong_FromUnsignedLongLong must pass unsigned PY_LONG_LONG to PYLONG_FROM_UINT, preserving the unsigned 64-bit temporary" +print("longobject PyLong_FromUnsignedLongLong VC6 parameter and macro type spelling: PASS") + +match = re.search(r"PyObject \*\s*PyLong_FromDouble\(double dval\)\s*\{(?P.*?)\n\}", source, re.S) +assert match, "PyLong_FromDouble definition not found" +body = re.sub(r"/\*.*?\*/", "", match.group("body"), flags=re.S) +assert re.search(r"\A\s*PyLongObject \*v;\s*double frac;\s*int i, ndig, expo, neg;\s*const double int_max = \(unsigned long\)LONG_MAX \+ 1;", body), "PyLong_FromDouble locals must precede the fast-path statement" +ordered = ( + "const double int_max = (unsigned long)LONG_MAX + 1;", + "if (-int_max < dval && dval < int_max)", + "return PyLong_FromLong((long)dval);", + "neg = 0;", + "if (Py_IS_INFINITY(dval))", + "if (Py_IS_NAN(dval))", + "if (dval < 0.0)", + "frac = frexp(dval, &expo);", + "ndig = (expo-1) / PyLong_SHIFT + 1;", + "v = _PyLong_New(ndig);", + "frac = ldexp(frac, (expo-1) % PyLong_SHIFT + 1);", + "for (i = ndig; --i >= 0; )", + "if (neg)", + "return (PyObject *)v;", +) +positions = [body.find(token) for token in ordered] +assert all(position >= 0 for position in positions) and positions == sorted(positions), "PyLong_FromDouble must preserve fast-path, validation, sign, conversion, allocation, digit-loop, and return order" +print("longobject PyLong_FromDouble C89 declarations and evaluation/error order: PASS") + +code = re.sub(r"/\*.*?\*/|//[^\n]*|\"(?:\\.|[^\"\\])*\"", "", source, flags=re.S) +assert not re.search(r"\blong\s+long\b", code), "longobject must spell 64-bit integers with PY_LONG_LONG for VC6" +assert not re.search(r"\bLLONG_(?:MAX|MIN)\b", code), "longobject must use PY_LLONG_MAX/PY_LLONG_MIN instead of unavailable LLONG limits" +for signature in ( + r"PyLong_FromLongLong\(PY_LONG_LONG ival\)", + r"PyLong_AsLongLong\(PyObject \*vv\)", + r"PyLong_AsUnsignedLongLong\(PyObject \*vv\)", + r"PyLong_AsUnsignedLongLongMask\(PyObject \*vv\)", + r"PyLong_AsLongLongAndOverflow\(PyObject \*vv, int \*overflow\)", +): + assert re.search(signature, source), "missing VC6-compatible long API declaration/definition: " + signature +print("longobject 64-bit API uses VC6 PY_LONG_LONG spellings: PASS") + +match = re.search(r"static digit\s+inplace_divrem1\(digit \*pout, digit \*pin, Py_ssize_t size, digit n\)\s*\{(?P.*?)\n\}", source, re.S) +assert match, "inplace_divrem1 definition not found" +body = re.sub(r"/\*.*?\*/", "", match.group("body"), flags=re.S) +loop = re.search(r"while \(--size >= 0\)\s*\{(?P.*?)\n \}", body, re.S) +assert loop, "inplace_divrem1 digit loop not found" +loop_body = loop.group("body") +assert re.search(r"\A\s*twodigits dividend;\s*digit quotient;\s*dividend = \(\(twodigits\)remainder << PyLong_SHIFT\) \| pin\[size\];\s*quotient = \(digit\)\(dividend / n\);\s*remainder = dividend % n;\s*pout\[size\] = quotient;", loop_body), "inplace_divrem1 loop temporaries must be declared before the executable dividend calculation, preserving divide/remainder/store order" +print("longobject inplace_divrem1 loop C89 declaration and division order: PASS") + +late_ucs4 = "else {\n assert (kind == PyUnicode_4BYTE_KIND);\n Py_UCS4 *p;" +entry_ucs4 = "else {\n Py_UCS4 *p;\n assert (kind == PyUnicode_4BYTE_KIND);" +assert source.count(late_ucs4) == 0, "Py_UCS4 locals must be hoisted before their branch assertions" +assert source.count(entry_ucs4) == 2, "both long-formatting branches must declare Py_UCS4 before checking the kind" +print("longobject Unicode formatting Py_UCS4 C89 branch declarations: PASS") + +match = re.search(r"PyObject \*\s*PyLong_FromString\(const char \*str, char \*\*pend, int base\)\s*\{(?P.*?)\n\}", source, re.S) +assert match, "PyLong_FromString definition not found" +body = re.sub(r"/\*.*?\*/", "", match.group("body"), flags=re.S) +assert re.search(r"\A\s*int sign = 1, error_if_nonzero = 0;\s*const char \*start, \*orig_str = str;\s*PyLongObject \*z = NULL;\s*PyObject \*strobj;\s*Py_ssize_t slen;\s*double fsize_z;", body), "PyLong_FromString must declare fsize_z in the function's opening declaration group" +ordered = ( + "if (digits > _PY_LONG_MAX_STR_DIGITS_THRESHOLD)", + "fsize_z = (double)digits * log_base_BASE[base] + 1.0;", + "if (fsize_z > (double)MAX_LONG_DIGITS)", + "size_z = (Py_ssize_t)fsize_z;", + "z = _PyLong_New(size_z);", +) +positions = [body.find(token) for token in ordered] +assert all(position >= 0 for position in positions) and positions == sorted(positions), "PyLong_FromString must preserve digit-limit, size calculation, overflow, allocation, and conversion order" +print("longobject PyLong_FromString C89 size declaration and parsing order: PASS") + +match = re.search(r"static int\s+long_divrem\(PyLongObject \*a, PyLongObject \*b,\s*PyLongObject \*\*pdiv, PyLongObject \*\*prem\)\s*\{(?P.*?)\n\}", source, re.S) +assert match, "long_divrem definition not found" +body = match.group("body") +short_case = re.search(r"if \(size_a < size_b.*?\) \{(?P.*?)\n \}", body, re.S) +assert short_case, "long_divrem smaller-dividend branch not found" +branch = short_case.group("body") +tokens = ( + "PyObject *zero;", + "*prem = (PyLongObject *)long_long((PyObject *)a);", + "if (*prem == NULL)", + "zero = _PyLong_GetZero();", + "Py_INCREF(zero);", + "*pdiv = (PyLongObject*)zero;", +) +positions = [branch.find(token) for token in tokens] +assert all(position >= 0 for position in positions) and positions == sorted(positions), "long_divrem must declare zero at branch entry and preserve remainder, error, singleton, incref, and quotient ordering" +print("longobject long_divrem C89 local and reference order: PASS") + +match = re.search(r"PyObject \*\s*_PyLong_Add\(PyLongObject \*a, PyLongObject \*b\)\s*\{(?P.*?)\n\}", source, re.S) +assert match, "_PyLong_Add definition not found" +body = match.group("body") +assert re.match(r"\s*PyLongObject \*z;", body), "_PyLong_Add must declare z at function entry before the small-value fast path" +assert " PyLongObject *z;" not in body[body.find("if (IS_MEDIUM_VALUE(a)"):], "_PyLong_Add must not declare z after an executable fast-path statement" +ordered = ( + "PyLongObject *z;", + "if (IS_MEDIUM_VALUE(a) && IS_MEDIUM_VALUE(b))", + "return _PyLong_FromSTwoDigits(medium_value(a) + medium_value(b));", + "if (Py_SIZE(a) < 0)", + "z = x_add(a, b);", + "return (PyObject *)z;", +) +positions = [body.find(token) for token in ordered] +assert all(position >= 0 for position in positions) and positions == sorted(positions), "_PyLong_Add must preserve small-integer fast path and sign/operation order" +print("longobject _PyLong_Add C89 declaration and arithmetic order: PASS") + +match = re.search(r"static PyObject \*\s*long_pow\(PyObject \*v, PyObject \*w, PyObject \*x\)\s*\{(?P.*?)\n\}", source, re.S) +assert match, "long_pow definition not found" +body = match.group("body") +assert 0 <= body.find("digit bi, bit;") < body.find("CHECK_BINOP(v, w);"), "long_pow must declare exponent digit temporaries with its opening locals" +assert "digit bi = i ?" not in body and "digit bit;" not in body[body.find("i = Py_SIZE(b);"):], "long_pow must not declare digit temporaries after executable statements" +ordered = ("i = Py_SIZE(b);", "bi = i ? b->ob_digit[i-1] : 0;", "if (i <= 1 && bi <= 3)") +positions = [body.find(token) for token in ordered] +assert all(position >= 0 for position in positions) and positions == sorted(positions), "long_pow must preserve exponent digit setup and branch ordering" +print("longobject long_pow C89 declarations and exponent setup order: PASS") +branch_start = body.find("else {\n int pending;\n int blen;") +branch_end = body.find("\n }\n\n if (negativeOutput", branch_start) +assert branch_start >= 0 and branch_end > branch_start, "long_pow k-ary branch boundaries not found" +branch = body[branch_start:branch_end] +assert branch.find("int pending;") < branch.find("Py_INCREF(a);") and branch.find("int blen;") < branch.find("Py_INCREF(a);"), "long_pow pending and bit-length locals must be declared at branch entry" +assert "int pending = 0, blen = 0;" not in branch, "long_pow pending locals must be declared separately from their original initialization point" +ordered = ("Py_CLEAR(a2);", "pending = 0;", "blen = 0;", "#define ABSORB_PENDING") +positions = [branch.find(token) for token in ordered] +assert all(position >= 0 for position in positions) and positions == sorted(positions), "long_pow must preserve pending initialization after table setup and before the sliding-window macro" +print("longobject long_pow C89 pending declarations and initialization order: PASS") + +match = re.search(r"static int\s+divmod_shift\(PyObject \*shiftby, Py_ssize_t \*wordshift, digit \*remshift\)\s*\{(?P.*?)\n\}", source, re.S) +assert match, "divmod_shift definition not found" +body = match.group("body") +assert re.match(r"\s*Py_ssize_t lshiftby;\s*PyLongObject \*wordshift_obj;", body), "divmod_shift must declare both conversion temporaries at function entry" +assert "Py_ssize_t lshiftby =" not in body and "PyLongObject *wordshift_obj =" not in body, "divmod_shift must preserve original conversion points as assignments" +ordered = ("assert(PyLong_Check(shiftby));", "assert(Py_SIZE(shiftby) >= 0);", "lshiftby = PyLong_AsSsize_t((PyObject *)shiftby);", "wordshift_obj = divrem1((PyLongObject *)shiftby, PyLong_SHIFT, remshift);", "*wordshift = PyLong_AsSsize_t((PyObject *)wordshift_obj);") +positions = [body.find(token) for token in ordered] +assert all(position >= 0 for position in positions) and positions == sorted(positions), "divmod_shift must preserve validation, conversion, fallback division, and result extraction order" +print("longobject divmod_shift C89 locals and overflow-fallback order: PASS") + +match = re.search(r"static PyObject \*\s*long_rshift1\(PyLongObject \*a, Py_ssize_t wordshift, digit remshift\)\s*\{(?P.*?)\n\}", source, re.S) +assert match, "long_rshift1 definition not found" +body = match.group("body") +assert "digit sticky = 0;" not in body and "for (Py_ssize_t j =" not in body and "for (Py_ssize_t i =" not in body, "long_rshift1 must not declare locals in executable branches or C99 for initializers" +negative_matches = list(re.finditer(r"if \(a_negative\)\s*\{(?P.*?)\n \}", body, re.S)) +assert negative_matches, "long_rshift1 negative-input branch not found" +negative_body = negative_matches[-1].group("body") +assert negative_body.find("digit sticky;") < negative_body.find("Py_SET_SIZE(z, -newsize);"), "long_rshift1 must declare sticky before branch statements" +assert negative_body.find("sticky = 0;") > negative_body.find("Py_SET_SIZE(z, -newsize);"), "long_rshift1 must initialize sticky after setting the result sign" +assert "{\n Py_ssize_t j;\n for (j = 0; j < wordshift; j++)" in negative_body, "long_rshift1 must keep the sticky-scan counter block-scoped with C89 declarations" +assert "{\n Py_ssize_t i, j;\n i = 0;\n j = wordshift + 1;\n for (; j < size_a; i++, j++)" in body, "long_rshift1 must keep copy-loop counters block-scoped and preserve initial values" +print("longobject long_rshift1 C89 locals and shifted-digit order: PASS") + +for name, op in (("long_and", "&"), ("long_xor", "^"), ("long_or", "|")): + match = re.search(r"static PyObject \*\s*" + name + r"\(PyObject \*a, PyObject \*b\)\s*\{(?P.*?)\n\}", source, re.S) + assert match, name + " definition not found" + body = match.group("body") + expected = """ + PyLongObject *x; + PyLongObject *y; + CHECK_BINOP(a, b); + x = (PyLongObject*)a; + y = (PyLongObject*)b; + """ + assert body.split()[:len(expected.split())] == expected.split(), name + " must declare x/y before type checking, then cast and preserve the small-value fast path" + assert "PyLongObject *x =" not in body and "PyLongObject *y =" not in body, name + " must not declare cast locals after CHECK_BINOP" + assert "return long_bitwise(x, '" + op + "', y);" in body, name + " must retain the expected general bitwise operation" +print("longobject bitwise entry points C89 locals and dispatch order: PASS") + +match = re.search(r"PyObject \*\s*_PyLong_DivmodNear\(PyObject \*a, PyObject \*b\)\s*\{(?P.*?)\n\}", source, re.S) +assert match, "_PyLong_DivmodNear definition not found" +body = match.group("body") +assert re.match(r"\s*PyLongObject \*quo = NULL, \*rem = NULL;\s*PyObject \*twice_rem, \*result, \*temp;\s*PyObject \*one;\s*int quo_is_odd, quo_is_neg;\s*Py_ssize_t cmp;", body), "_PyLong_DivmodNear must declare one with the function's opening locals" +assert "PyObject *one =" not in body, "_PyLong_DivmodNear must preserve _PyLong_GetOne lookup at its original execution point" +ordered = ("if (!PyLong_Check(a) || !PyLong_Check(b))", "quo_is_neg =", "long_divrem(", "one = _PyLong_GetOne();", "twice_rem = long_lshift((PyObject *)rem, one);") +positions = [body.find(token) for token in ordered] +assert all(position >= 0 for position in positions) and positions == sorted(positions), "_PyLong_DivmodNear must preserve validation, divrem, singleton lookup, and shift order" +print("longobject _PyLong_DivmodNear C89 local and singleton lookup order: PASS") + +match = re.search(r"static PyObject \*\s*int_bit_count_impl\(PyObject \*self\)\s*/\*.*?\*/\s*\{(?P.*?)\n\}", source, re.S) +assert match, "int_bit_count_impl definition not found" +body = match.group("body") +expected = """ + PyLongObject *z; + Py_ssize_t ndigits, bit_count, ndigits_fast, i; + PyObject *result; + assert(self != NULL); + assert(PyLong_Check(self)); + z = (PyLongObject *)self; + ndigits = Py_ABS(Py_SIZE(z)); + bit_count = 0; +""" +assert body.split()[:len(expected.split())] == expected.split(), "int_bit_count_impl must place declarations before assertions and preserve initialization order" +assert "Py_ssize_t ndigits =" not in body and "Py_ssize_t bit_count =" not in body and "for (Py_ssize_t i =" not in body, "int_bit_count_impl must not use late declarations or C99 loop variables" +assert "PyObject *result =" not in body and "PyObject *x =" not in body and "PyObject *y =" not in body, "int_bit_count_impl must initialize object locals at their original execution points" +assert "Py_ssize_t ndigits_fast =" not in body, "int_bit_count_impl must calculate the fast digit bound at the original point" +ordered = ("ndigits_fast = Py_MIN(ndigits, PY_SSIZE_T_MAX/PyLong_SHIFT);", "for (i = 0; i < ndigits_fast; i++)", "result = PyLong_FromSsize_t(bit_count);", "for (i = ndigits_fast; i < ndigits; i++)") +positions = [body.find(token) for token in ordered] +assert all(position >= 0 for position in positions) and positions == sorted(positions), "int_bit_count_impl must preserve fast and overflow-safe accumulation order" +loop = re.search(r"for \(i = ndigits_fast; i < ndigits; i\+\+\)\s*\{(?P.*?)\n \}", body, re.S) +assert loop and re.match(r"\s*PyObject \*x;\s*PyObject \*y;\s*x = PyLong_FromLong\(popcount_digit\(z->ob_digit\[i\]\)\);", loop.group("body")), "int_bit_count_impl must declare per-digit temporaries before the loop body statements" +assert 0 <= loop.group("body").find("y = long_add(") < loop.group("body").find("Py_DECREF(x);"), "int_bit_count_impl must preserve accumulation before releasing the temporary digit count" +print("longobject int_bit_count_impl C89 declarations and accumulation order: PASS") diff --git a/scripts/vc6-memoryobject-c89.test.py b/scripts/vc6-memoryobject-c89.test.py new file mode 100644 index 0000000..92a70ea --- /dev/null +++ b/scripts/vc6-memoryobject-c89.test.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +"""Focused Visual C++ 6.0 C89 regression checks for memoryobject.c.""" + +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() + +match = re.search( + r"static int\s+copy_single\(PyMemoryViewObject \*self, const Py_buffer \*dest, const Py_buffer \*src\)\s*\{(?P.*?)\n\}", + source, + re.S, +) +assert match, "copy_single definition not found" +body = match.group("body") +expected = """ + char *mem; + CHECK_RELEASED_INT_AGAIN(self); + mem = NULL; +""" +assert body.split()[:len(expected.split())] == expected.split(), ( + "copy_single must declare mem before the release-check macro while keeping its initialization after the check" +) +assert "char *mem =" not in body, "copy_single must not initialize mem before the release check" +print("memoryobject copy_single C89 local and release-check order: PASS") + +assert not re.search(r"\blong\s+long\b", source), "memoryobject must use PY_LONG_LONG spellings supported by VC6" +assert re.search(r"\bPY_LONG_LONG\b", source), "memoryobject must retain native 64-bit format support through PY_LONG_LONG" +assert not re.search(r"\b_Bool\b", source), "memoryobject must use the VC6-compatible one-byte bool representation" +assert "case '?': UNPACK_SINGLE(ld, ptr, unsigned char); goto convert_bool;" in source, "memoryobject bool unpacking must preserve nonzero-to-bool conversion" +assert "PACK_SINGLE(ptr, ld, unsigned char);" in source, "memoryobject bool packing must use a one-byte type after PyObject_IsTrue normalization" +print("memoryobject VC6 64-bit and bool format spellings: PASS") + +match = re.search(r"static PyObject \*\s*memoryiter_next\(memoryiterobject \*it\)\s*\{(?P.*?)\n\}", source, re.S) +assert match, "memoryiter_next definition not found" +body = match.group("body") +assert "Py_buffer *view =" not in body and "char *ptr =" not in body, "memoryiter_next must not declare locals after CHECK_RELEASED" +assert "if (it->it_index < it->it_length) {\n Py_buffer *view;\n char *ptr;\n\n CHECK_RELEASED(seq);\n view = &(seq->view);\n ptr = (char *)seq->view.buf;" in body, "memoryiter_next must declare conditional locals before release checks and preserve pointer setup order" +print("memoryobject memoryiter_next C89 locals and release-check order: PASS") + +match = re.search(r"static PyObject \*\s*memory_iter\(PyObject \*seq\)\s*\{(?P.*?)\n\}", source, re.S) +assert match, "memory_iter definition not found" +body = match.group("body") +expected = """ + PyMemoryViewObject *obj; + int ndims; + const char *fmt; + memoryiterobject *it; + if (!PyMemoryView_Check(seq)) +""" +assert body.split()[:len(expected.split())] == expected.split(), "memory_iter must declare locals before validation" +ordered = ("if (!PyMemoryView_Check(seq))", "obj = (PyMemoryViewObject *)seq;", "ndims = obj->view.ndim;", "fmt = adjust_fmt(&obj->view);", "it = PyObject_GC_New(memoryiterobject, &_PyMemoryIter_Type);") +positions = [body.find(token) for token in ordered] +assert all(position >= 0 for position in positions) and positions == sorted(positions), "memory_iter must preserve validation, type/shape setup, format adjustment, and allocation order" +assert not re.search(r"\b(?:PyMemoryViewObject|int|const char|memoryiterobject)\s+\*?\w+\s*=", body), "memory_iter must not retain late initialized declarations" +print("memoryobject memory_iter C89 locals and iterator allocation order: PASS") + +match = re.search(r"PyTypeObject _PyMemoryIter_Type = \{(?P.*?)\n\};", source, re.S) +assert match, "_PyMemoryIter_Type initializer not found" +body = match.group("body") +assert ".tp_" not in body, "_PyMemoryIter_Type must use positional fields instead of C99 designated initializers" +expected = ( + "PyVarObject_HEAD_INIT(&PyType_Type, 0)", + '"memory_iterator"', + "sizeof(memoryiterobject)", + "0,", + "(destructor)memoryiter_dealloc", + "0,", + "0,", + "0,", + "0,", + "0,", + "0,", + "0,", + "0,", + "0,", + "0,", + "0,", + "PyObject_GenericGetAttr", + "0,", + "0,", + "Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC", + "0,", + "(traverseproc)memoryiter_traverse", + "0,", + "0,", + "0,", + "PyObject_SelfIter", + "(iternextfunc)memoryiter_next", +) +positions = [] +cursor = 0 +for token in expected: + position = body.find(token, cursor) + positions.append(position) + if position >= 0: + cursor = position + len(token) +assert all(position >= 0 for position in positions), "_PyMemoryIter_Type positional initializer must preserve all configured field slots" +print("memoryobject _PyMemoryIter_Type C89 positional initializer: PASS") diff --git a/scripts/vc6-methodobject-c89.test.py b/scripts/vc6-methodobject-c89.test.py new file mode 100644 index 0000000..fe2a2c8 --- /dev/null +++ b/scripts/vc6-methodobject-c89.test.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +"""Focused Visual C++ 6.0 C89 regression checks for methodobject.c.""" + +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() + +match = re.search( + r"PyObject \*\s*PyCMethod_New\(PyMethodDef \*ml, PyObject \*self, PyObject \*module, PyTypeObject \*cls\)\s*\{(?P.*?)\n\}", + source, + re.S, +) +assert match, "PyCMethod_New definition not found" +body = match.group("body") +declarations = ("vectorcallfunc vectorcall;", "PyCFunctionObject *op;", "PyCMethodObject *om;", "switch (ml->ml_flags") +declaration_positions = [body.find(token) for token in declarations] +assert all(position >= 0 for position in declaration_positions) and declaration_positions == sorted(declaration_positions), ( + "PyCMethod_New must declare op and om at function entry before its dispatch switch" +) +ordered = ( + "switch (ml->ml_flags", + "op = NULL;", + "if (ml->ml_flags & METH_METHOD)", + "om = PyObject_GC_New(PyCMethodObject, &PyCMethod_Type);", + "op = PyObject_GC_New(PyCFunctionObject, &PyCFunction_Type);", + "op->m_weakreflist = NULL;", +) +positions = [body.find(token) for token in ordered] +assert all(position >= 0 for position in positions) and positions == sorted(positions), ( + "PyCMethod_New must preserve flag dispatch, null initialization, allocation, and field setup order" +) +assert "PyCFunctionObject *op =" not in body and "PyCMethodObject *om =" not in body, ( + "PyCMethod_New must not introduce late C89 declarations" +) +print("methodobject PyCMethod_New C89 declarations and allocation order: PASS") + +match = re.search(r"PyTypeObject PyCMethod_Type = \{(?P.*?)\n\};", source, re.S) +assert match, "PyCMethod_Type initializer not found" +body = match.group("body") +assert ".tp_" not in body, "PyCMethod_Type must use positional fields instead of C99 designated initializers" +expected = ( + "PyVarObject_HEAD_INIT(&PyType_Type, 0)", + '"builtin_method"', + "sizeof(PyCMethodObject)", +) + ("0,",) * 27 + ("&PyCFunction_Type",) +positions = [] +cursor = 0 +for token in expected: + position = body.find(token, cursor) + positions.append(position) + if position >= 0: + cursor = position + len(token) +assert all(position >= 0 for position in positions), "PyCMethod_Type positional initializer must preserve the base-type slot" +print("methodobject PyCMethod_Type C89 positional initializer: PASS") + + +def function_body(name): + found = re.search( + r"static PyObject \*\s*" + re.escape(name) + r"\s*\([^;]*\)\s*\{(?P.*?)\n\}", + source, + re.S, + ) + assert found, name + " definition not found" + return found.group("body") + + +checks = { + "cfunction_vectorcall_FASTCALL": ( + "PyThreadState *tstate;", + "Py_ssize_t nargs;", + "_PyCFunctionFast meth;", + "PyObject *result;", + "tstate = _PyThreadState_GET();", + "nargs = PyVectorcall_NARGS(nargsf);", + "meth = (_PyCFunctionFast)", + "result = meth(", + ), + "cfunction_vectorcall_FASTCALL_KEYWORDS": ( + "PyThreadState *tstate;", + "Py_ssize_t nargs;", + "_PyCFunctionFastWithKeywords meth;", + "PyObject *result;", + "tstate = _PyThreadState_GET();", + "nargs = PyVectorcall_NARGS(nargsf);", + "meth = (_PyCFunctionFastWithKeywords)", + "result = meth(", + ), + "cfunction_vectorcall_NOARGS": ( + "Py_ssize_t nargs;", + "PyCFunction meth;", + "PyObject *result;", + "PyThreadState *tstate = _PyThreadState_GET();", + "if (cfunction_check_kwargs(", + "nargs = PyVectorcall_NARGS(nargsf);", + "meth = (PyCFunction)cfunction_enter_call(", + "result = _PyCFunction_TrampolineCall(", + ), + "cfunction_vectorcall_O": ( + "Py_ssize_t nargs;", + "PyCFunction meth;", + "PyObject *result;", + "PyThreadState *tstate = _PyThreadState_GET();", + "if (cfunction_check_kwargs(", + "nargs = PyVectorcall_NARGS(nargsf);", + "meth = (PyCFunction)cfunction_enter_call(", + "result = _PyCFunction_TrampolineCall(", + ), + "cfunction_call": ( + "PyThreadState *tstate;", + "int flags;", + "PyCFunction meth;", + "PyObject *self;", + "PyObject *result;", + "assert(kwargs == NULL || PyDict_Check(kwargs));", + "tstate = _PyThreadState_GET();", + "flags = PyCFunction_GET_FLAGS(func);", + "meth = PyCFunction_GET_FUNCTION(func);", + "self = PyCFunction_GET_SELF(func);", + ), +} +for name, tokens in checks.items(): + body = function_body(name) + positions = [body.find(token) for token in tokens] + assert all(position >= 0 for position in positions) and positions == sorted(positions), ( + name + " must keep declarations before statements and preserve evaluation order" + ) + for declaration in tokens[:5]: + if " = " not in declaration and declaration.endswith(";"): + assert declaration in body, name + " is missing an opening local declaration" +print("methodobject vectorcall and call paths C89 declarations and evaluation order: PASS") diff --git a/scripts/vc6-moduleobject-c89.test.py b/scripts/vc6-moduleobject-c89.test.py new file mode 100644 index 0000000..04f25da --- /dev/null +++ b/scripts/vc6-moduleobject-c89.test.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +"""Focused Visual C++ 6.0 C89 regression checks for moduleobject.c.""" + +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() + + +def function_body(name): + match = re.search( + re.escape(name) + r"\s*\([^;]*\)\s*\{(?P.*?)\n\}", + source, + re.S, + ) + assert match, name + " definition not found" + return match.group("body") + + +def ordered(body, tokens, message): + positions = [body.find(token) for token in tokens] + assert all(position >= 0 for position in positions) and positions == sorted(positions), message + + +body = function_body("_PyModule_IsExtension") +ordered(body, ( + "PyModuleObject *module;", + "PyModuleDef *def;", + "if (!PyModule_Check(obj))", + "module = (PyModuleObject*)obj;", + "def = module->md_def;", +), "_PyModule_IsExtension must declare its locals before validation and preserve lookup order") +assert "PyModuleObject *module =" not in body and "PyModuleDef *def =" not in body +print("moduleobject _PyModule_IsExtension C89 locals and lookup order: PASS") + +body = function_body("_PyModuleSpec_IsUninitializedSubmodule") +ordered(body, ( + "PyObject *value;", + "int is_uninitialized;", + "if (spec == NULL)", + "value = PyObject_GetAttr(spec, &_Py_ID(_uninitialized_submodules));", + "is_uninitialized = PySequence_Contains(value, name);", + "Py_DECREF(value);", +), "_PyModuleSpec_IsUninitializedSubmodule must hoist declarations while preserving reference order") +assert "PyObject *value =" not in body and "int is_uninitialized =" not in body +print("moduleobject uninitialized-submodule C89 locals and reference order: PASS") + +body = function_body("module_getattro") +ordered(body, ( + "PyObject *attr, *mod_name, *getattr, *spec;", + "attr = PyObject_GenericGetAttr((PyObject *)m, name);", + "Py_INCREF(mod_name);", + "spec = PyDict_GetItemWithError(m->md_dict, &_Py_ID(__spec__));", +), "module_getattro must hoist spec without changing lookup timing") +assert "PyObject *spec =" not in body +print("moduleobject module_getattro C89 local and lookup order: PASS") + +body = function_body("module_get_annotations") +ordered(body, ( + "PyObject *dict;", + "PyObject *annotations;", + "int result;", + "dict = PyObject_GetAttr((PyObject *)m, &_Py_ID(__dict__));", + "annotations = PyDict_GetItemWithError(dict, &_Py_ID(__annotations__));", + "result = PyDict_SetItem(", +), "module_get_annotations must hoist locals and preserve dictionary/error order") +assert "PyObject *annotations =" not in body and "int result =" not in body +print("moduleobject module_get_annotations C89 locals and dictionary order: PASS") diff --git a/scripts/vc6-object-c89.test.py b/scripts/vc6-object-c89.test.py new file mode 100644 index 0000000..7b646f6 --- /dev/null +++ b/scripts/vc6-object-c89.test.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +"""Focused Visual C++ 6.0 C89 regression checks for object.c.""" + +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() + + +def body(name): + found = re.search(re.escape(name) + r"\s*\([^;]*\)\s*\{(?P.*?)\n\}", source, re.S) + assert found, name + " definition not found" + return found.group("body") + + +def check(name, tokens, message): + text = body(name) + positions = [text.find(token) for token in tokens] + assert all(position >= 0 for position in positions) and positions == sorted(positions), message + return text + + +text = check("PyObject_CallFinalizerFromDealloc", ( + "Py_ssize_t refcnt;", + "if (Py_REFCNT(self) != 0)", + "refcnt = Py_REFCNT(self);", + "_Py_NewReference(self);", + "Py_SET_REFCNT(self, refcnt);", +), "PyObject_CallFinalizerFromDealloc must hoist refcnt while preserving resurrection order") +assert "Py_ssize_t refcnt =" not in text +print("object PyObject_CallFinalizerFromDealloc C89 local and refcount order: PASS") + +text = check("_PyObject_Dump", ( + "PyTypeObject *type;", + "PyGILState_STATE gil;", + "PyObject *error_type, *error_value, *error_traceback;", + "if (_PyObject_IsFreed(op))", + "type = Py_TYPE(op);", + "gil = PyGILState_Ensure();", + "PyErr_Fetch(&error_type, &error_value, &error_traceback);", +), "_PyObject_Dump must hoist locals without changing diagnostics or exception preservation") +assert "PyTypeObject *type =" not in text and "PyGILState_STATE gil =" not in text +print("object _PyObject_Dump C89 locals and exception preservation: PASS") + +text = check("PyObject_Repr", ( + "PyObject *res;", + "PyThreadState *tstate;", + "if (PyErr_CheckSignals())", + "tstate = _PyThreadState_GET();", +), "PyObject_Repr must hoist tstate while preserving signal-check order") +assert "PyThreadState *tstate =" not in text +print("object PyObject_Repr C89 local and signal-check order: PASS") diff --git a/scripts/vc6-probe-pythoncore-frontier.test.sh b/scripts/vc6-probe-pythoncore-frontier.test.sh index 0765ee9..0eabe3a 100755 --- a/scripts/vc6-probe-pythoncore-frontier.test.sh +++ b/scripts/vc6-probe-pythoncore-frontier.test.sh @@ -9945,6 +9945,66 @@ else bad "frameobject frame_getlocals C89 declaration and ownership order" fi +if python3 "$REPO_ROOT/scripts/vc6-funcobject-c89.test.py" "$REPO_ROOT/cpython/Objects/funcobject.c"; then + pass "funcobject C89 declarations preserve function-construction ownership order" +else + bad "funcobject C89 declarations preserve function-construction ownership order" +fi + +if python3 "$REPO_ROOT/scripts/vc6-genericaliasobject-c89.test.py" "$REPO_ROOT/cpython/Objects/genericaliasobject.c" "$REPO_ROOT/cpython/Objects/object.c"; then + pass "genericaliasobject C89 declarations preserve writer and ownership order" +else + bad "genericaliasobject C89 declarations preserve writer and ownership order" +fi + +if python3 "$REPO_ROOT/scripts/vc6-genobject-c89.test.py" "$REPO_ROOT/cpython/Objects/genobject.c"; then + pass "genobject generator traversal C89 declaration and error order" +else + bad "genobject generator traversal C89 declaration and error order" +fi + +if python3 "$REPO_ROOT/scripts/vc6-interpreteridobject-c89.test.py" "$REPO_ROOT/cpython/Objects/interpreteridobject.c"; then + pass "interpreteridobject C89 declarations and ownership order" +else + bad "interpreteridobject C89 declarations and ownership order" +fi + +if python3 "$REPO_ROOT/scripts/vc6-iterobject-c89.test.py" "$REPO_ROOT/cpython/Objects/iterobject.c"; then + pass "iterobject anextawaitable C89 declarations and reference order" +else + bad "iterobject anextawaitable C89 declarations and reference order" +fi + +if python3 "$REPO_ROOT/scripts/vc6-listobject-c89.test.py" "$REPO_ROOT/cpython/Objects/listobject.c"; then + pass "listobject C89 declarations preserve allocation and ownership order" +else + bad "listobject C89 declarations preserve allocation and ownership order" +fi + +if python3 "$REPO_ROOT/scripts/vc6-longobject-c89.test.py" "$REPO_ROOT/cpython/Objects/longobject.c"; then + pass "longobject C89 declarations preserve allocation and evaluation order" +else + bad "longobject C89 declarations preserve allocation and evaluation order" +fi + +if python3 "$REPO_ROOT/scripts/vc6-memoryobject-c89.test.py" "$REPO_ROOT/cpython/Objects/memoryobject.c"; then + pass "memoryobject VC6 C89 declarations and native-format type spellings" +else + bad "memoryobject VC6 C89 declarations and native-format type spellings" +fi + +if python3 "$REPO_ROOT/scripts/vc6-methodobject-c89.test.py" "$REPO_ROOT/cpython/Objects/methodobject.c"; then + pass "methodobject VC6 C89 declarations and type initialization" +else + bad "methodobject VC6 C89 declarations and type initialization" +fi + +if python3 "$REPO_ROOT/scripts/vc6-moduleobject-c89.test.py" "$REPO_ROOT/cpython/Objects/moduleobject.c"; then + pass "moduleobject VC6 C89 declarations and evaluation order" +else + bad "moduleobject VC6 C89 declarations and evaluation order" +fi + if [ "$fail" -ne 0 ]; then echo "FAILED" exit 1