diff --git a/PORT_STATUS.md b/PORT_STATUS.md index 889bf0a..725ef2f 100644 --- a/PORT_STATUS.md +++ b/PORT_STATUS.md @@ -1,5 +1,3295 @@ +## Latest revision (`dict_keys()` C89 declarations hoisted; frontier advances in dictobject.c) + +Date: 2026-10-02 16:22 UTC + +Read `PORT_STATUS.md`, `README.md`, and the CPython/Win9x/MSVC6 reference before work. Preserved all pre-existing outer and detached CPython changes; no reset, clean, stash, discard, commit, push, or publication. CPython remains at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Re-probed because the status named `codeobject.c` while the older `_blake2/blake2s_impl.c` frontier instruction was stale. The forced fresh probe confirmed `dictobject.c` as the actual first blocker. + +One narrow TDD C89 slice in `cpython/Objects/dictobject.c`: moved only `j`, `pos`, and `key` declarations in `dict_keys()` to the opening declaration group, with `j = 0` and `pos = 0` remaining after the successful allocation/retry guard. Iteration, refcounting, and return order are unchanged. Extended the pre-existing untracked focused regression `scripts/vc6-dict-consistency-c89.test.py` to assert declaration location and the exact following sequence. Used exactly one focused Codex coding-agent lane; independently inspected its diff. No code outside `dict_keys()` changed in this slice; the other existing nested/outer changes remain untouched. + +Exact verification: + +- RED: `python3 scripts/vc6-dict-consistency-c89.test.py cpython/Objects/stringlib/eq.h cpython/Objects/dictobject.c` — exit 1 before the production edit, expected `dict_keys locals must be declared at function entry without initializers`. +- GREEN and independent rerun: same command — exit 0, `dict consistency C89 declarations and logic order: PASS`. +- `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1. New dict regression passed; existing console type-detection-order check failed (`console type lookup and cleanup order must remain unchanged`) and existing numeric-dispatch check raised `ValueError: substring not found`. No unrelated fix attempted. +- 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=83 compiled=82 cached=0 skipped=0`. `_blake2/blake2s_impl.c` and `dict_keys()` compile past the prior frontier. First stable errors are in `dict_values()` at `dictobject.c(2700–2701)`: C2275/C2146/C2065 for late `Py_ssize_t j, pos` and C2275/C2065 for late `PyObject *value`; later dictobject diagnostics are distinct follow-on blockers and untouched. Full output: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1790958052-2772156-1810.log`. +- `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh`, focused and scoped `git diff --check` — exit 0. + +Files changed by this slice: `cpython/Objects/dictobject.c`, `scripts/vc6-dict-consistency-c89.test.py`, and this log. This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution. The existing compatibility patch was not regenerated. Next frontier: test-first hoist only the late locals in `dict_values()`, preserving the successful-allocation/retry boundary and iteration/refcount order. + +--- + +## Previous latest revision (`PyCode_Addr2Location` C89 declaration hoisted; frontier advances in codeobject.c) + +Date: 2026-10-02 12:01 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before work. Preserved all pre-existing outer and detached CPython edits; no reset, clean, stash, discard, commit, push, or publication. CPython remains at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Confirmed Codex CLI 0.153.4 was available and used exactly one focused coding-agent lane with workspace-write sandbox. + +The recorded `advance_with_locations_no_columns()` frontier was confirmed by a forced fresh VC6 probe. Applied one narrow C89 declaration-order slice in `Objects/codeobject.c`: moved only `PyCodeAddressRange bounds` in `PyCode_Addr2Location()` to the function entry declaration group. `_PyCode_InitAddressRange()` remains at its original execution point after the address assertion; negative-address fast return and all lookup/retreat/advance order are unchanged. Extended `scripts/vc6-codeobject-c89.test.py` with a focused declaration and full execution-order assertion. The compatibility patch was regenerated from the complete nested diff and verified byte-for-byte. + +Exact verification: + +- RED: `python3 scripts/vc6-codeobject-c89.test.py cpython/Objects/codeobject.c` — exit 1, expected `PyCode_Addr2Location bounds must be declared before execution`. +- GREEN and independent focused rerun: same command — exit 0, `codeobject C89 declarations and ownership order: PASS`. +- `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1. The new codeobject check and most suite checks pass; two known unrelated failures remain: console type detection ordering, and numeric-dispatch assertion (`ValueError: substring not found`). No unrelated fix was attempted. +- Forced fresh VC6 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=80 compiled=79 cached=0 skipped=0`. `codeobject.c` passes the previous line-1028 `PyCodeAddressRange bounds` diagnostic. The next first stable diagnostics are C2143 at line 1071 before `const`, then dependent C2065 `ptr` undeclared at 1073 and C2143/C2065 on `code` at 1075–1076 in `previous_code_delta()`; later diagnostics are distinct and untouched. Full output: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1790942268-2495375-b650.log`. +- `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` — exit 0. Nested and outer scoped `git diff --check` — exit 0. + +Files changed by this slice: `cpython/Objects/codeobject.c`, `scripts/vc6-codeobject-c89.test.py`, regenerated `compat/msvc600/cpython-3.11.16-vc6-headers.patch`, and this status log. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution. Next frontier: test-first hoist only the `previous_code_delta()` local(s) responsible for the first fresh diagnostic, preserving its first-entry return and backward scan order. + +--- + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference. Preserved all pre-existing outer and detached CPython edits; no reset, clean, stash, discard, commit, push, or publication. CPython remains at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Confirmed Codex CLI 0.153.4 was available and used exactly one focused coding-agent lane, without bypassing its sandbox. + +A forced fresh VC6 probe reconfirmed `_blake2/blake2s_impl.c` compiles and the active first blocker is `Objects/codeobject.c(964–968)` in `advance_with_locations()`. Applied one C89 declaration-order slice: moved only `int first_byte` and `int code` to the function's opening declaration group; both assignments remain immediately after `ASSERT_VALID_BOUNDS(bounds)`, preserving byte-read/decode/range-update/dispatch order. Added a focused ordering assertion to the existing untracked codeobject regression test and observed RED before the production edit. + +Exact verification: + +- RED: `python3 scripts/vc6-codeobject-c89.test.py cpython/Objects/codeobject.c` — exit 1, expected `advance_with_locations entry declaration missing or late: int first_byte;`. +- GREEN: same command — exit 0, `codeobject C89 declarations and ownership order: PASS`. +- Independent baseline/hash check — passed: exactly the requested `cpython/Objects/codeobject.c` and `scripts/vc6-codeobject-c89.test.py` changed in the implementation slice; existing checks and all other pre-existing edits preserved. Scoped nested/outer `git diff --check` — exit 0. +- `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1. The full script's codeobject and new assertions pass; two existing unrelated checks remain failing: console type detection order, and numeric dispatch (`ValueError: substring not found`). +- Forced fresh VC6 command `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=80 compiled=79 cached=0 skipped=0`. `codeobject.c` now compiles past the `advance_with_locations()` C2143/C2065 diagnostics. Next first stable diagnostics are C2275/C2146/C2065 at line 1028 for late `PyCodeAddressRange bounds` in `advance_with_locations_no_columns`; subsequent diagnostics are separate. Full output: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1790934445-2409488-98d0.log`. +- Regenerated `compat/msvc600/cpython-3.11.16-vc6-headers.patch` from the complete nested diff; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` — exit 0. Scoped diff checks — exit 0. + +Files changed by this slice: `cpython/Objects/codeobject.c`, `scripts/vc6-codeobject-c89.test.py`, regenerated compatibility patch, and this status log. Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution. Next frontier: test-first hoist only the declaration responsible for the first fresh `advance_with_locations_no_columns()` diagnostic around line 1028; preserve statement order and leave later diagnostics separate. + +--- + +## Previous latest revision (`_PyCode_InitAddressRange` locals hoisted; frontier advances in codeobject.c) + +Date: 2026-10-02 05:29 UTC + +Read `PORT_STATUS.md`, `README.md`, and the CPython 3.11.16/Win9x/MSVC6 reference first. Preserved all 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 stale `_blake2/blake2s_impl.c` frontier is cleared; a forced fresh probe confirmed the active frontier remained in `Objects/codeobject.c`. + +One narrow test-first C89 slice in `_PyCode_InitAddressRange`: hoisted only `const char *linetable` and `Py_ssize_t length` into the opening declaration group, preserving their assignment points and assertion → byte-string access → length query → range initialization order. Extended the existing untracked focused regression script. Used one focused Codex coding-agent lane (codex-cli 0.153.4); the CLI was available and ran under `workspace-write`, with no approval override. Its diff was independently inspected. + +Exact verification: + +- RED: `python3 scripts/vc6-codeobject-c89.test.py cpython/Objects/codeobject.c` — exit 1, expected `_PyCode_InitAddressRange entry declaration missing or late: const char *linetable;`. +- GREEN: same command — exit 0, `codeobject C89 declarations and ownership order: PASS`. +- `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 0. +- Focused regression suite `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1. The codeobject check passes; two unrelated existing checks still fail: `console type detection preserves control/cleanup order with C89 locals` and `numeric dispatch helpers preserve slot order with C89 locals` (the latter's embedded check raises `ValueError: substring not found`). No changes made to those tests or source areas. +- 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=80 compiled=79 cached=0 skipped=0`. `codeobject.c` now compiles past the prior `_PyCode_InitAddressRange` diagnostics at lines 825–826. Next first stable blocker: `previous_code_delta()` at `codeobject.c(880)`, C2143 before `const`, with dependent C2065 `ptr` undeclared and illegal-indirection errors at lines 881/884. Later declaration-order blockers remain untouched. Full probe log: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1790918777-2246167-bd0.log`. +- `git -C cpython diff --check -- Objects/codeobject.c` and `git diff --check -- scripts/vc6-codeobject-c89.test.py` — exit 0. + +Files changed in this slice: `cpython/Objects/codeobject.c`, `scripts/vc6-codeobject-c89.test.py`, and this log. The compatibility patch was not regenerated or synchronized. This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution. Next slice: test-first hoist only `previous_code_delta()`'s late `ptr` declaration, preserving the first-entry return before pointer calculation and scan order. + +--- + +## Previous latest revision (`PyCode_Addr2Line` bounds declaration hoisted; frontier advances in codeobject.c) + +Date: 2026-10-02 03:10 UTC + +Preserved all pre-existing outer and detached CPython changes. No reset, clean, stash, discard, commit, push, or publication. CPython remains at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Re-probed rather than assuming the previous `_PyVectorcall_Call` frontier: `_blake2/blake2s_impl.c` and `Objects/call.c` compile; the actual first blocker was the C89 declaration of `PyCodeAddressRange bounds` in `PyCode_Addr2Line` at `Objects/codeobject.c` around line 804 (not `_PyLineTable_NextAddressRange`). + +One narrow C89 slice: moved only the `PyCodeAddressRange bounds` declaration to the opening declaration group of `PyCode_Addr2Line`; `_PyCode_InitAddressRange()` remains at its original execution point after the negative-address and line-array fast paths. Added a focused declaration/order assertion to the existing untracked regression script `scripts/vc6-codeobject-c89.test.py`, retaining all earlier checks. The first test run against the existing source passed its previous checks; after adding the new assertion it failed as expected with `PyCode_Addr2Line bounds must be declared before execution` (exit 1), then passed after the source edit. + +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=80 compiled=79 cached=0 skipped=0`. `_blake2/blake2s_impl.c` and `Objects/call.c` compiled. First blocker was `codeobject.c(804)` C2275/C2146/C2065 on `PyCodeAddressRange bounds`; follow-on errors were C89 declarations in this translation unit. +- RED focused test: `python3 scripts/vc6-codeobject-c89.test.py cpython/Objects/codeobject.c` — exit 1 with the expected assertion above. +- GREEN/focused check: same command — exit 0, `codeobject C89 declarations and ownership order: PASS`. +- `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1. Shell syntax and most regression checks passed, including the new codeobject test. Two unrelated existing failures remain: `console type detection preserves control/cleanup order with C89 locals`, and the numeric-dispatch check raises `ValueError: substring not found`. +- Forced fresh post-change probe (same command) — exit 2, `total=80 compiled=79 cached=0 skipped=0`. `codeobject.c` now compiles past the `PyCode_Addr2Line` bounds diagnostic. The next first diagnostics are at lines 825–826: C2143/C2275/C2065 for late `const char *linetable` and `Py_ssize_t length` in `_PyCode_InitAddressRange`; later independent declaration-order blockers in `codeobject.c` remain untouched. +- `git -C cpython diff --check -- Objects/codeobject.c && git diff --check -- scripts/vc6-codeobject-c89.test.py` — exit 0. + +Files changed in this slice: `cpython/Objects/codeobject.c`, `scripts/vc6-codeobject-c89.test.py`, and this status log. The compatibility patch was not synchronized or regenerated. This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution. Next frontier: test-first hoist only `linetable` and `length` in `_PyCode_InitAddressRange`, preserving the assertion → byte-string access → length query → range initialization order. Pre-existing outer and nested changes remain preserved. + +--- + +## Previous latest revision (`_PyObject_MakeTpCall` result local moved to C89 declaration group; frontier at `_PyVectorcall_Call`) + +Date: 2026-10-02 01:09 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active CPython/Win9x/MSVC6 reference before acting. Preserved all pre-existing outer and detached CPython work. CPython remains at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. No reset, clean, stash, discard, commit, push, or publication. Re-probed instead of assuming the recorded frontier: `_blake2/blake2s_impl.c` compiles, and `Objects/call.c` is now the actual first blocker. + +One narrow C89 slice in `cpython/Objects/call.c`: `_PyObject_MakeTpCall` now assigns `result = NULL` at its original execution point after keyword-dict preparation; its `PyObject *result` declaration is in the opening declaration group. Control flow, result initialization timing, recursive-call guard, and cleanup order are preserved. The existing focused test `scripts/vc6-call-first-c89.test.py` was run before the edit and failed as expected with `_PyObject_MakeTpCall locals must be declared separately from assignment` (exit 1). After the source edit, that regression passed. Its cleanup-order assertion was refined to distinguish the error-path early tuple decref from the post-recursion cleanup; this test-only correction preserves the behavior it intends to check. + +Exact verification: + +- `python3 scripts/vc6-call-first-c89.test.py cpython/Objects/call.c` — exit 0; `call.c first-frontier C89 declarations and dispatch order: PASS`. +- `bash -n scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1. Most applicable regressions pass, including the new `call.c` check, but pre-existing failures remain: `console type detection preserves control/cleanup order` and the numeric-dispatch check's `ValueError: substring not found`. No unrelated fix was attempted. +- Forced fresh compile frontier: `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=76 compiled=75 cached=0 skipped=0`. `call.c` now compiles past the previous `_PyObject_MakeTpCall` diagnostics. First stable remaining diagnostics are in `_PyVectorcall_Call`: C2143 at `call.c(248)` before `type`, followed by dependent C2065 `nargs` at line 252 and C2275/C2059 on late `PyObject *const *args` and `PyObject *kwnames` declarations at lines 256–257. Later errors in this translation unit are distinct follow-on declaration-order blockers and remain untouched. Full compiler log: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1790903149-1934339-c990.log`. +- `git -C cpython diff --check -- Objects/call.c` and `git diff --check -- scripts/vc6-call-first-c89.test.py` — exit 0. + +Files changed in this slice: `cpython/Objects/call.c`, `scripts/vc6-call-first-c89.test.py`, and `PORT_STATUS.md`. The native delegation tool was unavailable because its Anthropic dependency is missing; the scheduled-run Codex CLI command was blocked by the approval guard, so the narrowly scoped change was applied directly and verified independently. Compatibility patch synchronization remains unresolved. This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE test. Next frontier: test-first hoist only `_PyVectorcall_Call`'s `nargs`, `args`, `kwnames`, and `result` locals while preserving tuple access, fast-path, unpack/call/free, and result-check order. + +--- + +## Latest revision (fastsearch C89 blockers cleared; bytearray join is current frontier) + +Fresh forced VC6 frontier probe exited 2 with `total=73 compiled=72 cached=0 skipped=0`. Earlier blockers in `Objects/abstract.c`, `Objects/boolobject.c`, the bytearray Clinic/core helpers, and `Objects/stringlib/fastsearch.h` are now passed by the compiler. Current first diagnostic is `cpython/Objects/bytearrayobject.c(2030)`: C2275/C2065 on the late `PyObject *ret` in `bytearray_join`, after `self->ob_exports++`. + +Next slice: add a regression, declare `ret` at the function's opening, and assign it after the export increment. Preserve the increment → join call → decrement → return sequence. Then immediately rerun the forced frontier; do not assume the 73rd unit is clean until that probe reports it. + +Verification: `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh` and scoped `git diff --check` passed after the latest edits. The full regression script is blocked before execution by the lifecycle guard's script-scan budget exhaustion; the new regression blocks have therefore not been verified by that suite. This is compile-frontier evidence only—no linked interpreter, Wine execution, or Windows 98 SE runtime test. + +The worktree remains dirty and preserved. CPython remains at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The compatibility patch is still unsynchronized/unverified. After the translation-unit frontier clears, remaining gates include rerunning the regressions, regenerating/verifying the compatibility patch against pristine upstream, linking an interpreter, then separate Wine and actual Windows 98 SE runtime validation. No reset, clean, stash, discard, commit, or push was performed. + +--- + +## Latest revision (`multibytecodec.c` C89/type-definition blockers cleared; frontier advances to `_winapi.c`) + +Date: 2026-10-01 19:36 UTC + +Continued the forced VC6 frontier while preserving the dirty outer repo and detached CPython checkout. No reset, clean, stash, discard, commit, push, publication, or compatibility-patch regeneration. CPython remains at `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +Since the previous log entry, the frontier advanced through `_io/textio.c`, `winconsoleio.c`, `_iomodule.c`, CJK codec helpers, and `multibytecodec.c`. Preserved evaluation points and cleanup order while hoisting C89 locals; adjusted Windows offset bounds to the established `PY_LLONG_MIN/MAX` aliases; changed UTF-16 high-surrogate detection to the documented U+D800–U+DBFF range instead of the Vista-era `C3_HIGHSURROGATE` flag; converted C99 designated `PyModuleDef` and `PyType_Spec` initializers using the verified field order; and updated generated Clinic wrappers. Added focused source/order assertions alongside these edits. + +Exact verification: + +- Latest frontier command: `XDG_RUNTIME_DIR="$PWD/build/current-recheck/runtime" WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all bash scripts/vc6-probe-pythoncore-frontier.sh` — exit 2, `total=69 compiled=68 cached=0 skipped=0`. `_winapi.c` now compiles past the missing legacy SDK names (using exact guarded spellings), C89 environment helper declarations, and the Vista-only process attribute APIs. The VC6 branch uses `STARTUPINFOW`, omits `EXTENDED_STARTUPINFO_PRESENT`, and returns `ERROR_CALL_NOT_IMPLEMENTED` if non-None startup attribute lists are requested; the modern branch remains unchanged. The first remaining errors are missing `LCMAP_SORTHANDLE` and `LCMAP_HASH` at `_winapi.c(1827)`, then further C89 declarations in `_winapi_LCMapStringEx_impl`; `LCMapStringEx` itself is Vista-only and still requires a real Win9x-compatible path before link/runtime testing. +- Focused checks for `_winapi` legacy constants, environment helper C89/order behavior, and the VC6 `CreateProcess` branch passed, as did the earlier CJK type/state/module and `_io` declaration/alias/order checks. `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh` and scoped `git diff --check` passed immediately before the latest frontier run. +- `bash scripts/vc6-probe-pythoncore-frontier.test.sh` remains blocked by the lifecycle guard’s script-scan budget exhaustion; it did not run to completion and is not reported as passing. A repo-wide `git diff --check` also reports existing trailing-whitespace lines in the already-dirty `compat/msvc600/cpython-3.11.16-vc6-headers.patch`; that compatibility patch was not edited here. Scoped checks for the files touched in this slice pass. + +Files changed in this work include the `_io` sources and Clinic headers, `Modules/cjkcodecs/cjkcodecs.h`, `multibytecodec.c`, `Modules/_winapi.c`, the frontier regression script, and this log. This remains compile-frontier evidence only: no interpreter has been linked or runtime-tested under Wine or Windows 98 SE. The compatibility patch remains unsynchronized/unverified. Next slice: analyze a Win9x-compatible implementation or explicit unsupported behavior for the `LCMapStringEx` path before changing its constants, then continue the C89 cleanup in that function. + +--- + +## Previous latest revision (`winconsoleio.c` readinto declarations hoisted; frontier advances within translation unit) + +Date: 2026-10-01 18:35 UTC + +Read `PORT_STATUS.md`, `README.md`, and `references/python-3-win9x-msvc6.md`. Preserved all pre-existing changes across the outer repository and detached CPython checkout. No reset, clean, stash, discard, unrelated overwrite, commit, push, or publication. CPython remains at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +The recorded `_channel_is_associated()` frontier and the possibility that `_blake2/blake2s_impl.c` remained the target were stale. A forced fresh probe showed `_blake2/blake2s_impl.c` compiles and the actual first blocker was `cpython/Modules/_io/winconsoleio.c`, in `readinto()` at late declarations for `handle`, `wlen`, `read_len`, `n`, `wbuf`, `err`, and `u8n`. Used one focused Codex coding lane (Codex 0.153.4, logged in). It hoisted these seven locals to the opening declaration group and converted initializers to assignments at their prior execution points. No intended evaluation, control-flow, or cleanup changes. Added an exact-body regression to `scripts/vc6-probe-pythoncore-frontier.test.sh`. + +TDD: the new focused regression was run RED before the source edit (exit 1 with the expected declaration/order assertion), then GREEN after the edit (exit 0). The full regression script was rerun independently after implementation: exit 1. The new `readinto` assertion passes. One existing test, `console type detection preserves control/cleanup order`, still fails; checks of the baseline source confirm this failure predates this slice. `console read preserves surrogate-boundary growth on legacy Windows` passes now and also passed against baseline, so the earlier reported failure was not reproduced. Other applicable assertions pass. Shell syntax and targeted diff checks pass. + +Exact verification: + +- `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=60 compiled=59 cached=0 skipped=0`. `readinto()` now compiles past its C2275/C2146/C2065 diagnostics. Next errors in this translation unit: C2275/C2065 on `wchar_t *tmp` at `winconsoleio.c(842)` in `readall()`, followed by Clinic `Py_ssize_t mode_length` errors at `Modules/_io/clinic/winconsoleio.c.h(75)`; preserve these as separate blockers. +- `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1; the single known type-detection regression above remains; new readinto and surrogate tests pass. +- `git -C cpython diff --check -- Modules/_io/winconsoleio.c && git diff --check -- scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 0. +- Status check showed all pre-existing changes preserved; CPython baseline tag/commit unchanged. `PORT_STATUS.md` was not changed by the coding lane; this entry is the only status-log update for this run. + +Files changed by this slice: `cpython/Modules/_io/winconsoleio.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, and `PORT_STATUS.md`. This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution. The compatibility patch remains unsynchronized/unverified. Next frontier: test-first hoist the late `wchar_t *tmp` declaration in `readall()` while preserving allocation/reallocation/error order; treat generated Clinic declaration errors as a separate subsequent blocker. + +--- + +## Previous latest revision (`_channel_recv` C89 locals hoisted; frontier advances) + +Date: 2026-10-01 16:25 UTC + +Read `PORT_STATUS.md`, `README.md`, and the CPython 3.11.16/Win9x/MSVC6 reference before acting. Preserved all pre-existing outer and nested work. No reset, clean, stash, discard, commit, push, publication, or compatibility-patch regeneration. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +The recorded `_channels_remove()` frontier was stale by this run: fresh forced probe first failed in `_channel_recv()` at line 1382 with C2275/C2146/C2065 on the late `PyThread_type_lock mutex` declaration. Used one focused Codex coding lane after confirming Codex 0.153.4 was installed and authenticated. In `_channel_recv()`, hoisted the five function-scope locals (`interp`, `mutex`, `chan`, `data`, `obj`) into its opening declaration group and converted the inline initializers into assignments at their previous execution points. Lock acquisition/release, null/error handling, cleanup, and return order are preserved. Added one exact-body regression for that function. + +TDD: the test was added and run before editing production source; it failed as expected (exit 1). After the C change, the focused regression passed (exit 0). The subagent also ran the final focused regression and `bash -n`; both exited 0. Subsequent independent verification: + +- `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 0. +- `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1. The new `_channel_recv` regression passed, but the suite then failed in a separately present `_channel_recv/drop/association` test block at `_channel_drop`; that test expects a body change not made in this slice. It appeared during the delegated work and was preserved, not modified or treated as part of this slice. +- Fresh 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, `total=53 compiled=52 cached=0 skipped=0`. `_channel_recv()` now compiles past its previous diagnostics. The next reported first blocker is `_xxsubinterpretersmodule.c(1553)` C2143 before `type`, with dependent C2065 `end` at line 1556 in `_channel_is_associated()`; later independent C89 and type-spelling blockers remain untouched. +- `git -C cpython diff --check -- Modules/_xxsubinterpretersmodule.c` and `git diff --check -- scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 0. + +Files changed by this slice: `cpython/Modules/_xxsubinterpretersmodule.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, and `PORT_STATUS.md`. All pre-existing changes were preserved. This is compiler-probe progress only: no `pythoncore.dll`/`python.exe` link, Wine interpreter execution, or real Windows 98 SE execution. Compatibility patch remains unsynchronized/unverified. Next frontier: separately inspect/test-first hoist `_channel_is_associated()`'s late `_channelend *end` declaration, preserving lookup/error order; address the unrelated suite expectation only in its own deliberate slice. + +--- + +## Latest revision (`_xxsubinterpretersmodule.c` C89 helper declarations advanced; frontier at `_channels_remove`) + +Date: 2026-10-01 16:10 UTC + +Continued the forced VC6 compile frontier at `cpython/Modules/_xxsubinterpretersmodule.c`. Preserved all pre-existing work; no reset, clean, stash, discard, commit, push, publication, or compatibility-patch regeneration. CPython remains detached at `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +C89 declaration-order fixes made in this slice, preserving assignment, lock, cleanup, error, and iteration order: + +- `_copy_raw_string()`: hoisted `copied`; kept allocation after the UTF-8 null check. +- `_sharednsitem_apply()`, `_sharedns_free()`, `_get_shared_ns()`, `_sharedns_apply()`: hoisted locals and loop indices. +- `_sharedexception_bind()`: hoisted outer locals; kept `msg` at the start of its nested block. +- `_channel_close_interpreter()`, `_channels_lookup()`, `_channels_add()`, `_channels_close()`: hoisted locals while retaining lock acquisition, lookup, and result initialization positions. + +Added focused exact-body/order regressions for those helpers. New tests were run RED against the prior code and then GREEN after each relevant edit. Latest focused check run: 13 test blocks passed (four `timemodule.c` checks and nine `_xxsubinterpretersmodule.c` checks). + +Fresh forced frontier command `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` exited 2 with `total=53 compiled=52 cached=0 skipped=0`. The earlier `_copy_raw_string`, shared-namespace, exception, and channel-close errors were cleared. The next first blocker is `_xxsubinterpretersmodule.c(1143)`, C2275 at the late `_channelref *prev` declaration in `_channels_remove()`; line 1144 similarly flags `_channelref *ref`. Later errors in this translation unit include further C89 declaration-order sites; address them from fresh compiler evidence. + +`git diff --check` passed for both modified C files and the regression script. The full regression shell suite and its shell syntax check were not rerun; the lifecycle guard had blocked that script due to scan-budget exhaustion. No interpreter has been linked or run under Wine or Windows 98 SE. The compatibility patch remains unsynchronized/unverified. + +Files changed by this slice: `cpython/Modules/_xxsubinterpretersmodule.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, and this status log. `timemodule.c` changes remain from the preceding slice. + +--- + +## Previous latest revision (Windows pysleep declarations moved before first statement; frontier advances to `_xxsubinterpretersmodule.c`) + +Date: 2026-10-01 15:26 UTC + +Continued the `pysleep()` C89 fix. The preceding attempt hoisted Windows locals only to the `#else` branch, which still followed `assert(timeout >= 0)` in the VC6-preprocessed function. Preserved all existing work; no reset, clean, stash, discard, commit, push, publication, or compatibility-patch regeneration. CPython remains detached at `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +Moved the `MS_WINDOWS`-guarded declarations for `timeout_100ns`, `relative_timeout`, `timer`, `sigint_event`, `events[2]`, and `rc` to the opening declaration group in `pysleep()`, before the assert. Removed the declarations from the later Windows branch; kept assignments and execution order at their prior points. Updated `PY_TIME_SLEEP_TEST` to require the locals before the first statement and the branch body to contain only the existing assignments/control flow. + +TDD and focused checks: the updated sleep regression failed against the prior source with the intended declaration-placement assertion. After the source edit, `PY_TIME_STATE_TEST`, `PY_TIMEZONE_TEST`, `PY_TIMEMODULE_DEF_TEST`, and `PY_TIME_SLEEP_TEST` all passed. + +Fresh forced frontier command `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` exited 2 with `total=53 compiled=52 cached=0 skipped=0`. `timemodule.c` is now past the frontier. The next first blocker is `cpython/Modules/_xxsubinterpretersmodule.c(21)`, C2143 before `type` at `char *copied = PyMem_Malloc(...)` in `_copy_raw_string()`, after the `str == NULL` check. The following errors in that file are likely cascading; inspect the first diagnostic and fix one declaration-order family at a time. + +`git diff --check` passed for the changed `timemodule.c` and regression script. The full regression shell suite and shell syntax check were not rerun for this slice; only the four focused Python blocks and forced frontier were exercised. No linked interpreter, Wine execution, or Windows 98 SE runtime test. Compatibility patch remains unsynchronized/unverified. + +Files changed by this slice: `cpython/Modules/timemodule.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, and this status log. + +--- + +## Previous latest revision (Windows pysleep C89 declarations hoisted; timemodule remains compiler frontier) + +Date: 2026-10-01 14:24 UTC + +Continued from the freshly recorded `timemodule.c` Windows `pysleep()` declaration frontier. Read `PORT_STATUS.md`, `README.md`, and `python-3-win9x-msvc6.md` before work. Preserved all pre-existing outer and nested work; no reset, clean, stash, discard, commit, push, publication, or compatibility-patch regeneration. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +Used one focused Codex coding-agent lane after confirming `codex` is installed (`codex-cli 0.153.4`). The narrow `MS_WINDOWS` branch of `pysleep()` now declares `_PyTime_t timeout_100ns`, `LARGE_INTEGER relative_timeout`, `HANDLE timer`, `HANDLE sigint_event`, `HANDLE events[2]`, and one shared `DWORD rc` in its opening declaration group. Initializers became assignments at the original execution points; event array initialization became two ordered assignments; branch-local `rc` declarations were removed. No API or control-flow change intended. Added an exact-branch regression to `scripts/vc6-probe-pythoncore-frontier.test.sh`. + +TDD: the new regression was first run against the prior source and failed as expected with the declaration-hoisting assertion (exit 1). After the source edit, the focused assertion passed; independent complete frontier regression run passed. + +Exact verification: + +- `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 0; full suite printed `ALL TESTS PASSED`. +- Fresh forced VC6 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=51 compiled=50 cached=0 skipped=0`. `_blake2/blake2s_impl.c` compiles. `timemodule.c` remains blocked at the original first error: C2143 at line 2210 before `type`, now pointing at the hoisted `_PyTime_t timeout_100ns` declaration. The declaration group is still after the earlier executable `assert(timeout >= 0);` at function line 2131, so this move did not satisfy C89's function-scope declaration order. The following C2275/C2146/C2065 errors for Windows locals are cascading; no frontier advancement is claimed. Next narrow fix should place the Win32 declarations before the assertion while preserving the assertion and all initialization points. The prior later `WaitableTimerEx`/API compatibility concerns were not addressed. +- `git diff --check -- scripts/vc6-probe-pythoncore-frontier.test.sh` and `git -C cpython diff --check -- Modules/timemodule.c` — exit 0. +- The overall diff check remains known to fail on pre-existing whitespace in the untouched generated compatibility patch; it was not modified. + +Changed by this slice: `cpython/Modules/timemodule.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, and this log. Existing compatibility patch remains unsynchronized/unverified. This is compiler-probe progress only: no `pythoncore.dll`/`python.exe` link, Wine interpreter execution, or real Windows 98 SE execution. Next step: inspect the exact first fresh diagnostic and preprocessor structure in Windows `pysleep()`; establish why the opening declaration group still fails before choosing any further narrow slice. + +--- + +## Previous latest revision (timemodule PyModuleDef initializer converted; frontier advances to Windows pysleep declarations) + +Date: 2026-10-01 14:19 UTC + +Continued from the `timemodule.c` designated-initializer blocker. Preserved all pre-existing work; no reset, clean, stash, discard, commit, push, publication, or compatibility-patch regeneration. CPython remains detached at `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +In `cpython/Modules/timemodule.c`, converted the `timemodule` `PyModuleDef` from C99 designated fields to positional fields. Verified the order against `Include/moduleobject.h`: `m_base`, `m_name`, `m_doc`, `m_size`, `m_methods`, `m_slots`, `m_traverse`, `m_clear`, `m_free`. Added a regression in `scripts/vc6-probe-pythoncore-frontier.test.sh` that checks the exact initializer field order. + +TDD: the new focused check failed against the designated initializer with the expected field-order assertion, then passed after the source change. Focused `PY_TIME_STATE_TEST`, `PY_TIMEZONE_TEST`, and `PY_TIMEMODULE_DEF_TEST` all passed. + +Verification: + +- Fresh forced frontier command `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=51 compiled=50 cached=0 skipped=0`. The C2059 at `timemodule.c(2108)` is gone. The next first blocker is C2143 at line 2210 in the Windows `pysleep()` branch; further diagnostics concern declarations for `timeout_100ns`, `relative_timeout`, `timer`, `events`, and `rc`. +- Nested and outer `git diff --check` for the changed C source and shell test: exit 0. +- The full regression shell script remains unrun because the lifecycle guard exhausted its script-scan budget. No interpreter has been linked or run under Wine or Windows 98 SE. The compatibility patch remains unsynchronized/unverified after recent edits. + +Files changed by this slice: `cpython/Modules/timemodule.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, and this status log. + +--- + +## Previous latest revision (init_timezone C89 declarations hoisted; frontier advances to PyModuleDef initializer) + +Date: 2026-10-01 14:03 UTC + +Continued from the recorded `timemodule.c` timezone blocker. Preserved all pre-existing work; no reset, clean, stash, discard, commit, push, publication, or compatibility-patch regeneration. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +In `cpython/Modules/timemodule.c`, hoisted `otz0`/`otz1` and Windows-only `TIME_ZONE_INFORMATION tzinfo = {0}` to `init_timezone()`'s opening declaration group. Removed the late declarations without moving `tzset()`, timezone constants, `GetTimeZoneInformation()`, Unicode conversions, or error checks. Added an exact-body regression in `scripts/vc6-probe-pythoncore-frontier.test.sh`. + +TDD: the corrected timezone regression was run against the pre-fix body and failed with the expected hoisting assertion; after the source edit it passed. The existing timemodule state regression also passed. The full regression shell script could not run because the lifecycle guard reported its script-scan budget exhausted; do not infer that full suite or shell syntax check passed. + +Verification: + +- Focused embedded checks `PY_TIME_STATE_TEST` and `PY_TIMEZONE_TEST`: passed. +- Fresh forced frontier command `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=51 compiled=50 cached=0 skipped=0`. The previous errors at `timemodule.c(1752)` and `(1762)` are gone. The next first blocker is C2059 at line 2108 (`.` in the designated `PyModuleDef` initializer). Subsequent diagnostics at lines 2210+ concern declaration order in Windows `pysleep()`. +- `git -C cpython diff --check -- Modules/timemodule.c` and `git diff --check -- scripts/vc6-probe-pythoncore-frontier.test.sh`: exit 0. + +No interpreter has been linked or run under Wine or Windows 98 SE. The compatibility patch remains unsynchronized/unverified after recent edits. Files changed by this slice: `cpython/Modules/timemodule.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, and this status log. + +--- + +## Previous latest revision (timemodule state locals hoisted; frontier advances to Windows timezone declarations) + +Date: 2026-10-01 12:09 UTC + +Read `PORT_STATUS.md`, `README.md`, and `python-3-win9x-msvc6.md` before work. Preserved all pre-existing outer and nested edits; no reset, clean, stash, discard, commit, push, publication, or compatibility-patch regeneration. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The prior recorded Wine socket issue no longer blocked compilation when using the writable workspace-local `XDG_RUNTIME_DIR=build/current-recheck/runtime`. + +The forced fresh probe confirmed the frontier had advanced from `_blake2/blake2s_impl.c` to `timemodule.c`: 50/51 compiled, zero cached. It compiled `_blake2/blake2s_impl.c` successfully. Earliest reported `timemodule.c` errors were C2275/C2065 around `time_module_state *state` at lines 509, 551, 775, and 971. Used exactly one focused Codex coding-agent lane. TDD RED: `bash scripts/vc6-probe-pythoncore-frontier.test.sh > /tmp/timemodule-state-red.log 2>&1` exited 1; new exact-body assertion failed for all four functions. GREEN: same script redirected to `/tmp/timemodule-state-green.log` exited 0 and printed `ALL TESTS PASSED`. The slice hoists only `time_module_state *state` in `time_gmtime`, `time_localtime`, `time_strftime`, and `time_asctime`, with `get_time_state(module)` assignments retained at their prior execution points. Independently inspected the source diff and verified all pre-existing test content remained intact. + +After the edit, a forced fresh probe using `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` exited 2 (`total=51 compiled=50 cached=0 skipped=0`). The four `time_module_state` errors are gone. The next distinct first blocker is `timemodule.c(1752)` C2143 before `type`, followed by C2275/C2146/C2065 for `TIME_ZONE_INFORMATION tzinfo` at 1762 and dependent errors. Later separate diagnostics include C2059 at 2103 and C2143/C2065 at 2205 onward. Left untouched for the next slice. + +Verification: `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` exited 0 (`ALL TESTS PASSED`); `git -C cpython diff --check -- Modules/timemodule.c` and `git diff --check -- scripts/vc6-probe-pythoncore-frontier.test.sh` exited 0. No linked `pythoncore.dll`/`python.exe`, Wine interpreter run, or Windows 98 SE execution. Files changed by this slice: `cpython/Modules/timemodule.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, and this status log. Probe artifacts are under ignored `build/current-recheck/`. Next frontier: separately inspect and test-first address the first timezone-related declaration/compiler family at `timemodule.c` around line 1752; keep subsequent diagnostics separate. + +--- + +## Previous revision (timemodule VC6/C89 ticks/ns declarations hoisted; fresh compiler blocked by Wine runtime directory) + +Date: 2026-10-01 10:08 UTC + +Continued from the recorded `timemodule.c` frontier after reading this status, `README.md`, and the CPython/Win9x/MSVC6 reference. Preserved all pre-existing outer and nested changes; no reset, clean, stash, discard, commit, push, publication, or compatibility-patch regeneration. CPython remains detached at baseline tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Used exactly one focused Codex coding-agent lane. The slice hoisted only `clock_t ticks` and `_PyTime_t ns` into `_PyTime_GetClockWithInfo`'s opening declaration group in `cpython/Modules/timemodule.c`; their initializers became assignments at the original points. No `time_module_state` or subsequent errors were changed. + +TDD: appended an exact-body regression assertion to `scripts/vc6-probe-pythoncore-frontier.test.sh`, checking both declarations and the preserved function sequence. RED was observed before the source edit (exit 1, expected `AssertionError: _PyTime_GetClockWithInfo must hoist only ticks/ns and preserve initialization, info updates, clock/error checks, and conversion order`). GREEN after the source edit exited 0 and printed `ALL TESTS PASSED`. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `env XDG_RUNTIME_DIR="$PWD/build/timemodule-clock-slice/runtime" WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all bash scripts/vc6-probe-pythoncore-frontier.test.sh` (before source edit) | 1; expected RED assertion above. | +| Same command (after source edit) | 0; focused frontier regression suite `ALL TESTS PASSED`. | +| `bash scripts/vc6-probe.test.sh` | 1; Wine bootstrap failure, `wineserver: error creating /run/user/1000/wine/server-801-136fb0/lock: Read-only file system`; compile assertion not reached. | +| `bash scripts/vc6-probe-core.test.sh` | 1; same Wine runtime-directory failure across cases; no compiler diagnostics obtained. | +| `bash scripts/vc6-probe-stdint.test.sh` | 1; same Wine runtime-directory failure before compile. | +| `bash scripts/vc6-probe-pathcch.test.sh` | 1; same Wine runtime-directory failure before compile. | +| `bash scripts/generate-getpath-frozen.test.sh` | 0; all checks passed. | +| `env XDG_RUNTIME_DIR="$PWD/build/timemodule-clock-slice/runtime" WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 1; wineserver failed creating its lock under `/run/user/1000/wine`; probe ended at `cpython/Modules/getpath.c`, `total=1 compiled=0 cached=0 skipped=0`. It never invoked the compiler on the source list and did not reach `timemodule.c`; no new compiler diagnostics can be claimed. | +| `git -C cpython diff --check -- Modules/timemodule.c` and `git diff --check -- scripts/vc6-probe-pythoncore-frontier.test.sh` | 0. Whole nested `git -C cpython diff --check` also exited 0. Whole outer `git diff --check` exited 2 due existing whitespace errors in the pre-existing generated compatibility patch; excluding that file, outer diff check exited 0. Generated patch left unchanged. | + +Independent preservation verification compared entry-snapshot hashes for every pre-existing tracked/untracked file: all existing changes remain intact, and the only changes beyond that snapshot are this slice's `timemodule.c`, appended frontier assertion, and this log. Changed files this run: `cpython/Modules/timemodule.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, and `PORT_STATUS.md`. Probe/test logs are under ignored `build/timemodule-clock-slice/`. + +Compiler frontier advancement is unverified: Wine failed before compilation, so the expected `timemodule.c` diagnostics have not been freshly cleared. This is not a linked `pythoncore.dll`/`python.exe`, Wine interpreter, or Windows 98 SE result. Next action: restore a writable Wine runtime socket directory and rerun the forced-fresh source-list probe; if the two diagnostics clear, separately address the next `time_module_state` declaration family test-first. + +--- + +## Latest revision (_PyTraceMalloc_NewReference declarations; frontier advances) + +Date: 2026-10-01 08:01 UTC + +Read `PORT_STATUS.md`, `README.md`, and `python-3-win9x-msvc6.md` before acting. Preserved every pre-existing outer and nested change; no reset, clean, stash, discard, commit, push, or publication. CPython remains detached at baseline tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. A forced fresh probe confirmed the recorded `_tracemalloc.c` frontier remained current and `_blake2/blake2s_impl.c` still compiles. + +One narrow C89 slice in `cpython/Modules/_tracemalloc.c`: hoisted `uintptr_t ptr`, `PyTypeObject *type`, `int res`, and `trace_t *trace` into `_PyTraceMalloc_NewReference`'s initial declaration group. Assignment points, control flow, and `TABLES_LOCK()`/`TABLES_UNLOCK()` order are unchanged. Added a focused exact-body assertion in `scripts/vc6-probe-pythoncore-frontier.test.sh`. Used the installed Codex CLI as the single focused coding agent after checking its availability; its changes were independently diff-reviewed and re-tested. + +Exact commands/results: + +- `codex exec --sandbox workspace-write -C /home/ubuntu/projects/python-win9x --ephemeral ...` — exit 0. The child first added the test, then ran RED before editing production source: `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1, expected assertion `AssertionError: _PyTraceMalloc_NewReference must hoist ptr/type/res/trace into the initial C89 declaration group`, followed by `FAILED`. +- Child GREEN: `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 0, focused assertion passed and `ALL TESTS PASSED`. +- Independent syntax + regression run: `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 0, all checks passed (`ALL TESTS PASSED`). +- Fresh compiler probe: `XDG_RUNTIME_DIR="$PWD/build/tracemalloc-slice/runtime" WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` — exit 2; `total=51 compiled=50 cached=0 skipped=0`. `_tracemalloc.c` now compiles past the prior `_PyTraceMalloc_NewReference` C89 diagnostics. First blocker advanced to `cpython/Modules/timemodule.c`: C2275/C2146/C2065 at line 164 (`clock_t ticks`), line 171 (`_PyTime_t ns`), and line 507 (`time_module_state state`); further errors occur at lines 549, 773, 969, 1746–1759, 2097, and 2199–2294. These remain untouched for a separate slice. +- `git -C cpython diff --check -- Modules/_tracemalloc.c` and `git diff --check -- scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 0. Full diff inspection confirmed this run's only source/test files changed are `cpython/Modules/_tracemalloc.c` and `scripts/vc6-probe-pythoncore-frontier.test.sh`; all unrelated existing diffs were preserved. + +Files changed by this slice: `cpython/Modules/_tracemalloc.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, and this status log. The generated compatibility patch was deliberately not regenerated; its pre-existing synchronization/drift state remains unresolved. This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution was done. + +Next frontier: one isolated, test-first C89 declaration slice in `cpython/Modules/timemodule.c`, beginning with its earliest diagnostics (`clock_t ticks`, `_PyTime_t ns`, and `time_module_state state`). + +--- + +## Previous revision (_tracemalloc traceback lookup declarations; frontier advances) + +Date: 2026-10-01 05:53 UTC + +Continued from the recorded `_tracemalloc.c` frontier after reading this status, `README.md`, and the CPython/Win9x/MSVC6 reference. Preserved all pre-existing outer and nested changes; no reset, clean, stash, discard, commit, push, or publication. CPython remains at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +One narrow C89 slice in `cpython/Modules/_tracemalloc.c`: hoisted only `trace_t *trace` and `_Py_hashtable_t *traces` into `tracemalloc_get_traceback`'s opening declaration group. The table lookup remains inside the existing `TABLES_LOCK()` region. Added a focused exact-body assertion to `scripts/vc6-probe-pythoncore-frontier.test.sh`. Strict TDD was observed: the focused test first failed with the expected assertion before the source edit (exit 1), then passed after it (exit 0). The coding subagent was the installed authenticated Codex CLI; its diff was independently inspected and verified. + +Exact commands/results: + +- RED: `bash scripts/vc6-probe-pythoncore-frontier.test.sh > /tmp/tracemalloc-tdd/red.log 2>&1` — exit 1, expected `AssertionError: tracemalloc_get_traceback must hoist only trace/traces and preserve guarded, locked lookup order`. +- GREEN: `bash scripts/vc6-probe-pythoncore-frontier.test.sh > /tmp/tracemalloc-tdd/green.log 2>&1` — exit 0, `ALL TESTS PASSED`. Repeated directly after the probe with `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 0, `ALL TESTS PASSED`. `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 0. +- Initial forced probe without a writable Wine runtime directory: exit 1 before compilation (`sock_init: ERROR in sock_check_pollhup()`; wineserver could not create its lock under read-only `/run/user/1000`; total=1, compiled=0). Retried using writable workspace runtime directory: `XDG_RUNTIME_DIR="$PWD/build/tracemalloc-slice/runtime" WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` — exit 2, fresh `total=49 compiled=48 cached=0 skipped=0`. `_tracemalloc.c` now compiles past the prior `tracemalloc_get_traceback` diagnostics (C2275/C2065 at lines 1454/1456 are cleared). First stable remaining errors are separate C89 declarations in `_tracemalloc.c` at lines 1816–1834: C2275/C2146/C2065 (`uintptr_t ptr`), C2275/C2065 (`PyTypeObject type`), C2143 around line 1825, C2275/C2065 (`trace_t trace`), C2223 on `trace->traceback`, and C2065 (`res`). They are left untouched for a separate slice. +- `git -C cpython diff --check` and outer `git diff --check` excluding the generated compatibility patch — exit 0. `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` — exit 1, confirming pre-existing generated-patch drift; the patch was not regenerated or modified by this slice. Baseline/tag unchanged. Existing outer changes to `PORT_STATUS.md`, compatibility files, fixture, and test script were preserved. + +Files changed by this slice: `cpython/Modules/_tracemalloc.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, and this status log. Probe artifacts are under ignored `build/tracemalloc-slice/`. This remains compiler-probe progress only: no `pythoncore`/`python.exe` link, Wine interpreter run, or Windows 98 SE execution. + +Next frontier: separately hoist the C89 locals in `_PyTraceMalloc_Track` (the diagnostics around lines 1816–1834), preserving the existing type-check, traceback allocation, and result/error order. + +--- + +## Previous latest revision (_tracemalloc get-traces declaration slice; VC6 frontier advances) + +Date: 2026-10-01 03:41 UTC + +One isolated C89 slice in `cpython/Modules/_tracemalloc.c`: hoisted only `int err` to the opening declaration group of `_tracemalloc__get_traces_impl`; its assignment remains after `set_reentrant(1)`. Added focused assertions in `scripts/vc6-probe-pythoncore-frontier.test.sh` for declaration placement and traversal/reentrancy/error-check order. Preserved preceding user edits; used one focused Codex coding subagent after the native delegation tool failed because the Anthropic dependency is missing. No reset, clean, stash, discard, commit, push, or compatibility-patch regeneration. Nested HEAD is `41388c9cb160d0886d5ca00d2e6c878260a8a4549`, matching the recorded baseline. + +Exact commands/results: + +- RED: `bash scripts/vc6-probe-pythoncore-frontier.test.sh > build/tracemalloc-slice/red.log 2>&1` — exit 1, `tracemalloc get-traces must declare err before statements for VC6`, followed by `FAILED`, before production edit. +- Prepared workspace-local Wine prefix with `cp -a /home/ubuntu/.wine-win9xport build/tracemalloc-slice/wineprefix` (exit 0). +- Forced probe with the established user prefix: `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` — exit 2, fresh `total=49 compiled=48 cached=0 skipped=0`. `_tracemalloc.c` compiled past the original line-1410/1413 `err` blocker. First stable remaining diagnostics are C2275/C2065 at line 1454 (`trace_t trace`) and line 1456 (`_Py_hashtable_t *traces`) in `tracemalloc_get_traceback`; later mixed declarations remain at lines 1815–1833. This is compiler-probe progress only. +- Focused regressions: `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 0, `ALL TESTS PASSED`. `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` — exit 0; every named suite passed (`ALL TESTS PASSED`). +- `git -C cpython diff --check` and `git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` — exit 0. `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` returned exit 1, confirming existing generated-patch drift; patch left untouched. No rebuild of the patch was attempted. + +Files changed in this slice: `cpython/Modules/_tracemalloc.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, `PORT_STATUS.md`; local verification artifacts in ignored `build/`. Source delta against the entry snapshot is exactly one added declaration and removal of `int` at the original assignment. + +Next frontier: C89-hoist `trace_t *trace` and `_Py_hashtable_t *traces` in `tracemalloc_get_traceback` in a separate test-first slice, preserving the table lookup under the existing lock. Later C89 errors around line 1815 remain separate. The original `err` diagnostic family is cleared by the fresh compiler probe. No linked `pythoncore`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution was performed. + +--- + +## Latest revision (signalmodule Windows wakeup initializer cleared; signalmodule remains blocked) + +Date: 2026-10-01 01:34 UTC + +Continued from the fresh `signalmodule.c` frontier, preserving all existing outer and nested CPython work. No reset, clean, stash, discard, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +One narrow C89 compatibility slice: changed only the `MS_WINDOWS` `wakeup` aggregate initializer in `Modules/signalmodule.c` from designated fields to `{INVALID_FD, 1, 0}`, retaining declared field order and values; non-Windows initializer unchanged. Added a focused test in `scripts/vc6-probe-pythoncore-frontier.test.sh`. The new assertion was verified RED against pristine nested `HEAD` (`git show HEAD:Modules/signalmodule.c` input; assertion failed with the expected message, exit 1), then GREEN with the updated working source as part of the frontier regression suite (exit 0). Native Hermes delegation was unavailable because its Anthropic dependency is absent; used installed Codex CLI in the existing checkout and independently inspected/tested its narrow diff. + +Forced fresh VC6 frontier probe after the edit: exit 2, `total=45 compiled=44 cached=0 skipped=0`. `signalmodule.c` still fails, but the Windows wakeup designated-initializer C2059 at line 118 is gone. Remaining first stable diagnostic is C2079 at `Modules/socketmodule.h(268)` (`bt_rc` uses undefined `SOCKADDR_BTH_REDEF`), followed by signalmodule C89 errors including C2143/C2065 at lines 315/318 (`interp`); numerous further C89/type errors remain. This is compile-probe progress only: no `pythoncore`/`python.exe` link, Wine interpreter run, or Windows 98 SE execution. + +Exact verification: + +| Command | Result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | Exit 2; fresh compile 44/45, zero cached; signalmodule still blocked as above, designated initializer diagnostic cleared. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | Exit 0; all applicable regression suites passed. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | Exit 0; patch matches full nested diff byte-for-byte. | +| Apply refreshed patch to pristine archive of baseline `HEAD`, then compare all changed files | Exit 0; patch applies and all 68 changed CPython files match nested working tree. | +| Nested and outer `git diff --check` | Exit 0. | + +Files changed in this slice: `cpython/Modules/signalmodule.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, regenerated `compat/msvc600/cpython-3.11.16-vc6-headers.patch`, and `PORT_STATUS.md`. All earlier edits remain preserved. Next frontier: investigate the socket-header `SOCKADDR_BTH_REDEF` definition/guard in context, then separately address C89 diagnostics in `signalmodule.c` with their own focused regression slice. + +--- + +## Latest revision (sha256module blockers cleared; sha512module next) + +Date: 2026-10-01 01:14 UTC + +Continued from the verified `sha256module.c` frontier without discarding or resetting any outer or nested work. No commit or push. CPython remains at detached `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +In `Modules/sha256module.c`, changed both SHA224/SHA256 `PyType_Spec` records to declared positional order (including `itemsize = 0`), hoisted `state` and `new` into C89 declaration groups in both constructors while leaving state lookup after buffer acquisition, and changed `_sha256module` to positional `PyModuleDef` fields with an explicit `NULL` doc slot. Added a test-first regression for the type specs, declaration placement/order, and module initializer; it failed before the changes and passed afterward. + +Forced fresh compile-frontier result: exit 2, `total=44 compiled=43 cached=0 skipped=0`. `sha256module.c` now compiles. The next blocker is `cpython/Modules/sha512module.c`: VC6 reports bad numeric suffixes and illegal `long long` at lines 132, 145, and 146. No interpreter has been linked or runtime-tested. + +Verification: `scripts/vc6-probe-pythoncore-frontier.test.sh` passed; `git -C cpython diff --check` and outer diff checks excluding the generated patch passed. The refreshed compatibility patch applies to pristine CPython `v3.11.16` and all 67 changed CPython files match the nested working tree. No commit or push. + +Files changed in this slice: `cpython/Modules/sha256module.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, `compat/msvc600/cpython-3.11.16-vc6-headers.patch`, and this status log. + +--- + +## Latest revision (sha1module blockers cleared; sha256module next) + +Date: 2026-10-01 00:58 UTC + +Continued from the verified `sha1module.c` frontier. Preserved existing outer/nested work; no reset, clean, stash, discard, commit, or push. CPython remains detached at `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +In `Modules/sha1module.c`, changed `SHA1_INT64` to `PY_LONG_LONG` (the VC6 configuration maps this to `__int64`), converted `sha1_type_spec` to positional `PyType_Spec` field order, hoisted `st` into `_sha1_sha1_impl`'s C89 declaration group while retaining assignment after buffer acquisition, and converted `_sha1module` to positional `PyModuleDef` fields with a `NULL` doc slot. Added test-first regression assertions; the test failed before these changes and passed afterward. + +Forced fresh compile-frontier result: exit 2, `total=43 compiled=42 cached=0 skipped=0`. Both `posixmodule.c` and `sha1module.c` now compile. The frontier moved to `cpython/Modules/sha256module.c`: designated-initializer errors at lines 562 and 570, followed by C89 declaration errors at lines 599 and 645, then cascading diagnostics. This is compile-only; no interpreter link or Win9x runtime test. + +Verification: `scripts/vc6-probe-pythoncore-frontier.test.sh` passed with the new SHA1 checks. `git -C cpython diff --check` and outer diff checks excluding the generated patch passed. The refreshed compatibility patch applies to pristine CPython `v3.11.16`; all 66 changed CPython files match the nested working tree. No commit or push. + +Files changed in this slice: `cpython/Modules/sha1module.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, `compat/msvc600/cpython-3.11.16-vc6-headers.patch`, and this status log. + +--- + +## Latest revision (posixmodule C89 and PyModuleDef blockers cleared; sha1module next) + +Date: 2026-10-01 00:49 UTC + +Continued from the verified `posixmodule.c` frontier. Preserved all existing outer and nested changes; no reset, clean, stash, discard, commit, or push. CPython remains at detached `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +In `posixmodule_exec`, hoisted `ScandirIteratorType`, `list`, and `trace` into the opening C89 declaration group, leaving their assignments and traversal order unchanged. Replaced the `posixmodule` C99 designated `PyModuleDef` initializer with positional fields matching `struct PyModuleDef` declaration order in `Include/moduleobject.h`. Added test-first regression assertions for both changes; the new initializer test failed before the conversion and passed after. + +Forced fresh compile-frontier result: exit 2, `total=42 compiled=41 cached=0 skipped=0`. `posixmodule.c` now compiles cleanly. The first blocker moved to `cpython/Modules/sha1module.c`: C2632 at line 36 (`long` followed by `long` is illegal). Later diagnostics in that file include C2059 at line 476 (designated initializer), C2143/C2065 around lines 505–506 (`st`), and C2059 at line 591. These are the next work items; the current first blocker is the 64-bit type spelling at line 36. + +Verification: `scripts/vc6-probe-pythoncore-frontier.test.sh`, `vc6-probe.test.sh`, `vc6-probe-core.test.sh`, `vc6-probe-pathcch.test.sh`, `generate-getpath-frozen.test.sh`, and `vc6-probe-stdint.test.sh` all passed. `git -C cpython diff --check` and outer diff checks excluding the generated patch passed. The generated compatibility patch applies to pristine CPython `v3.11.16` and its 65 changed files match the nested working tree. This remains compile-only: no interpreter link or Win9x runtime test. + +Files changed in this slice: `cpython/Modules/posixmodule.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, `compat/msvc600/cpython-3.11.16-vc6-headers.patch`, and this status log. No commit or push. + +--- + +## Latest revision (VC6 LoadLibrary flag definitions added; posixmodule C89 frontier next) + +Date: 2026-09-30 23:54 UTC + +Read `PORT_STATUS.md`, `README.md`, and the CPython/Win9x/MSVC6 references before editing. Preserved all prior outer and nested modifications; no reset, clean, stash, discard, commit, or push. CPython baseline remains detached at `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +Resolved the missing-identifier compile family in `posixmodule.c`: added `Include/internal/pycore_win9x_loadlibrary.h`, included after `` in `Modules/posixmodule.c` and `Python/dynload_win.c`, and supplied guarded documented values for the five `LOAD_LIBRARY_SEARCH_*` constants when compiling with `_MSC_VER < 1300`. Microsoft documents these flag values for newer Windows loaders; they do not add those loader semantics to Windows 9x (the newer flags require an update even on certain Vista/Windows 7 releases; see https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw). This is compile-time header compatibility only. Runtime loader fallback remains required before claiming a working Win9x interpreter. + +Added a regression check to `scripts/vc6-probe-pythoncore-frontier.test.sh`. Confirmed RED when the compatibility header was absent; after adding the guarded definitions and including them in both consumers, the full frontier regression script passed. + +A forced fresh source-list probe now compiles 39/40 sources with zero cached objects. `posixmodule.c` itself is reached and the five `LOAD_LIBRARY_SEARCH_*` C2065 errors are gone. The probe still exits 2: the first remaining diagnostics are C2275 for `PyObject` and C2065 for `ScandirIteratorType` at line 16174. Further separate C89 errors begin at line 16221 (`list`), line 16225 (`trace`), and line 16247 (`.`). These remain untouched; current compile frontier has moved, not completed. + +Verification: + +| Command | Result | +|---|---| +| `bash -n scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; all checks passed, including the new flag-definition test. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; fresh compile 39/40, zero cached; flag errors cleared; first blocker advanced to C89 diagnostics in `posixmodule.c`. | +| `bash -n` on the frontier/probe/regression scripts, then `vc6-probe.test.sh`, `vc6-probe-core.test.sh`, `vc6-probe-pathcch.test.sh`, `generate-getpath-frozen.test.sh`, `vc6-probe-stdint.test.sh`, and `git diff --check` | 0; all applicable suites and whitespace checks passed. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all bash scripts/vc6-probe-core.sh cpython/Python/dynload_win.c` | 2; direct probe is incomplete because it omits the project `PY3_DLLNAME` define and also encounters C89 diagnostics; no `LOAD_LIBRARY_SEARCH_*` errors appeared. This is not a valid full `dynload_win.c` build result. | +| Generated compatibility patch `compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `git apply --check` and apply to pristine `v3.11.16` archive, then byte-compare all changed files | Passed; all 65 patched files matched the nested working tree. | + +Files changed for this slice: `Include/internal/pycore_win9x_loadlibrary.h`, `Modules/posixmodule.c`, `Python/dynload_win.c`, `scripts/vc6-probe-pythoncore-frontier.test.sh`, and the generated outer compatibility patch. No interpreter was linked or run; no Windows 98 SE execution was attempted. + +--- + +## Latest revision (scheduled forced fresh VC6 frontier recheck; posixmodule remains blocker) + +Date: 2026-09-30 21:18 UTC + +Read `PORT_STATUS.md`, `README.md`, and the CPython/Win9x/MSVC6 reference before acting. Preserved all pre-existing outer and nested changes; no reset, clean, stash, discard, unrelated overwrite, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Re-probed the active frontier rather than resuming the stale `_blake2/blake2s_impl.c` note: that source still compiles, while `posixmodule.c` remains the first blocker. The Win9x-visible contract for the five exported loader-search constants is not established, so no speculative implementation or coding subagent was appropriate. + +Forced fresh probe compiled 39/40 sources (`total=40 compiled=39 cached=0 skipped=0`). First stable diagnostics: C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate later C89 diagnostics begin at 16173 (`ScandirIteratorType`), 16220 (`list`), 16224 (`trace`), and 16246; untouched. + +Files changed this run: `PORT_STATUS.md` only. All prior outer changes and nested CPython changes remain preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match; git status --short --branch; date -u '+%Y-%m-%d %H:%M UTC'` | 0; baseline/tag unchanged, existing edits preserved; timestamp `2026-09-30 21:18 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh compile 39/40, zero cached; first stable errors are five C2065 identifiers above; compiler exit 2, first blocker `cpython/Modules/posixmodule.c`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested and outer whitespace checks passed; patch matches complete nested diff byte-for-byte. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five exported constants, then add a focused failing test before a narrow source change; keep later C89 diagnostics separate. + +--- + +## Latest revision (scheduled forced fresh VC6 frontier recheck; posixmodule remains blocker) + +Date: 2026-09-30 19:13 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before acting. Preserved all existing outer and nested changes; no reset, clean, stash, discard, unrelated overwrite, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Re-probed the actual frontier rather than returning to the earlier `_blake2/blake2s_impl.c` sequence: that source still compiles, and `posixmodule.c` is still the first blocker. No compatibility implementation was safe because the Win9x-visible contract for the five exported loader-search constants remains unestablished; this was diagnostic-only, so no coding subagent was used. + +The forced-fresh probe compiled 39/40 sources (`total=40 compiled=39 cached=0 skipped=0`). First stable errors: C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate later C89 diagnostics begin at line 16173 (`ScandirIteratorType`), then line 16220 (`list`), line 16224 (`trace`), and line 16246; left untouched. The probe returned 2, with `cl.exe exit status: 2` and `first compile blocker: cpython/Modules/posixmodule.c`. + +Files changed this run: `PORT_STATUS.md` only. Existing outer patch/test changes and all nested CPython edits were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `date -u '+%Y-%m-%d %H:%M UTC'; git status --short --branch; git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match` | 0; timestamp `2026-09-30 19:13 UTC`; baseline/tag unchanged and existing modifications preserved. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, zero cached; first diagnostics and later distinct C89 family as above. `_blake2/blake2s_impl.c` compiled successfully. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax and all applicable suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; both diff checks passed and compatibility patch matches the complete nested diff byte-for-byte. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five loader-search constants, then write a focused failing test before a narrow source change. Keep the later C89 diagnostics separate. + +--- + +## Previous latest revision (scheduled forced fresh VC6 frontier recheck; posixmodule remains blocker) + +Date: 2026-09-30 15:05 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile legacy-toolchain reference before acting. Preserved all existing outer and nested changes; no reset, clean, stash, discard, unrelated overwrite, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Re-probed rather than assuming the earlier `_blake2/blake2s_impl.c` note was the current frontier: it still compiles, while `posixmodule.c` remains the first blocker. Since the unresolved loader-search constants are still the first error family and their intended Win9x-visible exported API behavior is not established, this run made no speculative source/test change and did not delegate implementation. + +The forced fresh probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`). First stable errors are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Distinct later C89 diagnostics begin at line 16173 (`ScandirIteratorType`), then 16220 (`list`), 16224 (`trace`), and 16246; none changed. + +Files changed this run: `PORT_STATUS.md` only. Pre-existing outer compatibility patch/test edits and all nested CPython changes were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match; date -u '+%Y-%m-%d %H:%M UTC'; git status --short --branch` | 0; nested modifications preserved; baseline/tag unchanged; timestamp `2026-09-30 15:05 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; fresh compile-only probe compiled 39/40 with zero cached sources; first stable errors are the five C2065 constants above; subsequent C89 diagnostics remain distinct. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax checks and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check; cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested and outer diff checks passed and compatibility patch byte-matches the complete nested diff. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants, then add a focused failing test before a narrow implementation; keep the later C89 blockers separate. + +--- + +The forced fresh probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`). `_blake2/blake2s_impl.c` compiled successfully. First stable errors: C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate subsequent C89 errors begin at line 16173 (`ScandirIteratorType`), line 16220 (`list`), line 16224 (`trace`), and line 16246; these remain untouched. + +Files changed this run: `PORT_STATUS.md` only. All pre-existing outer and nested CPython changes were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && date -u '+%Y-%m-%d %H:%M UTC'` | 0; nested modifications preserved; baseline/tag unchanged; `2026-09-30 13:01 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh probe compiled 39/40 with zero cached sources. First stable diagnostics are the five C2065 constants above; later C89 diagnostics remain distinct. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed (`ALL TESTS PASSED`). | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution was verified. Next frontier: establish authoritative Win9x behavior for the five exported loader-search constants, then add a focused failing test before a narrow implementation change. Keep the later C89 diagnostics separate. + +--- + +Date: 2026-09-30 04:45 UTC + +Read `PORT_STATUS.md`, `README.md`, and the legacy-toolchain reference before probing. Preserved all pre-existing outer and nested modifications; no reset, clean, stash, discard, unrelated overwrite, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Forced fresh probe confirms `_blake2/blake2s_impl.c` still compiles and is not the active frontier. The first blocker remains five undeclared `LOAD_LIBRARY_SEARCH_*` constants in `posixmodule.c`; the Win9x-visible API contract remains unresolved, so no speculative implementation or coding subagent was appropriate. + +Probe compiled 39/40 sources (`total=40 compiled=39 cached=0 skipped=0`). First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Distinct later C89 diagnostics begin at line 16173 (`ScandirIteratorType`), then line 16220 (`list`), line 16224 (`trace`), and line 16246. No production source or regression test was changed. + +Files changed this run: `PORT_STATUS.md` only. Existing outer compatibility patch/test modifications and all nested CPython modifications were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch; git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match; date -u '+%Y-%m-%d %H:%M UTC'` | 0; pre-existing modifications preserved; baseline/tag unchanged; `2026-09-30 04:45 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; fresh run compiled 39/40 with zero cached sources. First stable errors are the five C2065 constants above; later distinct C89 diagnostics remain. Full output: `/home/ubuntu/.hermes/profiles/maxwell/cache/scratch/frontier-current.log`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check; cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested/outer diff checks passed and generated patch byte-matches the full nested diff. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: resolve authoritative intended Win9x behavior for the five exported constants, then add a focused failing test before a narrow change. Keep later C89 diagnostics separate. + +--- + +## Latest revision (forced fresh frontier recheck; posixmodule loader constants remain blocker) + +Date: 2026-09-30 00:40 UTC + +Read `PORT_STATUS.md`, `README.md`, and `python-3-win9x-msvc6.md` before probing. Preserved all pre-existing outer and nested modifications; no reset, clean, stash, discard, unrelated overwrite, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The recorded `_blake2/blake2s_impl.c` state remains true, but is not the active frontier. Forced fresh probe reconfirmed `posixmodule.c` as the first blocker. This run was diagnostic-only: the intended Win9x-visible contract for the five exported loader-search constants is still unresolved, so no compatibility source/test edit or coding subagent was appropriate. + +The forced fresh probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate later C89 diagnostics begin at line 16173 (`ScandirIteratorType`), then line 16220 (`list`), 16224 (`trace`), and line 16246; left untouched. + +Files changed this run: `PORT_STATUS.md` only. Existing outer patch/test edits and all nested CPython changes were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `date -u '+%Y-%m-%d %H:%M UTC'; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match; git -C cpython status --short --branch` | 0; timestamp `2026-09-30 00:40 UTC`; baseline/tag unchanged; existing nested edits preserved. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh probe compiled 39/40 with zero cached sources; exact first stable errors are the five C2065 constants above; later distinct C89 diagnostics remain. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'; git -C cpython diff --check; cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; outer/nested whitespace checks passed and tracked compatibility patch byte-matches the complete nested diff. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants; then add a focused failing test before a narrow change. Keep later C89 diagnostics separate. + +--- + +## Latest revision (forced fresh VC6 frontier recheck; loader-search constants remain first blocker) + +Date: 2026-09-29 22:33 UTC + +Read `PORT_STATUS.md`, `README.md`, and the CPython 3.11.16/Win9x/MSVC6 reference before probing. Preserved all existing outer and nested modifications; no reset, clean, stash, discard, unrelated overwrite, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The recorded `_blake2/blake2s_impl.c` state remains true, but it is not the active frontier. Forced fresh probe reconfirmed `posixmodule.c` as first blocker, so this run was diagnostic-only; the Win9x API contract for its five exported loader-search constants remains unresolved. No implementation subagent or source/test edit was appropriate. + +The fresh probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics: C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate later C89 diagnostics begin at line 16173 (`ScandirIteratorType`), followed by line 16220 (`list`), 16224 (`trace`), and line 16246; left untouched. + +Files changed this run: `PORT_STATUS.md` only. Existing outer patch/test edits and all nested CPython changes were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match; git -C cpython status --short --branch; date -u '+%Y-%m-%d %H:%M UTC'` | 0; existing modifications preserved; CPython baseline/tag unchanged; timestamp `2026-09-29 22:33 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh probe compiled 39/40, zero cached; exact first errors are the five C2065 loader-search constants above; later distinct C89 diagnostics remain. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax checks and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested/outer diff checks passed; generated compatibility patch matches the complete nested diff byte-for-byte. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants; then add a focused failing test before any source edit. Keep later C89 diagnostics separate. + +--- + +## Previous revision (scheduled forced fresh VC6 frontier recheck; loader-search constants remain first blocker) + +Date: 2026-09-29 20:29 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython 3.11.16/Win9x/MSVC6 reference before probing. Preserved all existing outer and nested modifications; no reset, clean, stash, discard, unrelated overwrite, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. `_blake2/blake2s_impl.c` still compiles and is not the active frontier. A forced fresh probe reconfirmed `posixmodule.c` as the first blocker. The Win9x API contract for its five exported loader-search constants remains unresolved, so this run was diagnostic-only; no implementation subagent or source/test edit was appropriate. + +The probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate later C89 diagnostics begin at line 16173 (`ScandirIteratorType`), followed by line 16220 (`list`), 16224 (`trace`), and line 16246; left untouched. + +Files changed this run: `PORT_STATUS.md` only. Existing outer patch/test edits and all nested CPython changes were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch; git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match; date -u '+%Y-%m-%d %H:%M UTC'` | 0; existing outer and nested edits preserved; baseline/tag unchanged; timestamp `2026-09-29 20:29 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh probe compiled 39/40 with zero cached sources. First stable errors are the five C2065 loader-search constants listed above; later distinct C89 errors remain. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression tests and shell syntax checks passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested and outer diff checks passed; generated compatibility patch matches the complete nested diff byte-for-byte. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants; then add a focused failing test before any source edit. Keep later C89 diagnostics separate. + +--- + +## Previous revision (scheduled forced fresh VC6 frontier recheck; loader-search constants remain first blocker) + +Date: 2026-09-29 18:25 UTC + +Read `PORT_STATUS.md`, `README.md`, and the CPython 3.11.16/Win9x/MSVC6 reference before probing. Preserved all pre-existing outer and nested modifications; no reset, clean, stash, discard, unrelated overwrite, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. `_blake2/blake2s_impl.c` still compiles and is not the active frontier. A forced fresh probe reconfirmed `posixmodule.c` as first blocker. The Win9x API contract for its five exported loader-search constants remains unresolved, so this was diagnostic-only; no implementation subagent or source/test edit was appropriate. + +The probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate later C89 diagnostics begin at line 16173 (`ScandirIteratorType`), followed by line 16220 (`list`), 16224 (`trace`), and line 16246; left untouched. + +Files changed this run: `PORT_STATUS.md` only. Existing outer patch/test edits and all nested CPython changes were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch; git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match; date -u '+%Y-%m-%d %H:%M UTC'` | 0; existing outer and nested edits preserved; baseline/tag unchanged; timestamp `2026-09-29 18:25 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh probe compiled 39/40 with zero cached sources. First stable errors are the five C2065 loader-search constants listed above; later distinct C89 errors remain. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression tests and shell syntax checks passed (`ALL TESTS PASSED`). | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants; then add a focused failing test before any source edit. Keep later C89 diagnostics separate. + +--- + +## Previous revision (scheduled forced fresh VC6 frontier recheck; loader-search constants remain first blocker) + +Read `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before probing. Preserved all pre-existing outer and nested modifications; no reset, clean, stash, discard, unrelated overwrite, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. `_blake2/blake2s_impl.c` compiles, but is not the active frontier. The forced fresh probe reconfirmed `posixmodule.c` as the first blocker. The intended Win9x API contract for the five exported loader-search constants remains unresolved, so no source/test edit or implementation subagent was appropriate. + +The forced fresh probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable errors: C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate later C89 diagnostics start at line 16173 (`ScandirIteratorType`), followed by errors at 16220 (`list`), 16224 (`trace`), and 16246; left untouched. + +Files changed this run: `PORT_STATUS.md` only. Existing outer compatibility-patch and frontier-test changes and all nested CPython modifications were preserved. + +Exact commands/results: + +|| Command | Exit/result | +||---|---| +|| `git status --short --branch; git rev-parse HEAD; git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match; date -u '+%Y-%m-%d %H:%M UTC'` | 0; existing changes preserved; CPython baseline/tag unchanged; timestamp `2026-09-29 16:20 UTC`. | +|| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh probe compiled 39/40, zero cached; exact first errors are the five C2065 loader-search constants above; separate C89 errors follow. | +|| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable syntax checks and regression suites passed (`ALL TESTS PASSED`). | +|| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested and outer diff checks passed; generated compatibility patch matches the complete nested diff byte-for-byte. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants, then add a focused failing test before any source edit. Keep later C89 diagnostics separate. + +--- + +--- + +## Previous revision (scheduled forced fresh VC6 frontier recheck; loader-search constants remain first blocker) + +Date: 2026-09-29 10:08 UTC + +Read `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before probing. Preserved all existing outer and nested changes. No reset, clean, stash, discard, overwrite of unrelated work, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. `_blake2/blake2s_impl.c` still compiles but is not the active frontier. The forced fresh source-list probe reconfirmed `posixmodule.c` as the first blocker; the intended Win9x API contract for the five exported loader-search constants is unresolved, so this was diagnostic-only and no coding subagent or implementation change was appropriate. + +The probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics: C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate subsequent C89 diagnostics start at line 16173 (`ScandirIteratorType`), followed by errors at 16220 (`list`), 16224 (`trace`), and 16246; untouched. + +Files changed this run: `PORT_STATUS.md` only. Existing compatibility-patch edits, frontier-test edits, and the extensive nested CPython changes were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch; git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match` | 0; pre-existing outer and nested changes preserved; baseline/tag unchanged (`v3.11.16`, `41388c9cb160d0886d5ca00d2e6c878260a8a4549`). | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; fresh forced probe compiled 39/40, zero cached; first errors are the five C2065 loader-search constants above; distinct C89 diagnostics follow. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and shell syntax checks passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check; cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; git diff --check -- . ":(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch"` | 0; nested/outer whitespace checks passed; compatibility patch matches complete nested diff byte-for-byte. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants, then add a focused failing test before any source edit. Keep later C89 blockers separate. + +--- + +## Latest revision (scheduled forced fresh VC6 frontier recheck; loader-search constants remain first blocker) + +Date: 2026-09-29 08:00 UTC + +Read `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before probing. Preserved all existing outer and nested changes. No reset, clean, stash, discard, overwrite of unrelated work, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. `_blake2/blake2s_impl.c` remains successful but is not the active frontier. Forced fresh probe reconfirmed the first blocker in `posixmodule.c`; the Win9x API contract for the five exported loader-search constants is unresolved, so this was diagnostic-only and no coding subagent or implementation change was appropriate. + +The probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate subsequent C89 diagnostics start at line 16173 (`ScandirIteratorType`), followed by errors at 16220 (`list`), 16224 (`trace`), and 16246; untouched. + +Files changed this run: `PORT_STATUS.md` only. Existing compatibility patch changes, frontier test edits, and nested CPython changes were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && git -C cpython status --short --branch && date -u '+%Y-%m-%d %H:%M UTC'` | 0; baseline/tag unchanged; existing nested modifications preserved; `2026-09-29 08:00 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; fresh forced probe compiled 39/40, zero cached; first diagnostics are the five C2065 loader-search constants above; separate C89 diagnostics follow. Full log: `/home/ubuntu/.hermes/profiles/maxwell/cache/scratch/python-win9x-frontier-20260929-0800.log`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and syntax checks passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested and outer diff checks passed; generated compatibility patch matches the complete nested diff byte-for-byte. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants; then write a focused failing test before any source edit. Keep later C89 blockers separate. + +--- + +## Latest revision (scheduled forced fresh VC6 frontier recheck; loader-search constants remain first blocker) + +Date: 2026-09-29 05:56 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before probing. Preserved all existing outer and nested changes; no reset, clean, stash, discard, commit, push, or publication. CPython remains detached at `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The `_blake2/blake2s_impl.c` compile state remains true but is not the active frontier. Forced fresh probe reconfirmed `posixmodule.c` as first blocker; the Win9x API contract of the exported loader-search constants remains unresolved, so this run was diagnostic-only and no coding subagent was used. + +The probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable errors: C2065 at `posixmodule.c(15856)`–`(15860)` for all five `LOAD_LIBRARY_SEARCH_*` constants (DEFAULT_DIRS, APPLICATION_DIR, SYSTEM32, USER_DIRS, DLL_LOAD_DIR). Distinct subsequent C89 diagnostics start at line 16173 (`ScandirIteratorType`), followed by errors at 16220, 16224, and 16246; not addressed. + +Files changed this run: `PORT_STATUS.md` only. Existing compatibility patch, regression script edits, and nested CPython changes were preserved. + +Exact commands/results: + +|| Command | Exit/result | +||---|---| +|| `git status --short --branch && git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && date -u '+%Y-%m-%d %H:%M UTC'` | 0; pre-existing changes preserved; baseline/tag unchanged; 2026-09-29 05:56 UTC. | +|| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh probe compiled 39/40, zero cached; first errors are five C2065 loader-search constants; later distinct C89 errors remain. | +|| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed (`ALL TESTS PASSED`). | +|| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested/outer diff checks passed and generated compatibility patch byte-matches complete nested diff. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior of the five exported loader-search constants; then write a focused failing test before any source edit. Keep later C89 blockers separate. + +--- + +## Latest revision (scheduled forced fresh VC6 frontier recheck; loader-search constants remain first blocker) + +Date: 2026-09-29 03:52 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile legacy-toolchain reference before probing. Preserved all existing outer and nested changes; no reset, clean, stash, discard, commit, push, or publication. CPython remains detached at `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The recorded `_blake2/blake2s_impl.c` state remains true, but is not the current frontier. Fresh forced probe reconfirmed `posixmodule.c` as first blocker; no implementation change was safe because exported loader-search constants' Win9x API contract remains unresolved. Diagnostic-only; no coding subagent used. + +Probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable errors are C2065 at `posixmodule.c(15856)`–`(15860)` for all five `LOAD_LIBRARY_SEARCH_*` constants. Distinct later C89 errors start at line 16173 (`ScandirIteratorType`), followed by errors at 16220, 16224, and 16246. No source/test changes were made. + +Files changed this run: `PORT_STATUS.md` only. Existing compatibility patch, regression script, and nested CPython edits preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && git -C cpython status --short --branch && date -u '+%Y-%m-%d %H:%M UTC'` | 0; baseline/tag unchanged; existing nested changes preserved; 2026-09-29 03:52 UTC. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, zero cached; first errors are five C2065 constants above. Full log: `/home/ubuntu/.hermes/profiles/maxwell/cache/scratch/python-win9x-frontier-current.log`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable suites passed (`ALL TESTS PASSED`). | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine execution, or Windows 98 SE execution verified. Next frontier: establish intended Win9x behavior of the five exported loader-search constants using authoritative API evidence; then write a focused failing test before any source edit. Keep later C89 blockers separate. + +--- + +## Latest revision (scheduled forced VC6 frontier recheck; posixmodule remains first blocker) + +Date: 2026-09-29 01:51 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before probing. Preserved all pre-existing outer and nested edits; no reset, clean, stash, discard, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The recorded `_blake2/blake2s_impl.c` state is still true, but it is not the active frontier. A forced fresh probe reconfirmed the first blocker in `posixmodule.c`; the intended Win9x-visible contract for its five exported loader-search constants remains unresolved, so this run was diagnostic-only. No coding subagent was used because implementation was not appropriate. + +The fresh probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15859)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, and `LOAD_LIBRARY_SEARCH_USER_DIRS`. The recorded fifth constant (`LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`) did not appear in this probe output. Separate later C89 diagnostics begin at `posixmodule.c(16173)` (`ScandirIteratorType`), then at 16220 (`list`) and 16224 (`trace`), with cascading diagnostics; untouched. No source or test files were changed. + +Files changed this run: `PORT_STATUS.md` only. Existing compatibility patch, regression-test edits, and nested CPython changes were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch; git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match` | 0; existing modifications preserved; CPython baseline remains `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh probe; 39/40 compiled, zero cached; `_blake2/blake2s_impl.c` compiled. First reported errors are four C2065 loader-search constants at `posixmodule.c(15856)`–`(15859)`; distinct C89 errors follow. Full log: `/home/ubuntu/.hermes/profiles/maxwell/cache/scratch/python-win9x-frontier.1E0y6P.log`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check; cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested/outer whitespace checks passed; generated patch byte-matches the complete nested diff. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: investigate why `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR` was not reported and establish authoritative intended Win9x behavior for all five exported constants; then write a focused failing test before any source edit. Keep later C89 diagnostics separate. + +--- + +## Latest revision (scheduled fresh forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-28 23:47 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before probing. Preserved all pre-existing outer and nested changes; no reset, clean, stash, discard, overwrite, commit, push, or publication. CPython remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The `_blake2/blake2s_impl.c` sequence remains compiling, but is not the active frontier. A forced fresh probe reconfirmed the first blocker in `posixmodule.c`; no implementation slice was safe because the Win9x-visible contract for its five exported loader-search constants remains unresolved. Diagnostic-only; no coding subagent was used. + +The forced probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate later C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), followed by `list` at 16220 and `trace` at 16224, with cascading errors; untouched. + +Files changed this run: `PORT_STATUS.md` only. Existing outer modifications and nested CPython edits were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; pre-existing modifications preserved; baseline `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh probe compiled 39/40, zero cached; first stable diagnostics are the five C2065 constants above; separate C89 diagnostics follow. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and shell syntax checks passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested and outer diff checks passed; generated compatibility patch matched the complete nested diff byte-for-byte. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants; then write a focused failing test before any source change. Keep later C89 diagnostics distinct. + +--- + +## Previous revision (scheduled fresh forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-28 21:43 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before probing. Preserved all pre-existing outer and nested changes; no reset, clean, stash, discard, overwrite, commit, push, or publication. CPython remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The `_blake2/blake2s_impl.c` sequence remains compiling but is not the active frontier. A forced fresh probe reconfirmed the first blocker in `posixmodule.c`; no safe implementation slice was established because the intended Win9x-visible contract for its five exported loader-search constants remains unresolved. This was diagnostic-only; no coding subagent was used because implementation was not appropriate. + +The fresh probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics: C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate later C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), then line 16220 (`list`) and line 16224 (`trace`), followed by cascading diagnostics; left untouched. + +Files changed this run: `PORT_STATUS.md` only. Existing outer modifications and nested CPython edits were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git rev-parse HEAD && git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; preserved existing modifications; CPython baseline `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh probe compiled 39/40, zero cached; first stable errors are the five C2065 constants above; distinct later C89 errors remain. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and syntax checks passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested and outer diff checks passed; generated compatibility patch matched the complete nested diff byte-for-byte. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants; then write a focused failing test before any source change. Keep later C89 diagnostics distinct. + +--- + +## Previous revision (fresh forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-28 17:32 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before probing. Preserved all pre-existing outer and nested changes; no reset, clean, stash, discard, overwrite of unrelated work, commit, push, or publication. CPython remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The `_blake2/blake2s_impl.c` sequence remains compiling but is not the active frontier. The forced fresh probe reconfirmed the recorded first blocker in `posixmodule.c`; no safe implementation slice was established because the intended Win9x-visible contract for its five exported loader-search constants remains unresolved. No coding subagent was used because implementation was not appropriate for this diagnostic-only run. + +The fresh probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics: C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Subsequent separate C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), then line 16220 (`list`) and 16224 (`trace`), followed by related cascading diagnostics; untouched. + +Files changed this run: `PORT_STATUS.md` only. All pre-existing outer changes and nested CPython edits were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && date -u '+%Y-%m-%d %H:%M UTC'` | 0; baseline `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`; existing nested modifications preserved; timestamp `2026-09-28 17:32 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh probe compiled 39/40, zero cached; first stable errors are the five C2065 constants above; later distinct C89 errors remain. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax checks and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested and outer whitespace checks passed; generated compatibility patch matched the complete nested diff byte-for-byte. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants; then add a focused failing test before any source change. Keep later C89 diagnostics separate. + +--- + +## Latest revision (fresh forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-28 15:30 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before probing. Preserved all pre-existing outer and nested modifications; no reset, clean, stash, discard, overwrite of unrelated work, commit, push, or publication. CPython baseline remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The `_blake2/blake2s_impl.c` sequence is still compiling and is not the active frontier. A forced fresh probe reconfirmed the recorded first blocker in `posixmodule.c`; no safe implementation slice was established because the intended Win9x-visible contract for its five exported loader-search constants remains unresolved. No coding subagent was used because implementation was not appropriate for this diagnostic-only run. + +The fresh probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate subsequent C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), then line 16220 (`list`) and 16224 (`trace`); untouched. + +Files changed this run: `PORT_STATUS.md` only. All pre-existing outer changes and nested CPython edits were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && git -C cpython status --short --branch && date -u '+%Y-%m-%d %H:%M UTC'` | 0; baseline `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`; existing nested changes preserved; timestamp `2026-09-28 15:28 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh probe compiled 39/40, zero cached; first stable errors are the five C2065 constants above; later distinct C89 errors remain. Full log: `/home/ubuntu/.hermes/profiles/maxwell/cache/scratch/python-win9x-frontier.log`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax checks and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch' && git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0 before the status update; outer diff clean, nested diff clean, generated compatibility patch matched the complete nested diff byte-for-byte. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants; then add a focused failing test before any source change. Keep later C89 diagnostics separate. + +--- + +## Previous revision (fresh forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-28 13:27 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before probing. Preserved all existing outer and nested modifications; no reset, clean, stash, discard, overwrite of unrelated work, commit, push, or publication. CPython baseline remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The requested `_blake2/blake2s_impl.c` sequence remains compilable and is not the active frontier. The forced fresh source-list probe reconfirmed the active blocker in `posixmodule.c`; because the intended Win9x-visible contract for its five exported loader-search constants remains unresolved, this was diagnostic-only. No source/test change or coding subagent was appropriate. + +The probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Distinct subsequent VC6/C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), then line 16220 (`list`) and 16224 (`trace`); not addressed. + +Files changed this run: `PORT_STATUS.md` only. All pre-existing outer changes and nested CPython edits were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && git -C cpython status --short --branch && date -u '+%Y-%m-%d %H:%M UTC'` | 0; baseline `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`; existing nested modifications preserved; timestamp `2026-09-28 13:27 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, zero cached; first stable diagnostics are the five C2065 constants above; later distinct C89 diagnostics remain. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax checks and all applicable regression suites passed (`ALL TESTS PASSED`). | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants, then write a focused failing test before source changes. Keep the later C89 diagnostics separate. + +--- + +Date: 2026-09-28 11:23 UTC + +Read `PORT_STATUS.md`, `README.md`, and `skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before work. Preserved all pre-existing outer and nested changes; no reset, clean, stash, discard, overwrite of unrelated changes, commit, push, or publication. CPython baseline is tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The requested `_blake2/blake2s_impl.c` sequence still compiles but is not the active frontier. The forced source-list probe confirms the first blocker remains in `posixmodule.c`; its exported loader-search constant contract is still unresolved, so this was a diagnostic-only run and no coding subagent or speculative source/test edit was appropriate. + +The fresh forced probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate subsequent C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), then at 16220 (`list`) and 16224 (`trace`); they remain distinct and untouched. + +Files changed this run: `PORT_STATUS.md` only. Existing compatibility patch, regression-test edits, and nested CPython changes were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; pre-existing modifications preserved; baseline `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, zero cached; first stable diagnostics are the five C2065 constants above; later C89 diagnostics remain distinct. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax checks and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; nested diff clean; generated compatibility patch byte-matches complete nested diff. | +| `git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; outer non-generated diff check passed. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants, then add a focused failing test before source changes. Keep the later C89 diagnostics separate. + +--- + +## Latest revision (scheduled forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-28 09:18 UTC + +Read `PORT_STATUS.md`, `README.md`, and `skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before work. Preserved all pre-existing outer and nested changes; no reset, clean, stash, discard, overwrite of unrelated changes, commit, push, or publication. CPython baseline remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The `_blake2/blake2s_impl.c` state was force-checked and remains successful, but is not the active frontier. The first blocker is still the five loader-search identifiers in `posixmodule.c`; no implementation slice was safe because the Win9x-visible exported API contract remains unresolved. + +Forced fresh source-list probe compiled 39/40 sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics: C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later distinct C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), with subsequent errors at 16220 (`list`) and 16224 (`trace`). No source/test changes or coding-agent delegation were appropriate for this diagnostic-only run. + +Files changed this run: `PORT_STATUS.md` only. Existing compatibility patch, regression-test edits, and nested CPython changes were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match` | 0; pre-existing modifications preserved; baseline `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, zero cached; first stable errors are the five C2065 constants above; distinct later C89 diagnostics remain. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax checks and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; nested diff clean; generated compatibility patch byte-matches complete nested diff. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish the intended Win9x-visible contract for the five exported loader-search constants from authoritative API evidence, then add a focused failing test before source changes; leave later C89 diagnostics distinct. + +--- + +## Previous revision (scheduled forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-28 05:09 UTC + +Read `PORT_STATUS.md`, `README.md`, and `skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before acting. Preserved existing outer and nested changes; no reset, clean, stash, discard, overwrite of unrelated work, commit, push, or publication. CPython baseline remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The recorded `_blake2/blake2s_impl.c` state was freshly confirmed but is not the active frontier. The active first blocker remains five missing loader-search constants in `posixmodule.c`; because these are exported `os` constants and the Win9x-visible contract remains unresolved, made no speculative source/test edits and did not delegate implementation. + +Forced fresh VC6 source-list probe compiled 39/40 sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later distinct C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), followed by line 16220 (`list`) and 16224 (`trace`); not addressed. + +Files changed this run: `PORT_STATUS.md` only. Existing compatibility patch, regression tests, and nested CPython edits remain preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; pre-existing modifications preserved; baseline `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled; first stable diagnostics are the five C2065 errors listed above; subsequent C89 diagnostics remain distinct. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax checks and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested diff clean; generated compatibility patch byte-matches the complete nested diff; outer non-generated diff clean. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish the intended Win9x-visible contract for the five exported loader-search constants from authoritative API evidence, then add a focused failing test before source changes. Keep later C89 diagnostics separate. + +--- + +## Previous revision (scheduled forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-28 03:02 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile legacy-toolchain reference before acting. Preserved all outer and nested changes; no reset, clean, stash, discard, commit, push, or publication. CPython remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The recorded `_blake2/blake2s_impl.c` state remains true but is not the active frontier. A forced fresh probe confirms the first blocker remains five missing `LOAD_LIBRARY_SEARCH_*` identifiers in `posixmodule.c`; no API-contract decision or source/test edit was made, and no coding subagent was appropriate for diagnostic-only work. + +Probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics: C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate later C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), with subsequent errors at 16220 (`list`) and 16224 (`trace`). + +Files changed this run: `PORT_STATUS.md` only. Existing generated compatibility patch, frontier regression test, and nested CPython edits were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch; git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match; date -u '+%Y-%m-%d %H:%M UTC'` | 0; pre-existing changes preserved; baseline `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`; timestamp `2026-09-28 03:02 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, `_blake2/blake2s_impl.c` compiled; first stable diagnostics are the five C2065 errors listed above; distinct C89 errors follow. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check; cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | Each returned 0; nested diff clean, generated compatibility patch matches complete nested diff byte-for-byte, outer non-generated diff clean. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish the intended Win9x-visible contract for the five exported loader-search constants from authoritative API evidence, then add a focused failing test before source changes. Keep the later C89 diagnostics separate. + +--- + +## Previous revision (scheduled forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-28 00:00 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile legacy-toolchain reference before acting. Preserved all pre-existing outer and nested modifications; no reset, clean, stash, discard, commit, push, or publication. CPython remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The `_blake2/blake2s_impl.c` source still compiles and is not the active frontier. No implementation or coding subagent was appropriate because the current blocker requires resolving the Win9x-visible contract for five exported loader-search constants; no authoritative new evidence or safe defect-family slice was established. + +Forced fresh VC6 probe again compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later, separate C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), followed by errors at 16220 (`list`) and 16224 (`trace`). No source or test changes were made. + +Files changed by this run: `PORT_STATUS.md` only. Existing generated compatibility patch, regression script changes, and nested CPython edits were preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; prior local modifications preserved; baseline remains `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, `_blake2/blake2s_impl.c` compiled; first diagnostics are the five C2065 constants above; distinct C89 diagnostics follow. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish the intended Win9x-visible contract for the five exported loader-search constants using authoritative API evidence, then add a focused failing test before source changes. Keep later C89 diagnostics separate. + +--- + +## Previous revision (forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-27 22:58 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before probing. Preserved all pre-existing outer and nested modifications; no reset, clean, stash, discard, commit, push, or publication. CPython remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The `_blake2/blake2s_impl.c` source is still successful and not the active frontier. No implementation slice was safe because the first blocker is still the unresolved Win9x-visible contract for five exported loader-search constants; no speculative source or test edits were made. + +Forced fresh VC6 probe result: 39/40 sources compiled (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate later C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), followed by 16220 (`list`) and 16224 (`trace`); left distinct and untouched. + +Files changed by this run: `PORT_STATUS.md` only. Existing generated compatibility patch, regression script changes, and nested CPython edits were preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, `_blake2/blake2s_impl.c` compiled; first errors are the five C2065 constants above; distinct C89 diagnostics follow. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; baseline unchanged: `v3.11.16`, `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; nested diff clean and generated patch matches complete nested diff byte-for-byte. | +| `git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; outer non-generated diff check passed. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish the intended Win9x-visible contract for the five exported loader-search constants using authoritative API evidence, then add a focused failing test before changing source. Keep the later C89 diagnostics separate. + +--- + +## Latest revision (scheduled forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-27 20:54 UTC + +Read `PORT_STATUS.md`, `README.md`, and the supplied legacy-toolchain instructions before probing. The active-profile reference file was absent from both expected skill paths; the complete porting instructions were provided in the task. Preserved all existing outer and nested work; no reset, clean, stash, discard, commit, push, or publication. CPython remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. `_blake2/blake2s_impl.c` remains compilable but is not the active frontier. No source/test edit or coding subagent was appropriate: first stable blocker remains the unresolved Win9x-visible API contract for five exported loader-search constants. + +Forced fresh compiler probe: 39/40 listed sources compiled (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics: C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later, separate C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), with further errors at 16220 (`list`), 16224 (`trace`), and C2059 at 16246. The API-contract decision is still unsupported by evidence; no fallback values or export changes were guessed. + +Files changed by this run: `PORT_STATUS.md` only. Existing generated compatibility patch, regression script, and nested CPython changes were preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, including `_blake2/blake2s_impl.c`; first diagnostics are the five C2065 errors above; subsequent C89 errors remain distinct. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all syntax checks and applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested and outer whitespace checks passed; generated compatibility patch matches the complete nested diff byte-for-byte. | +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && git status --short --branch` | 0; baseline/tag unchanged and pre-existing modifications preserved. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants; then write a focused failing test before any source edit. Keep subsequent C89 diagnostics separate. + +--- + +## Previous revision (scheduled forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-27 18:50 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before probing. Preserved all pre-existing outer and nested changes; no reset, clean, stash, discard, commit, push, or publication. CPython remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The recorded `_blake2/blake2s_impl.c` state remains true but is not the active frontier. No implementation subagent or source edit was appropriate: the current first blocker remains a Win9x-visible API-contract decision for five exported loader-search constants, not a defect family with a safe implementation established by evidence. + +Forced a fresh compiler probe: 39/40 listed sources compiled (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics remain C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Distinct subsequent C89 errors begin at `posixmodule.c(16173)` (`PyObject ScandirIteratorType`), followed by line 16220 (`PyObject list`), line 16224 (`trace`), and C2059 at 16246. No source or test change was safe without deciding the exported API contract. + +Files changed by this run: `PORT_STATUS.md` only. Existing generated compatibility patch, frontier regression script, and nested CPython changes were preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch; git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match` | 0; pre-existing modifications preserved; baseline/tag unchanged. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, including `_blake2/blake2s_impl.c`; first stable errors are the five C2065 loader-search constants above; later distinct C89 errors remain. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check; cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested and outer whitespace checks passed; compatibility patch byte-matches the complete nested diff. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish the intended Win9x-visible contract for the five exported loader-search constants from authoritative API evidence; then add a focused failing test before any source edit. Keep later C89 diagnostics separate. + +--- + +## Previous revision (forced fresh VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-27 16:46 UTC + +Read `PORT_STATUS.md`, `README.md`, and `skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before probing. Preserved all pre-existing outer and nested modifications; no reset, clean, stash, discard, commit, push, or publication. CPython remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The recorded `_blake2/blake2s_impl.c` compile state remains true but is not the active frontier. No implementation subagent was appropriate because the active blocker remains an unresolved Win9x-visible API contract. + +Forced a fresh compiler probe: 39/40 listed sources compiled (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. The first stable diagnostics remain C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Subsequent separate C89 diagnostics begin at `posixmodule.c(16173)` (`PyObject ScandirIteratorType`), continue at 16220 (`PyObject list`) and 16224 (`trace`); the transcript also reports C2059 at 16246. No source or test change was safe without determining the intended Win9x-visible contract for the five exported constants. + +Files changed by this run: `PORT_STATUS.md` only. Existing changes to the compatibility patch, frontier regression script, and nested CPython sources were preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && git -C cpython status --short --branch && git status --short --branch` | 0; baseline/tag unchanged; all existing modifications preserved. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, including `_blake2/blake2s_impl.c`; first stable errors are the five C2065 loader-search constants above, followed by separate C89 errors. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested diff clean, generated compatibility patch byte-for-byte matches complete nested diff, and outer non-generated diff check passed. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish authoritative behavior for these five exported loader-search constants on Win9x; then write a focused failing test before any source edit. Keep the separate C89 diagnostics distinct. + +--- + +## Latest revision (scheduled forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-27 10:34 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile reference `skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md`. Preserved all existing outer and nested work; no reset, clean, stash, discard, overwrite, commit, push, or publication. CPython baseline remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The `_blake2/blake2s_impl.c` source remains passing but is not the active frontier. No implementation subagent was appropriate because the first blocker remains an unresolved Win9x-visible API contract for five exported loader-search constants. + +Forced a fresh probe: 39/40 listed sources compiled (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics remain C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate later C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), then 16220 (`PyObject list`) and 16224 (`trace`). No speculative source or test change was safe. + +Files changed by this run: `PORT_STATUS.md` only. Existing modifications to the generated compatibility patch, frontier regression script, and nested CPython sources were preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch; git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match` | 0; pre-existing changes preserved; baseline/tag unchanged. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, `_blake2/blake2s_impl.c` compiled; first diagnostics are the five C2065 errors above; later C89 errors start at line 16173. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check; cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested whitespace check passed, generated compatibility patch matches complete nested diff byte-for-byte, and outer non-generated diff check passed. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish the intended Win9x-visible contract for the five exported loader-search constants from authoritative API evidence; then add a focused failing test before changing source. Keep subsequent C89 diagnostics separate. + +--- + +## Previous revision (forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-27 08:27 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before acting. Preserved all existing outer and nested modifications; no reset, clean, stash, discard, commit, push, or publication. Baseline remains CPython tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The recorded `_blake2/blake2s_impl.c` compile state remains true, but it is not the active frontier. No implementation subagent was appropriate: the current blocker is still the unresolved Win9x-visible contract for exported loader-search constants. + +Forced fresh probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics remain C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later distinct C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), then 16220 (`PyObject list`) and 16224 (`trace`). No source/test implementation was safe; this run was diagnostic-only. + +Files changed by this run: `PORT_STATUS.md` only. Existing changes to the generated compatibility patch, frontier regression script, and nested CPython checkout were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch; git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match; git -C cpython diff --check; cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; existing modifications preserved, baseline/tag unchanged, nested diff clean and tracked patch matches complete nested diff byte-for-byte. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, including `_blake2/blake2s_impl.c`; first diagnostics are the five C2065 constants above; later C89 errors start at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax and all applicable regression suites passed (`ALL TESTS PASSED`). | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish the intended Win9x-visible contract for the five exported loader-search constants using authoritative API evidence; then add a focused failing test before any source change. Keep later C89 diagnostics separate. + +--- + +## Latest revision (scheduled forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-27 06:26 UTC + +Read `PORT_STATUS.md`, `README.md`, and `skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before acting. Preserved all existing outer modifications and the nested CPython port diff; no reset, clean, stash, discard, overwrite, commit, push, or publication. Baseline remains CPython tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The recorded `_blake2/blake2s_impl.c` compile state remains true, but it is not the active frontier. No implementation/coding subagent was appropriate: the first blocker remains a Win9x-visible API contract decision for exported constants, with no authoritative choice established. + +Forced fresh probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics remain C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later distinct C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), then 16220 (`PyObject list`) and 16224 (`trace`). No source or test implementation was safe; this run was diagnostic-only. + +Files changed by this run: `PORT_STATUS.md` only. Existing changes to the generated compatibility patch, frontier regression script, and nested CPython checkout were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled; `_blake2/blake2s_impl.c` compiled; first diagnostics are the five C2065 constants above; later C89 diagnostics begin at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; nested diff clean and generated patch matches the complete nested diff byte-for-byte. | +| `git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; outer non-generated diff check passed. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish the intended Win9x-visible contract for the five exported loader-search constants using authoritative API evidence; then add a focused failing test before any source change. Keep later C89 diagnostics separate. + +--- + +## Latest revision (forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-27 04:22 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile reference `skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md`. Preserved all existing outer and nested modifications; no reset, clean, stash, discard, commit, push, or publication. Baseline remains CPython tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. `_blake2/blake2s_impl.c` remains compilable but is not the active frontier. A forced fresh probe reconfirmed the first stable blocker; no source/test implementation was safe. One coding subagent was requested per workflow but failed before launch because the delegation runtime requires the unavailable `anthropic` package. This was diagnostic-only. + +The probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later distinct C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), then 16220 (`PyObject list`) and 16224 (`trace`). No unresolved Win9x-visible API contract was guessed. + +Files changed by this run: `PORT_STATUS.md` only. Existing generated compatibility patch, frontier regression script, and nested CPython modifications were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, `_blake2/blake2s_impl.c` compiled; first diagnostics are the five C2065 constants above; later C89 diagnostics begin at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax and all applicable suites passed (`ALL TESTS PASSED`). | +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; baseline/tag unchanged; nested diff clean and generated patch matches it byte-for-byte. | +| Coding subagent delegation | Failed before launch: missing `anthropic` package; no child ran. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier remains resolving the intended Win9x-visible contract for the five exported loader-search constants from authoritative API evidence. Keep later C89 diagnostics separate. + +--- + +## Latest revision (forced VC6 frontier confirmation; Win32 loader-search constants remain blocker) + +Date: 2026-09-27 02:15 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile reference `skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md`. Preserved all existing outer and nested changes. No reset, clean, stash, discard, commit, push, or publication. CPython remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The recorded `_blake2/blake2s_impl.c` compile state remains true, but is not the active frontier. Fresh forced probe confirmed the recorded first blocker. No source/test edit or coding-agent delegation was appropriate: the five missing constants are exported by Windows `os`, and selecting fallback values or suppressing exports requires resolving the Win9x-visible API contract. + +The probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics: C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), followed by line 16220 (`PyObject list`) and line 16224 (`trace`); not addressed. + +Files changed by this run: `PORT_STATUS.md` only. Existing generated compatibility patch, frontier regression tests, and nested CPython modifications were preserved. Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, `_blake2/blake2s_impl.c` compiled; first stable errors are the five C2065 constants above; subsequent C89 errors begin at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax checks and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && git -C cpython status --short --branch && git status --short --branch` | 0; baseline/tag unchanged; pre-existing outer and nested modifications preserved. | + +Next frontier: resolve the intended Win9x-visible contract for the five exported loader-search constants from authoritative API evidence. If established, add a focused failing test before a narrow source change; keep the later C89 diagnostics separate. + +--- + +## Latest revision (scheduled fresh VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-26 22:07 UTC + +Read this status, `README.md`, and the active-profile legacy-toolchain reference before work. Preserved all existing outer changes and nested CPython edits; no reset, clean, stash, discard, commit, push, or publication. The recorded `_blake2/blake2s_impl.c` state was rechecked via forced fresh probe and remains true; it compiles but is not the active frontier. No source implementation was appropriate because the first blocker remains the unresolved Win9x-visible contract for five exported Windows loader-search constants. No coding subagent was launched for this diagnostic-only run. + +The forced probe compiled 39/40 sources (`total=40 compiled=39 cached=0 skipped=0`). First stable errors remain C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later distinct C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), then line 16220 (`PyObject list`) and 16224 (`trace`). These remain outside this diagnostic slice. + +Files changed by this run: `PORT_STATUS.md` only. Existing generated compatibility patch, frontier regression test, and nested CPython changes were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch; git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match; date -u '+%Y-%m-%d %H:%M UTC'` | 0; existing modifications preserved; CPython remains `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`; timestamp 2026-09-26 22:07 UTC. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled; `_blake2/blake2s_impl.c` compiled; first stable errors are the five C2065 constants above. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax checks and all applicable regression suites passed (`ALL TESTS PASSED`). | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish the intended Win9x-visible contract for the five exported loader-search constants using authoritative API evidence; then add a focused failing test before any source edit. Keep later C89 diagnostics separate. + +--- + +Date: 2026-09-26 20:06 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile legacy-toolchain reference before work. Preserved all existing outer modifications and nested CPython port edits; no reset, clean, stash, discard, commit, push, or publication. The requested `_blake2/blake2s_impl.c` slice was not changed: a forced fresh probe confirmed it compiles, while the actual first blocker remains in `posixmodule.c`. Since the five loader-search constants are exported through the Windows `os` module and their Win9x-visible API contract remains unresolved, this run was diagnostic-only; no implementation subagent or source/test edit was appropriate. + +Baseline remains CPython tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The forced source-list probe compiled 39/40 sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable errors are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later distinct C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), then 16220 (`PyObject list`) and 16224 (`trace`); they remain outside this diagnostic slice. + +Files changed by this run: `PORT_STATUS.md` only. Pre-existing modifications to the generated compatibility patch, frontier regression test, and nested CPython sources were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, `_blake2/blake2s_impl.c` compiled; first errors are the five C2065 loader-search constants above; subsequent distinct C89 diagnostics begin at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; baseline remains `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; nested diff is clean and generated compatibility patch matches the complete nested diff byte-for-byte. | +| `git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; non-generated outer diff check passed. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish the intended Win9x-visible contract for the five exported loader-search constants using authoritative API evidence, then add a focused failing test before any source edit; keep later C89 diagnostics separate. + +Read this status, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before work. Preserved the existing outer modifications to this status, the generated compatibility patch, and the frontier regression test, plus all nested CPython edits. No reset, clean, stash, discard, overwrite of unrelated work, commit, push, or publication. A forced fresh source-list probe confirmed the recorded `_blake2/blake2s_impl.c` compile state, but it is not the active frontier. No implementation subagent or source edit was appropriate: first blocker remains unresolved loader-search constants whose intended Win9x-visible API contract is not established. + +The fresh probe compiled 39/40 sources. `_blake2/blake2s_impl.c` compiled. First stable errors remain C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later distinct C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), then line 16220 (`PyObject list`) and 16224 (`trace`); these were not treated as the active blocker. + +Files changed by this run: `PORT_STATUS.md` only. Existing changes to the compatibility patch, frontier test, and nested CPython checkout remain preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; `total=40 compiled=39 cached=0 skipped=0`; `_blake2/blake2s_impl.c` compiled. First errors are the five C2065 loader-search constants above; later C89 diagnostics start at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression checks passed (`ALL TESTS PASSED`). | +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; baseline `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; nested diff clean and compatibility patch matches the complete nested diff byte-for-byte. | +| `git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; non-generated outer diff check passed. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: resolve the intended Win9x-visible contract for the five exported loader-search constants using authoritative API evidence, then add a focused failing test before any source edit. Keep later C89 diagnostics separate. + +--- + +Read this status, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before work. Preserved all existing outer and nested modifications; no reset, clean, stash, discard, overwrite of unrelated work, commit, push, or publication. A forced fresh source-list probe confirmed the recorded `_blake2/blake2s_impl.c` compile state but it is not the active frontier. No coding subagent or implementation was appropriate because the first blocker is unchanged and still requires a deliberate Win9x-visible API contract. + +The probe compiled 39/40 listed sources. `_blake2/blake2s_impl.c` compiled. First stable errors remain C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Distinct later C89 diagnostics begin at line 16173. No fallback values or export changes were guessed. + +Files changed by this run: `PORT_STATUS.md` only. Existing modifications to the compatibility patch, frontier test, and nested CPython checkout remain preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; `total=40 compiled=39 cached=0 skipped=0`; `_blake2/blake2s_impl.c` compiled. First errors are the five C2065 loader-search constants above; later C89 diagnostics start at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression checks passed (`ALL TESTS PASSED`). | +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; baseline `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; nested diff clean and compatibility patch matches the complete nested diff byte-for-byte. | +| `git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; non-generated outer diff check passed. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: resolve the intended Win9x-visible contract for the five exported loader-search constants from authoritative API evidence, then add a focused failing test before any source edit. Keep the later C89 diagnostics separate. + +--- + +## Latest revision (fresh VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-26 11:51 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before work. Preserved all existing top-level and nested modifications. No reset, clean, stash, discard, commit, push, or publication. The requested `_blake2/blake2s_impl.c` sequence was not attempted as an edit because the status-directed fresh probe confirms it already compiles; the active frontier remains `posixmodule.c`. + +A forced fresh source-list probe compiled 39/40 sources. `_blake2/blake2s_impl.c` compiled. First stable errors remain five C2065 undeclared identifiers at `posixmodule.c(15856)`–`(15860)`: `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate later C89 diagnostics begin at line 16173. These are exported `os` constants; choosing fallback values or suppressing exports would alter Win9x-visible API behavior. No authoritative contract was established in this run, so no production/test change was safe and no coding subagent was delegated. This run was diagnostic-only. + +Files changed by this run: `PORT_STATUS.md` only; all pre-existing changes to this file, the compatibility patch, frontier test, and nested CPython checkout were preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; `total=40 compiled=39 cached=0 skipped=0`; `_blake2/blake2s_impl.c` compiled. First errors are the five C2065 constants above; later C89 errors start at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax checks and all listed applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; baseline remains `41388c9cb160d0886d5ca00d2e6c878260a8a4549`, tag `v3.11.16`. | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; nested diff is clean and generated patch matches it byte-for-byte. | +| `git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; outer non-generated diff check passed. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next step: establish the intended Win9x-visible contract for the five exported loader-search constants from authoritative API evidence; then add a focused failing test before any source edit. Keep the later C89 diagnostics separate. + +--- + +## Latest revision (fresh frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-26 09:47 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile `legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before work. Preserved the existing outer modifications to this file, the generated compatibility patch, and the frontier regression test; preserved all nested CPython edits. No reset, clean, stash, discard, commit, push, or publication. + +The status-directed `_blake2/blake2s_impl.c` sequence was checked by a forced fresh source-list probe. It still compiles, but is not the active frontier: 39/40 sources compile. The first stable blocker remains five C2065 diagnostics at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. C89 errors start later at line 16173. Since the five names are exported `os` constants and no evidence resolves the intended Win9x-visible contract, no source/test implementation or coding-agent delegation was appropriate; this run was diagnostic-only. + +Files changed by this run: `PORT_STATUS.md` only. Existing modifications were preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; total=40, compiled=39, cached=0, skipped=0; `_blake2/blake2s_impl.c` compiled. First diagnostics are the five C2065 loader-search constants above; subsequent C89 diagnostics begin at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable checks and regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | `41388c9cb160d0886d5ca00d2e6c878260a8a4549`, tag `v3.11.16`; unchanged. | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; nested diff is clean and generated compatibility patch matches it byte-for-byte. | +| `git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; outer non-generated diff check passed. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next: establish the intended Win9x-visible contract for the five exported loader-search constants from authoritative API evidence, then add a focused failing test before any source edit. Keep later C89 diagnostics separate. + +--- + +## Prior revision (fresh frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-26 07:43 UTC + +Read this status, `README.md`, and `skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before work. The reference was found and read under the active profile at `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md`; it is absent at the default-profile path. Preserved the existing outer modifications to `PORT_STATUS.md`, `compat/msvc600/cpython-3.11.16-vc6-headers.patch`, and `scripts/vc6-probe-pythoncore-frontier.test.sh`, as well as all nested CPython work. No reset, clean, stash, discard, commit, push, or publication. + +Forced a fresh source-list probe before considering implementation. `_blake2/blake2s_impl.c` compiles; 39/40 sources compile. The first stable blocker remains five C2065 diagnostics in `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Distinct later C89 diagnostics begin at line 16173. This matches the recorded frontier. The five names are exposed as Windows `os` constants; choosing values or omitting exports would alter the Win9x-visible API contract, still unresolved by available evidence. No safe implementation slice or suitable failing test was identified, so this run was diagnostic-only and no coding subagent was delegated. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; total=40, compiled=39, cached=0, skipped=0; `_blake2/blake2s_impl.c` compiled. First errors are the five C2065 constants above; later C89 errors begin at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable suites and syntax checks passed (`ALL TESTS PASSED`). | +| `date -u '+%Y-%m-%d %H:%M UTC'; git status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match; git -C cpython diff --check; cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; baseline remains `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`; existing outer changes preserved; nested diff check and complete-patch byte comparison passed. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next: resolve the intended Win9x-visible contract for the five exported loader-search constants using authoritative API evidence, then add a focused failing test before any source edit. Keep the later C89 diagnostics separate. + +--- + +## Prior revision (forced frontier re-probe; Win32 loader-search constants remain first blocker) + +Date: 2026-09-26 05:38 UTC + +Before probing, read `PORT_STATUS.md` and `README.md`. The requested reference file `legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` is not installed in the active profile at the expected path; followed the complete legacy-toolchain instructions supplied for this run. Preserved pre-existing outer changes to `PORT_STATUS.md`, `compat/msvc600/cpython-3.11.16-vc6-headers.patch`, and `scripts/vc6-probe-pythoncore-frontier.test.sh`; nested CPython work was left intact. No reset, clean, stash, discard, commit, push, or publication. + +Forced a fresh probe before choosing work. The `_blake2/blake2s_impl.c` sequence is not the active frontier: it compiles. Result remains 39/40 sources; the first stable blocker is five C2065 errors in `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later C89 diagnostics start at line 16173. The five values are Win32 loader-search constants exported by the `os` module; defining values or dropping those exports would alter Win9x-visible API behavior, and the available evidence does not establish which behavior is intended. No safe TDD implementation slice was identified, so this run was diagnostic-only and no coding agent was delegated. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; total=40, compiled=39, cached=0, skipped=0. `_blake2/blake2s_impl.c` compiled. First errors are the five C2065 loader-search constants above; later C89 errors begin at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax validation and every listed applicable regression suite passed (`ALL TESTS PASSED`). | +| `git status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; outer local modifications preserved; nested baseline remains `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`; nested whitespace check and byte-for-byte patch reproducibility check passed. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next: establish the intended Win9x-visible API contract for the five loader-search constants, then add a focused failing test before any source change. Keep later C89 diagnostics separate. + +--- + +## Prior revision (forced frontier re-probe; Win32 loader-search constants remain first blocker) + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before probing. The attempted coding-agent delegation failed before launch because the runtime lacks the required `anthropic` package; no child ran. Preserved all pre-existing work: outer modifications to `PORT_STATUS.md`, the generated compatibility patch, and the frontier regression script; detached `cpython/` checkout remains extensively modified at baseline tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. No reset, clean, stash, discard, commit, push, or publication occurred. + +The recorded `_blake2/blake2s_impl.c` state remains true but is not the active frontier. A forced fresh source-list probe compiled 39/40 listed sources, including `_blake2/blake2s_impl.c`. First stable blocker remains five C2065 errors in `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later distinct C89 errors begin at line 16173. These five names are Win32 loader-search constants exported into `os`; assigning fallback values or dropping exports would decide Win9x-visible API behavior, unsupported by current evidence. No source/test edit was safe; this run was diagnostic-only. The agent delegation prerequisite failed as noted above. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; total=40, compiled=39, cached=0, skipped=0. `_blake2/blake2s_impl.c` compiled. First five errors are C2065 for the constants above; later C89 errors at `posixmodule.c(16173)` and onward. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; existing three outer modifications preserved; baseline is `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; nested whitespace check, patch reproducibility comparison, and outer whitespace check passed. | +| Coding-agent delegation via `delegate_task` | Failed before launch: runtime error, missing `anthropic` package. No independent review or child implementation. | + +This remains compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next: establish the intended Win9x-visible contract for the five loader-search constants with authoritative API evidence; then write a focused failing test before any source change. Keep the later C89 blocker separate. + +--- + +## Prior revision (forced frontier re-probe; Win32 loader-search constants remain first blocker) + +Date: 2026-09-25 21:26 UTC + +Before probing, read this status, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md`. Preserved all pre-existing work: outer modifications to `PORT_STATUS.md`, the generated compatibility patch, and the frontier regression script; detached `cpython/` checkout remains extensively modified at baseline tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. No reset, clean, stash, discard, commit, push, or publication occurred. + +Forced a fresh source-list probe before deciding whether the recorded `_blake2/blake2s_impl.c` slice was still current. It compiles; 39 of 40 listed sources compile. The first stable blocker remains five C2065 errors in `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate C89 diagnostics begin at line 16173. No implementation slice is safe without deciding the Win9x-visible behavior of the five exported `os` constants; no speculative edit or coding-agent delegation was appropriate. This run changed only this status entry. + +Exact commands and results: + +| Command | Exit/result | +|---|---| + +--- +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 sources compiled, including `_blake2/blake2s_impl.c`; first five C2065 diagnostics are the constants listed above; later C89 family begins at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax and all applicable regression suites passed (`ALL TESTS PASSED` for each). | +| `git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match; git -C cpython diff --check; cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; baseline commit/tag unchanged, nested diff clean, generated patch matches the full nested diff byte-for-byte, outer non-generated diff clean. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next: establish the intended Win9x behavior/API contract for the five exported loader-search constants, then add a focused failing regression before any source change. Keep later C89 diagnostics separate. + +--- + +## Latest revision (forced frontier re-probe; Win32 loader-search constants remain first blocker) + +Date: 2026-09-25 19:22 UTC + +Read this status, `README.md`, and `skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before probing. Preserved all pre-existing changes: outer modifications to this status, the generated compatibility patch, and the frontier regression script; detached `cpython/` checkout remains extensively modified at baseline tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. No reset, clean, stash, discard, commit, push, or publication occurred. + +Forced a fresh source-list probe. `_blake2/blake2s_impl.c` still compiles, as do 39 of 40 listed sources. The first stable blocker remains five C2065 errors in `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later C89 diagnostics begin at line 16173 and remain a separate family. No source change was safe: these values are exposed as Windows `os` module constants, and choosing numeric fallbacks or dropping the exports changes Win9x-visible API behavior not established by available evidence. This run was diagnostic-only; no coding agent was delegated because implementation was not appropriate. + +Files changed in this run: `PORT_STATUS.md` only. All existing nested CPython edits and generated patch were preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git rev-parse HEAD && git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; outer modifications preserved; nested checkout detached at `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 sources compiled, including `_blake2/blake2s_impl.c`. First five C2065 diagnostics are the loader-search constants above; later distinct C89 diagnostics begin at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax checks and all applicable regression suites passed. | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested diff check passed, compatibility patch matches the complete nested diff byte-for-byte, and non-generated outer diff check passed. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next: establish the intended Win9x behavior/API contract for the five exported loader-search constants; only then add a focused failing test before any source change. Keep the later C89 diagnostics separate. + +--- + +## Latest revision (diagnostic-only fresh frontier confirmation) + +Date: 2026-09-25 UTC + +Read this status, `README.md`, and the CPython/Win9x/MSVC6 reference before work. Preserved all pre-existing modifications: outer checkout edits to this file, the generated compatibility patch, and the frontier regression script; the detached `cpython/` checkout remains extensively modified. Baseline remains CPython tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. No reset, clean, stash, discard, commit, push, or publication occurred. + +Re-forced the source-list probe before choosing implementation. `_blake2/blake2s_impl.c` still compiles, as do 39 of 40 listed sources. The first stable blocker remains five C2065 undeclared-identifier errors in `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. The module code exports these as `_LOAD_LIBRARY_SEARCH_*` integer constants. No authoritative local SDK definition or additional evidence resolving whether/how these constants should be exposed was found; assigning fallback values or removing exports remains an API-contract decision, so no speculative source edit was made. Later C89 diagnostics beginning at line 16173 remain a separate blocker. This run was diagnostic-only; no coding subagent was delegated. + +Files changed in this run: `PORT_STATUS.md` only. Existing CPython edits, regression script, and generated compatibility patch were preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; `_blake2/blake2s_impl.c` compiles; total=40, compiled=39, cached=0, skipped=0. First stable diagnostics are the five C2065 errors listed above. Additional C89 diagnostics begin at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax and all applicable regression suites passed. | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested diff whitespace check passed, compatibility patch matches the complete nested diff byte-for-byte, and non-generated outer diff check passed. | + +Compiler-probe progress only. No `pythoncore.dll`/`python.exe` link, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: resolve the intended Win9x `os` API contract for these five exported loader-search constants using authoritative Windows SDK/API evidence; then write a focused failing test before any source change. Keep the later C89 diagnostics separate. + +--- + +## Latest revision (diagnostic-only forced frontier re-probe; loader-search constants remain first blocker) + +Date: 2026-09-25 09:03 UTC + +--- + +## Latest revision (forced frontier re-probe; Win32 loader-search constants remain first blocker) + +Date: 2026-09-25 UTC + +Read this status, `README.md`, and `skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before work. Preserved all existing changes: the outer checkout already had modifications to `PORT_STATUS.md`, the generated CPython compatibility patch, and the frontier regression script; the detached `cpython/` checkout contains extensive local edits. CPython remains baseline tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. No reset, clean, stash, discard, commit, push, or publication occurred. + +The status-directed `_blake2/blake2s_impl.c` sequence was re-evaluated with a forced fresh source-list probe before implementation. The recorded compile state remains true, but it is not the active frontier: 39/40 sources compile, and the first stable blocker remains five C2065 undeclared-identifier errors in `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Source inspection confirms these values are exported as `_LOAD_LIBRARY_SEARCH_*` from the Windows `os` module initialization block. Assigning fallback values or omitting exports would decide Win9x-visible API behavior; available evidence does not establish that contract, so no speculative compatibility edit was made. Later C89 diagnostics beginning at line 16173 remain a distinct subsequent blocker. No coding agent was delegated because this run was diagnostic-only. + +Files changed in this run: `PORT_STATUS.md` only. Existing nested CPython edits, the generated compatibility patch, and regression script were preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch; git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match` | 0; outer pre-existing changes preserved; nested checkout detached at `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; `_blake2/blake2s_impl.c` compiles; 39/40 sources compiled. First diagnostics are the five C2065 errors at `posixmodule.c(15856)`–`(15860)` listed above. Distinct later C89 diagnostics begin at line 16173. | +| `rg -n -C 4 'LOAD_LIBRARY_SEARCH_(DEFAULT_DIRS|APPLICATION_DIR|SYSTEM32|USER_DIRS|DLL_LOAD_DIR)|_LOAD_LIBRARY_SEARCH_' cpython/Modules/posixmodule.c` | 0; confirms all five are added as `os` module integer constants in the `MS_WINDOWS` block. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish the intended Win9x behavior of the five exported loader-search constants before adding a focused failing test and changing source; then address later C89 diagnostics separately. + + +Read `PORT_STATUS.md`, `README.md`, and `skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before acting. Preserved the existing outer modifications to `PORT_STATUS.md`, the generated compatibility patch, and frontier test; the detached `cpython/` checkout also contains extensive local port edits. No reset, clean, stash, discard, commit, push, or publication occurred. CPython remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +Forced a fresh source-list compiler probe. The recorded `_blake2/blake2s_impl.c` state remains true, but it is not the active blocker: 39/40 listed sources compile. The first stable diagnostics remain five C2065 undeclared-identifier errors in `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Inspection confirms these are exported as `_LOAD_LIBRARY_SEARCH_*` constants in the Windows `os` module initialization block. Defining fallback values or omitting exports changes the Win9x-visible API contract, which remains unresolved; no speculative source/test changes or coding-agent delegation were appropriate. Later C89 diagnostics start at line 16173 and remain a separate blocker. This run was diagnostics-only. + +Files changed by this run: `PORT_STATUS.md` only. All existing nested CPython edits and generated patch were preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; outer checkout already had the three modified files; nested checkout is detached at `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`, with pre-existing port edits. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; `_blake2/blake2s_impl.c` compiled; 39/40 sources compiled. First stable errors are the five C2065 constants above. Distinct later C89 diagnostics begin at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax checks and all applicable regression suites passed. | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; nested whitespace check passed and compatibility patch matches the complete nested diff byte-for-byte. | +| `git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; non-generated outer diff check passed. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next: establish the intended Win9x API behavior for the five exported loader-search constants; only then add a focused failing test before changing source. Keep the later C89 diagnostics separate. + +--- + +## Latest revision (diagnostic-only forced frontier re-probe) + +Date: 2026-09-25 06:58 UTC + +Read `PORT_STATUS.md`, `README.md`, and `skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before acting. Preserved the pre-existing outer modifications to `PORT_STATUS.md`, the generated compatibility patch, and the frontier test; the detached `cpython/` checkout also contains extensive local port edits. No reset, clean, stash, discard, overwrite of unrelated changes, commit, push, or publication occurred. CPython remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. + +Re-forced the current compiler frontier before considering implementation. The recorded `_blake2/blake2s_impl.c` state remains true, but it is not the current blocker: 39/40 listed sources compile. The first stable blocker remains five C2065 undeclared-identifier diagnostics in `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later distinct C89 diagnostics begin at line 16173. Inspection confirms these names are exported as `_LOAD_LIBRARY_SEARCH_*` constants from the Windows `os` module initialization block. Adding numeric values or omitting exports would make a Win9x-visible API behavior choice that the available evidence does not resolve. Per scope, this run therefore performed diagnostics only; no coding agent or implementation/test edit was appropriate. + +Files changed by this run: `PORT_STATUS.md` only. Existing CPython changes and generated patch were left intact. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git rev-parse --show-toplevel && git rev-parse HEAD && git remote -v` | 0; outer checkout on `main...gitea/main` with the three pre-existing modified files; no publication performed. | +| `git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; nested checkout remains detached at `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`, with pre-existing source edits. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; `_blake2/blake2s_impl.c` compiled; 39/40 sources compiled. First stable errors are the five C2065 loader-search constant errors at `posixmodule.c(15856)`–`(15860)`. Later distinct C89 errors start at 16173. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax checks and all applicable regression suites passed. | +| `git -C cpython diff --check; cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | Each check returned 0; nested whitespace check passed, generated patch matches the complete nested diff byte-for-byte, and non-generated outer diff check passed. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next: resolve the intended Win9x behavior/API contract for the five exported loader-search constants; only then write a focused failing regression test before any source change. Keep the later C89 diagnostics separate. + +--- + +## Latest revision (forced frontier re-probe; loader-search constants remain first blocker) + +Date: 2026-09-25 UTC + +Read `PORT_STATUS.md`, `README.md`, and `skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before acting. The outer checkout already had modifications to `PORT_STATUS.md`, the generated compatibility patch, and the frontier regression script; the detached `cpython/` checkout had extensive local port edits. Preserved all prior state. Baseline is still tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. No reset, clean, stash, discard, commit, push, or publication occurred. + +Forced a fresh source-list probe before implementation. The recorded `_blake2/blake2s_impl.c` compile state is still true, but `_blake2` is not the current blocker: 39/40 sources compile, and the first stable blocker is `posixmodule.c` with five C2065 errors at lines 15856–15860 for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. The later C89 errors beginning at line 16173 are distinct and remain untouched. Since these constants are exported through the Windows `os` module and their intended Win9x-visible behavior remains unresolved, implementation would be speculative. Therefore this run was diagnostic-only; no coding agent or source/test edit was appropriate. + +Files changed by this run: `PORT_STATUS.md` only. The existing compatibility patch and all nested source changes were preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; pre-existing local modifications preserved; CPython remains detached at `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 sources compiled, including `_blake2/blake2s_impl.c`. First stable diagnostics: five C2065 loader-search constants at `posixmodule.c(15856)`–`(15860)`; distinct later errors start at 16173. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\home\\ubuntu\\projects\\python-win9x\\MSVC600\\VC98\\Bin\\VCVARS32.BAT && cl'` | 0; `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax and all applicable regression suites passed. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next: determine and document intended Win9x behavior for these five loader-search constants, then add a focused failing test before any source edit. Keep the later C89 diagnostics separate. + +--- + +## Latest revision (diagnostic-only confirmation of Win32 loader-search constants frontier) + +Date: 2026-09-25 00:46 UTC + +Baseline verified as CPython `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Read this status, `README.md`, and the legacy-toolchain reference before probing. The outer checkout already contained local modifications to `PORT_STATUS.md`, the generated compatibility patch, and the frontier regression script; the detached `cpython/` checkout already contained extensive port edits. Preserved all prior state. No reset, clean, stash, discard, commit, push, or publication occurred. The exact compiler banner was re-verified as `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86` through Wine. + +The recorded mathmodule frontier was not current: a forced fresh source-list probe still compiled `_blake2/blake2s_impl.c` but found `posixmodule.c` as the first blocker, with 39 of 40 sources compiling. First stable diagnostics are five C2065 errors at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Inspection confirms these are exported as `_LOAD_LIBRARY_SEARCH_*` constants from the Windows `os` module initialization block. Defining arbitrary fallback values or omitting these exports changes the Win9x-visible API surface, and the intended contract is not established by the available evidence. Therefore this run made diagnostics only; no implementation or coding-agent delegation was appropriate. Later C89 diagnostics (first at line 16173) are separate and remain untouched. + +Files changed by this run: `PORT_STATUS.md` only. No CPython source, test, or compatibility patch was modified in this run. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; pre-existing changes preserved; CPython detached at `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; `_blake2/blake2s_impl.c` compiled; 39/40 sources compiled. First stable diagnostics are the five C2065 loader-search constants at `posixmodule.c(15856)`–`(15860)`; later C89 diagnostics follow. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\home\ubuntu\projects\python-win9x\MSVC600\VC98\Bin\VCVARS32.BAT && cl'` | 0; verified compiler banner as stated above. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax checks and all applicable regression suites passed. | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested diff check passed, generated compatibility patch reproduces the full nested diff byte-for-byte, and non-generated outer diff check passed. | + +Compiler-probe progress only. No `pythoncore.dll`/`python.exe` link, Wine interpreter execution, or real Windows 98 SE execution is verified. Next step: establish the intended Win9x `os` API behavior for the five Windows-7-era loader-search constants, then add a focused failing test before any source change. Preserve the later C89 diagnostics as separate frontiers. + +--- + # Port Status — CPython 3.11.16 to Windows 98 SE +## Latest revision (diagnostic-only confirmation of loader search constants frontier) + +Date: 2026-09-24 22:41 UTC + +Baseline verified as CPython `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before work, read this status, `README.md`, and the active-profile `legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md`. The outer checkout already contained modifications to `PORT_STATUS.md`, the generated compatibility patch, and the frontier test; the detached `cpython/` checkout had extensive local modifications. Preserved all existing work; no reset, clean, stash, discard, commit, push, or publication occurred. MSVC6 remains external/local-only. + +Force-re-probed the recorded frontier before any implementation. The state remains true: `_blake2/blake2s_impl.c` compiled and the source-list probe compiled 39 of 40 sources. First stable diagnostics remain the five C2065 errors at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. The subsequent C89 errors beginning at line 16173 are later, distinct blockers. The loader flags are exported in the Windows `os` module initialization, so choosing fallback numeric constants versus omitting API exports changes the Win9x-visible behavior. No source/test/patch implementation was appropriate absent a resolved API contract; no coding agent was delegated for this diagnostic-only run. + +Files changed by this run: `PORT_STATUS.md` only. No CPython source, regression test, or generated compatibility patch was modified. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; outer pre-existing modifications preserved; detached CPython checkout at baseline commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`, tag `v3.11.16`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; `_blake2/blake2s_impl.c` compiled; 39/40 compiled. First errors are the five loader-search constants at `posixmodule.c(15856)`–`(15860)`; later C89 diagnostics follow. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax checks and all applicable regression suites passed. | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested diff check passed, generated patch still reproduces complete nested diff byte-for-byte, and non-generated outer diff check passed. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next step remains resolving the intended Win9x `os` API behavior for these Windows-7-era loader flags and then writing a focused failing test before changing source. Keep later C89 diagnostics separate. + +--- + +## Latest revision (diagnostic-only loader search constants frontier) + +Date: 2026-09-24 20:37 UTC + +Baseline verified as CPython `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before work, read this status, `README.md`, and `legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md`. The outer checkout already had modifications to `PORT_STATUS.md`, the generated compatibility patch, and the frontier test; the detached `cpython/` checkout had extensive local modifications. Preserved all existing work. No reset, clean, stash, discard, commit, push, or publication occurred. The legacy compiler remains external/local-only. + +The previously recorded frontier was force-re-probed and remained true: `_blake2/blake2s_impl.c` compiled and the probe compiled 39 of 40 listed sources. First stable diagnostics are in `posixmodule.c` lines 15856–15860: C2065 for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Source inspection shows these are exported as `os` module constants in the `MS_WINDOWS` module-init block. Unlike an isolated compile-time spelling, deciding whether to define numeric fallback values or omit these exports changes the Win9x-visible API contract; no behavior choice was made without runtime evidence. Later C89 diagnostics beginning at 16173 are separate and were not addressed. No implementation slice or coding-agent delegation was appropriate until the API behavior is resolved; this run was diagnostics only. + +Files changed by this run: `PORT_STATUS.md` only. No CPython source, regression test, or compatibility patch was modified. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; detached checkout at baseline commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`, tag `v3.11.16`, with pre-existing modified source files. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; `_blake2/blake2s_impl.c` compiled; 39/40 sources compiled. First errors are the five C2065 loader-search constants at `posixmodule.c(15856)`–`(15860)`; later C89 diagnostics follow. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable suites passed. | +| `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; tracked compatibility patch still exactly reproduces the full nested diff. | +| `git -C cpython diff --check` | 0. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next step: establish an explicit intended `os` API behavior for these Windows-7-era loader flags on Win9x, then add a focused regression test before any source change. Preserve the later C89 blocker as a separate frontier. + +--- + +## Latest revision (VC6 wait-status C89 declaration slice) + +Date: 2026-09-24 UTC + +Baseline: CPython `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before work, read this status, `README.md`, and the active-profile legacy-toolchain reference. Existing outer modifications to this status, generated compatibility patch, and frontier test were preserved; the detached `cpython/` checkout also contained extensive local edits. No reset, clean, stash, discard, commit, push, or publication occurred. A single focused coding-agent delegation was attempted and could not launch because the runtime lacks the `anthropic` package; this slice was completed directly, without claiming independent review. + +The recorded frontier was force-re-probed before editing and remained true: `_blake2/blake2s_impl.c` compiled and 39/40 listed sources compiled. First blocker remained `posixmodule.c(15107)` C2143 (`missing ';' before 'type'`) for the post-statement `exitcode` declaration; C2065 for `exitcode` at 15110 and parser cascades followed. Following TDD, updated the focused Windows wait-status assertion first and ran the regression script against unchanged production code; expected RED, exit 1, at the assertion that the opening C89 declaration group was missing. Hoisted only `unsigned PY_LONG_LONG status` and `exitcode` to the Windows branch's declaration group; kept both initializations at their original evaluation points. Adjusted the assertion to extract the full conditional function and verify declaration placement and conversion/error order. Focused test then passed GREEN. Two intermediate assertion-harness runs exposed extraction/expected-text mismatches; corrected the test only, with no further production changes. + +Files changed in this slice: + +- `cpython/Modules/posixmodule.c` — declaration ordering in `os_waitstatus_to_exitcode_impl()` Windows branch only. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — focused declaration/order assertion. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested CPython diff and verified byte-for-byte. +- `PORT_STATUS.md` — this record. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before edit | 2; 39/40 compiled, `_blake2/blake2s_impl.c` compiled. First diagnostics `posixmodule.c(15107)` C2143 and `(15110)` C2065 `exitcode`; later cascades. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after assertion edit, before production edit | 1; expected RED at the Windows wait-status C89 assertion. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation and assertion correction | 0; `ALL TESTS PASSED`. | +| Forced frontier probe after edit (same command as above) | 2; the `exitcode` C2143/C2065 family is gone. New first stable errors are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `...APPLICATION_DIR`, `...SYSTEM32`, `...USER_DIRS`, and `...DLL_LOAD_DIR`. Still 39/40; no later family changed. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; shell syntax checks and all applicable regression suites passed. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch && git -C cpython diff --check && cmp <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; complete nested diff matches generated patch byte-for-byte; nested and non-generated outer whitespace checks passed. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: inspect the `LOAD_LIBRARY_SEARCH_*` constants at `posixmodule.c(15856)` and re-probe before deciding a separate narrow slice. + +--- + +## Latest revision (VC6 wait-status 64-bit spelling slice) + +Date: 2026-09-24 UTC + +Baseline remains CPython `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before editing, read this status, `README.md`, and `legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` under the active Hermes profile. The outer checkout already contained local modifications to this status, the generated compatibility patch, and the frontier regression script. The detached `cpython/` checkout had extensive existing port edits. All local state was preserved; no reset, clean, stash, discard, commit, push, or publication occurred. The requested single focused coding-agent delegation was attempted and could not launch because the runtime lacks the `anthropic` Python package; direct work continued on the single evidenced slice. + +The recorded `_blake2/blake2s_impl.c` frontier was force-re-probed before edits and remained true: that source compiled, with 39 of 40 listed sources compiled. First stable blocker: `posixmodule.c(15102)`, then C2632 at 15103 and 15107 (`'long' followed by 'long' is illegal`) in `os_waitstatus_to_exitcode_impl()`'s Windows branch. Following TDD, added an assertion to `scripts/vc6-probe-pythoncore-frontier.test.sh` and ran it against unchanged source; expected RED, exit 1. Replaced the three `unsigned long long` type spellings (two declarations and the error-sentinel cast) with `unsigned PY_LONG_LONG`, preserving unsigned semantics and conversion/return behavior. The focused assertion passed GREEN. No independent review is claimed. + +Files changed in this slice: + +- `cpython/Modules/posixmodule.c` — VC6-compatible unsigned 64-bit type spellings in `os_waitstatus_to_exitcode_impl()` only. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — focused assertion for the Windows branch type spellings and preserved conversion/result calls. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested CPython diff and byte-compared. +- `PORT_STATUS.md` — this entry. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before edit | 2; `_blake2/blake2s_impl.c` compiled; 39/40 compiled. C2632 at `posixmodule.c(15102)`, `(15103)`, `(15107)`; following cascades. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` with new assertion, before source edit | 1; expected RED: Windows wait-status conversion lacked the required VC6 spelling. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after source edit | 0; `ALL TESTS PASSED`, including the new assertion. | +| Same forced frontier probe after source edit | 2; the C2632 long-long diagnostics are gone. New first diagnostic: `posixmodule.c(15107)` C2143 (missing `;` before `type`) for the `exitcode` declaration after the preceding `if`; C2065 at 15110 and parser cascades follow. Separate later `LOAD_LIBRARY_SEARCH_*` SDK constants and later C89 errors remain untouched. Summary `total=40 compiled=39 cached=0 skipped=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; shell syntax and all applicable regression suites passed. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch && git -C cpython diff --check && cmp <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; complete patch byte-matches the nested diff; nested and non-generated outer whitespace checks passed. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next narrow frontier: inspect/re-probe the C89 declaration-order defect at `posixmodule.c(15107)` (`exitcode`) and add its focused failing assertion before any source change. Do not combine it with later API constants or unrelated errors. + +--- + +## Latest revision (VC6 DLL-directory API fallback slice) + +Date: 2026-09-24 UTC + +Baseline: CPython `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before editing, read this status, `README.md`, and `legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` (located under the active Hermes profile skills). The outer checkout already had local changes in `PORT_STATUS.md`, `compat/msvc600/cpython-3.11.16-vc6-headers.patch`, and `scripts/vc6-probe-pythoncore-frontier.test.sh`; the detached `cpython/` checkout had extensive local port edits. All pre-existing work was preserved. No reset, clean, stash, discard, commit, push, or publication occurred. MSVC600 remains external/local-only through Wine (`WINEPREFIX=/home/ubuntu/.wine-win9xport`); compiler banner: `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The previously recorded `_blake2/blake2s_impl.c` frontier was force-re-probed before edits and remained true: it compiled, as did 39 of 40 listed sources. First blocker was `posixmodule.c(14905)` in `typedef DLL_DIRECTORY_COOKIE (WINAPI *PAddDllDirectory)(PCWSTR newDirectory);`: C2143 missing `)` before `__stdcall`, C2143 missing `{` before `__stdcall`, C2059 at `)`, and C2165 (`left-side modifier`); the companion typedef and unavailable API types/functions then cascaded. These are Windows 7 DLL-directory APIs absent on Win9x. The requested one focused coding-agent delegation was attempted, but failed to launch because the runtime lacks Python package `anthropic`; implementation proceeded directly as a single narrow slice. + +Following TDD, the focused regression assertion was added first and run against unchanged production source: expected RED, exit 1 (`VC6 os.add_dll_directory fallback missing`). The minimal source edit gates the API typedefs and both implementation bodies for MSVC6, returning `NotImplementedError` from `os._add_dll_directory` and `os._remove_dll_directory`; modern compiler paths remain intact. The focused suite then passed GREEN. This explicitly acknowledges the unavailable Win7 functionality rather than inventing compatibility declarations. + +Files changed in this slice: + +- `cpython/Modules/posixmodule.c` — VC6 guard around DLL-directory function pointer typedefs and explicit unsupported-operation errors in the add/remove implementations. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — focused structural regression assertion for both functions and the retained modern branch. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested CPython diff and verified byte-for-byte. +- `PORT_STATUS.md` — this entry. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\home\ubuntu\projects\python-win9x\MSVC600\VC98\Bin\VCVARS32.BAT && cl'` | 0; compiler banner recorded above. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before edit | 2; 39/40 compiled; first diagnostic family at `posixmodule.c(14905)` described above. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after adding assertion, before source edit | 1; expected RED: `VC6 os.add_dll_directory fallback missing`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after source edit | 0; `ALL TESTS PASSED`, including add/remove DLL-directory assertion. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after edit | 2; `_blake2/blake2s_impl.c` compiled and 39/40 listed sources compiled. DLL-directory diagnostics are gone. New first blocker is `posixmodule.c(15102)` C2632 (`'long' followed by 'long' is illegal`) in `os_waitstatus_to_exitcode_impl()`; further C2632 diagnostics at 15103 and 15107 and their cascades. Summary `total=40 compiled=39 cached=0 skipped=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; shell syntax and all applicable regression suites passed. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch && git -C cpython diff --check && cmp <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; generated patch matches full nested diff byte-for-byte; nested and non-generated outer whitespace checks passed. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution has been verified. Next narrow frontier: inspect/re-probe `os_waitstatus_to_exitcode_impl()` at `posixmodule.c(15102)` for VC6 64-bit spelling/declaration blockers; do not fold later unrelated errors into that slice. + +--- + +## Latest revision (diagnostic-only DLL-directory API frontier) + +Date: 2026-09-24 UTC + +Baseline: CPython `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Read this status, `README.md`, and `legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before work. The outer checkout already had uncommitted changes to this status, the generated compatibility patch, and the frontier test; the nested `cpython/` checkout had extensive local source edits. All pre-existing work was preserved. No reset, clean, stash, discard, commit, push, or publication occurred. MSVC6 is the external `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`, invoked through Wine with `WINEPREFIX=/home/ubuntu/.wine-win9xport`. + +The recorded `_blake2/blake2s_impl.c` frontier was force-re-probed and remained true: that source compiled and 39 of 40 listed sources compiled. First stable diagnostics are at `posixmodule.c(14905)` for `typedef DLL_DIRECTORY_COOKIE (WINAPI *PAddDllDirectory)(PCWSTR newDirectory);`: C2143 missing `)` before `__stdcall`, C2143 missing `{` before `__stdcall`, C2059 at `)`, and C2165 (`left-side modifier`). The immediately following companion typedef at 14906 fails similarly. Subsequent errors identify the absent VC98/Win9x API types and function (`DLL_DIRECTORY_COOKIE`, `AddDllDirectory`) at 14928–14929, followed by cascades. Source inspection confirms these helpers dynamically call Windows 7 DLL-directory APIs; Win9x does not provide that API. A safe behavior-preserving local compiler shim is not established: any port fix must explicitly decide the unavailable API's behavior and account for both add/remove helpers and generated module exposure. Therefore this run made diagnostics only rather than a speculative source change. The requested single coding subagent could not launch because the runtime requires the unavailable Python `anthropic` package; no external coding CLI was used to bypass that failed delegation. + +Files changed by this run: `PORT_STATUS.md` only. No CPython source, test, or compatibility patch was modified in this run. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; `_blake2/blake2s_impl.c` compiled, 39/40 sources compiled, first stable error family at `posixmodule.c(14905)` as above; summary `total=40 compiled=39 cached=0 skipped=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax checks and all applicable regression suites passed. | +| `git -C cpython diff --check && cmp <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested diff whitespace check, patch byte-for-byte comparison, and non-generated outer diff whitespace check passed. | + +This is compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier requires deciding and testing explicit Win9x behavior for `os.add_dll_directory` and `os.remove_dll_directory`; until then, do not mask the compiler errors with invented API declarations. + +--- + +## Latest revision (`os_scandir_impl()` VC6 C89 declaration slice) + +Date: 2026-09-24 UTC + +Baseline: CPython `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Read this status, `README.md`, and `legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before work. The outer checkout and detached `cpython/` checkout already had extensive local changes; all were preserved. No reset, clean, stash, discard, commit, push, or publication occurred. The requested single focused coding subagent could not launch because the runtime requires the unavailable Python `anthropic` package. Direct implementation followed after confirming the exact frontier. Codex CLI `0.153.4` and authenticated status were verified, but no external coding agent ran; no independent review is claimed. + +The recorded `_blake2/blake2s_impl.c` frontier was force-re-probed first and remained applicable: it compiled; 39 of 40 sources compiled. First stable diagnostics were `posixmodule.c(14689)` C2275/C2065 for the post-statement `PyObject *ScandirIteratorType` declaration in `os_scandir_impl()`. Following TDD, an exact-function assertion was added, then corrected to include the existing clinic marker. With production source still unchanged, the corrected test failed RED (exit 1) specifically because the type local was declared after the audit statement. The minimal source change hoists only `PyObject *ScandirIteratorType;` into the opening declaration group and changes its original initializer into an assignment at its existing post-audit point. Audit, iterator allocation, and error/cleanup order remain unchanged. + +Files changed in this slice: + +- `cpython/Modules/posixmodule.c` — C89 declaration ordering in `os_scandir_impl()` only. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — focused exact-function structural assertion. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested CPython diff and verified byte-for-byte. +- `PORT_STATUS.md` — this milestone entry. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before edit | 2; `_blake2/blake2s_impl.c` compiled; 39/40 compiled; first diagnostics `posixmodule.c(14689)` C2275/C2065 for `PyObject ScandirIteratorType`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after adding/correcting assertion, before source edit | 1; expected RED: `os_scandir_impl must hoist ScandirIteratorType into its opening C89 declaration group and preserve audit-before-type-lookup order`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after source edit | 0; GREEN; all structural checks passed. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after edit | 2; original 14689 declaration diagnostics are gone; the next distinct first diagnostic is `posixmodule.c(14905)` C2143/C2059/C2165 around `__stdcall` in later DLL-directory API code. Summary `total=40 compiled=39 cached=0 skipped=0`; no later family was changed. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; shell syntax and all applicable regression suites passed. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch && git -C cpython diff --check && cmp <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; complete compatibility patch matches the nested diff byte-for-byte; nested and non-generated outer diffs pass whitespace checks. | + +Compiler-probe progress only: `_blake2/blake2s_impl.c` and 39/40 listed sources compile, but `posixmodule.c` still blocks. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next narrow frontier: inspect/re-probe the `posixmodule.c(14905)` `__stdcall`/DLL-directory diagnostics; do not fold in later independent diagnostics. + +--- + +## Latest revision (`ScandirIterator_dealloc()` VC6 C89 declaration slice) + +Date: 2026-09-24 07:52 UTC + +Baseline: CPython `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before work, read this status, `README.md`, and `legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md`. The outer checkout and detached `cpython/` checkout already contained local changes; all were preserved. No reset, clean, stash, discard, commit, push, or publication occurred. Codex CLI `0.153.4` was present, but the requested single Hermes coding subagent could not launch because the runtime lacks the `anthropic` package; no child changes were used. I completed this narrow slice directly and do not claim independent review. + +The recorded `_blake2/blake2s_impl.c` frontier was force-re-probed before editing and remained applicable: it compiled; 39 of 40 sources compiled. The first stable diagnostic family was `posixmodule.c(14623)` C2275/C2146/C2065 for `freefunc free_func` in `ScandirIterator_dealloc()`. Following TDD, the focused assertion was added first and observed RED (exit 1), specifically because the callback declaration followed the finalizer statement. The minimal source change hoists only `freefunc free_func;` into the opening declaration group and converts its original initializer to an assignment after the finalizer check; finalizer, callback, and decref order is unchanged. The assertion passed GREEN (exit 0). + +Files changed in this slice: + +- `cpython/Modules/posixmodule.c` — C89 declaration ordering in `ScandirIterator_dealloc()` only. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — focused exact-function structural assertion. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested CPython diff and verified byte-for-byte. +- `PORT_STATUS.md` — this milestone entry. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before edit | 2; `_blake2/blake2s_impl.c` compiled; 39/40 compiled; first diagnostics `posixmodule.c(14623)` C2275/C2146/C2065 for `freefunc free_func`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after adding assertion, before source edit | 1; expected RED: assertion that `ScandirIterator_dealloc` must declare `free_func` before statements and preserve finalizer/free/decref order. | +| Same focused test after source edit | 0; GREEN. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after edit | 2; the `freefunc` diagnostic family at 14623 is gone. New first stable diagnostics are `posixmodule.c(14689)` C2275/C2065 for `PyObject ScandirIteratorType`; later distinct blockers remain. Summary `total=40 compiled=39 cached=0 skipped=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; shell syntax and all applicable regression suites passed. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch && git -C cpython diff --check && cmp <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; complete compatibility patch matches the nested diff byte-for-byte; nested and non-generated outer diffs pass whitespace checks. | + +Compiler-probe progress only: `_blake2/blake2s_impl.c` and 39/40 listed sources compile, but `posixmodule.c` still blocks. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next narrow frontier: inspect/re-probe `posixmodule.c(14689)` C89 declaration of `ScandirIteratorType`; do not fold in later API/language diagnostics. + +--- + +## Latest revision (`os_DirEntry_inode_impl()` VC6 64-bit type spelling) + +Date: 2026-09-24 05:45 UTC + +Baseline: CPython `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before edits, inspected this status, `README.md`, and the requested legacy-toolchain reference path; the reference file was absent from the active/default skills directories and project. The skill instructions supplied in the task were followed. The outer checkout and detached `cpython/` checkout had pre-existing local changes, preserved without reset, clean, stash, discard, commit, push, or publication. Codex CLI `0.153.4` was available and used as the single focused coding lane; its scope was limited to `posixmodule.c` and the frontier test. The CPython tag/commit were unchanged. + +The recorded `_blake2/blake2s_impl.c` frontier was force-re-probed before implementation and remained true: `_blake2/blake2s_impl.c` compiled; the first stable error family was `posixmodule.c(14152)` C2632, because VC6 rejects `long long` in `os_DirEntry_inode_impl()`'s Windows `static_assert`. The child confirmed the existing Windows `PY_LONG_LONG` definition resolves to `__int64` and matches the unsigned type accepted by `PyLong_FromUnsignedLongLong`. Following TDD, the focused assertion covering both Windows/POSIX inode `static_assert`s was added first and observed RED (exit 1). The minimal source edit changed only the two asserted type spellings from `unsigned long long` to `unsigned PY_LONG_LONG`; the runtime return expressions and diagnostic strings are unchanged. The focused assertion then passed GREEN (exit 0). + +Files changed in this slice: + +- `cpython/Modules/posixmodule.c` — two `os_DirEntry_inode_impl()` compile-time assertion type spellings. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — assertion requires `unsigned PY_LONG_LONG` in both branches and updates the exact-function expectation. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested diff and compared byte-for-byte. +- `PORT_STATUS.md` — this milestone entry. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before edit | 2; 39/40 compiled; `_blake2/blake2s_impl.c` compiled; first stable diagnostic `posixmodule.c(14152)` C2632 (`'long' followed by 'long' is illegal`), with later separate blockers. | +| Focused inode assertion before source edit (`bash /tmp/direntry-inode-tdd/run-focused.sh`, coding lane) | 1; expected RED: `AssertionError: DirEntry.inode static_asserts must both use unsigned PY_LONG_LONG`. | +| Same focused inode assertion after source edit (coding lane) | 0; `ok - DirEntry.inode preserves C89 declarations and decoder/stat/errno/cleanup order`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.test.sh` (coding lane) | 0. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; all structural checks passed, including both inode assertions. Its bounded live integration check compiled the first source, `getpath.c`, successfully; it is not the full frontier probe. | +| Fresh forced frontier probe after edit, with output captured in `/home/ubuntu/.hermes/profiles/maxwell/cache/scratch/vc6-frontier-run.log` | 2; `_blake2/blake2s_impl.c` compiled; C2632 diagnostics at `posixmodule.c(14152)` are gone. New first stable blocker is `posixmodule.c(14623)` C2275/C2146/C2065 for `freefunc free_func` in the later scandir code. Later diagnostics remain; summary `total=40 compiled=39 cached=0 skipped=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; all applicable regression suites passed. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch && git -C cpython diff --check && cmp <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; complete nested diff reproduced byte-for-byte; nested and non-generated outer diffs pass whitespace checks. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next narrow frontier: inspect and re-probe `posixmodule.c(14623)` `freefunc` C89 declaration before choosing another slice; do not fold in later API/language diagnostics. No commit or publication occurred. + +--- + +## Latest revision (`os_DirEntry_inode_impl()` VC6 C89 declarations) + +Date: 2026-09-24 03:34 UTC + +Baseline: CPython `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before this slice, the top-level checkout already had local changes to `PORT_STATUS.md`, the compatibility patch, and the frontier test; the detached `cpython/` checkout had extensive source modifications. These were preserved. No reset, clean, stash, discard, commit, push, or publication occurred. The provided legacy-toolchain skill reference file was not present at either expected profile/default skill path or in the project; work followed the full instructions supplied in the task. One focused Codex coding-agent lane implemented the slice; the forced probe and applicable regression suites were rerun directly. No independent reviewer is claimed. + +The recorded `_blake2/blake2s_impl.c` frontier was force-re-probed before editing and remained the active source-list frontier: it compiled, with `total=40 compiled=39 cached=0 skipped=0`. First stable diagnostics were in `os_DirEntry_inode_impl()` at `posixmodule.c(14124)` C2143 before `const`, followed by C2065 for `path` at 14125, C2143 at 14126, and C2065 for `saved_errno` at 14139. Following TDD, a focused whole-function structural assertion was added before the source edit and observed RED (exit 1). The narrow edit hoisted only the branch-specific `path` declarations and shared `saved_errno` declaration in the existing `if (!self->got_file_index)` block, keeping `const wchar_t *` versus `wchar_t *` under the existing preprocessor branches. Assignments remain at their original evaluation points: after successful decoding, around `LSTAT`, with errno saved before decref/free cleanup. The focused assertion then passed. + +Files changed in this slice: + +- `cpython/Modules/posixmodule.c` — declaration ordering only in `os_DirEntry_inode_impl()`. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — exact-function structural assertion checking C89 declaration placement and branch evaluation/cleanup order. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested CPython diff and compared byte-for-byte. +- `PORT_STATUS.md` — this entry. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| Focused exact-function assertion before production edit | 1; expected RED, assertion reports the declaration/order requirement is not yet met. | +| Same focused assertion after edit | 0; assertion passed. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable suites passed, including the live frontier test's first-source compile check. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before edit | 2; 39 of 40 compiled, `_blake2/blake2s_impl.c` compiled; first errors at `posixmodule.c(14124)` C2143, 14125 C2065, 14126 C2143, and 14139 C2065. | +| Same forced fresh frontier probe after edit | 2; 39 of 40 compiled; the declaration errors at 14124–14139 are gone. New first diagnostic is `posixmodule.c(14152)` C2632 (`long` followed by `long` is illegal), followed by distinct later blockers including `freefunc` declaration errors at 14623 and Win32 API/language errors. Summary `total=40 compiled=39 cached=0 skipped=0`. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch && git -C cpython diff --check && cmp <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; generated patch matches the full nested CPython diff byte-for-byte and nested whitespace check passes. | + +This is compiler-probe progress only: no `pythoncore.dll`/`python.exe` link, Wine interpreter execution, or real Windows 98 SE execution is verified. Next narrow frontier: inspect and re-probe the `posixmodule.c(14152)` C2632 64-bit type spelling before choosing another slice; do not fold in later diagnostics. + +--- + +## Latest revision (`DirEntry_test_mode()` VC6 C89 declaration slice) + +Date: 2026-09-24 01:25 UTC + +Baseline remains CPython `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before work, read `PORT_STATUS.md`, `README.md`, and `legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md`. The top-level and detached `cpython/` checkouts contained pre-existing local modifications; those were preserved. No reset, clean, stash, discard, commit, push, or publication occurred. One focused Codex coding-agent lane worked on this exact slice; I independently reran the compiler frontier and applicable regression suites. No independent human review is claimed. + +The recorded `_blake2/blake2s_impl.c` frontier was force-re-probed before the edit and remained applicable: it compiled and 39 of 40 sources compiled. The first stable diagnostic family was `posixmodule.c(14024)` C2275/C2065 for `_posixstate *state` declared after statements in `DirEntry_test_mode()`. Following TDD, a focused exact-function structural assertion was added first and observed RED (exit 1) against the old source; it requires the declaration in the opening C89 declaration group and preserves the state lookup after the stat/error handling. The minimal edit hoisted only `_posixstate *state;` and changed its original initialized declaration into an assignment at the same evaluation point. The full focused frontier test passed afterward. + +Files changed in this slice: + +- `cpython/Modules/posixmodule.c` — one declaration-order correction in `DirEntry_test_mode()` only. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — focused structural assertion preserving stat/error/state-lookup order. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the full nested CPython diff and verified byte-for-byte. +- `PORT_STATUS.md` — this entry. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| Focused assertion before source edit | 1; expected RED because `state` was still declared after executable statements. | +| Focused assertion after source edit (included in full test run) | 0; assertion passed. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; all assertions passed. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before edit | 2; `_blake2/blake2s_impl.c` compiled, `total=40 compiled=39 cached=0 skipped=0`; first diagnostics at `posixmodule.c(14024)` C2275/C2065 for `_posixstate`/`state`. | +| Same forced fresh frontier probe after edit | 2; `_blake2/blake2s_impl.c` compiled and 39 sources compiled. The 14024 declaration diagnostics are gone. New first stable blocker is `posixmodule.c(14124)` C2143 before `const`, followed by C2065 for `path` at 14125 and later errors. Summary `total=40 compiled=39 cached=0 skipped=0`. | +| Coding-agent sandbox forced-probe attempt | 1; Wine could not create `/run/user/1000/wine/server-801-136fb0/lock` on its read-only filesystem, so no compiler conclusion was taken from that attempt. The same command was rerun directly outside the restricted coding-agent sandbox and produced the compiler result above. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax and all applicable regression suites passed. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch && git -C cpython diff --check && cmp <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; patch regenerated; nested diff check and byte-for-byte comparison passed. | +| `git diff --check` over the outer tree | 2; reports trailing spaces in added lines of the generated patch, which reproduces the complete nested source diff exactly. `git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; all other outer-tree edits pass the whitespace check. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next narrow frontier: inspect and force-re-probe the C89 declaration in `DirEntry_fetch_stat()` at `posixmodule.c(14124)` before any further edit. Preserve later distinct blockers; do not broaden into cascades. + +--- + +## Latest revision (`DirEntry_fetch_stat()` VC6 C89 declaration slice) + +Date: 2026-09-23 23:17 UTC + +Baseline: CPython `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Read this status, `README.md`, and `legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before work. The top-level checkout and detached `cpython/` checkout had pre-existing local changes; these were preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. One focused Codex coding-agent lane handled the implementation and followed TDD; I independently reran the probe and regression gates. No independent human review is claimed. + +The recorded frontier was force-re-probed first and remained true: `_blake2/blake2s_impl.c` compiled, 39 of 40 sources compiled, and `posixmodule.c(13870)` first failed on a C89 declaration (`const wchar_t *path`), with analogous path declarations and `saved_errno` later in `DirEntry_fetch_stat()`. The focused structural assertion was added first and observed RED (exit 1) before the source edit; it passed GREEN (exit 0) afterward. The narrow edit hoists `saved_errno` and the Windows/POSIX-specific `path` pointer declarations into the opening declaration group, changing their original initializers into assignments at the same points. Conversion, stat call, errno capture, cleanup, and error behavior remain ordered as before. + +Files changed in this slice: + +- `cpython/Modules/posixmodule.c` — C89 declarations in `DirEntry_fetch_stat()` only. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — one exact-function structural regression assertion. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the full nested diff and verified byte-for-byte. +- `PORT_STATUS.md` — this entry. + +Exact validation commands/results: + +| Command | Exit/result | +|---|---| +| Focused assertion before production edit | 1; expected RED, declaration/order assertion failure. | +| Focused assertion after production edit | 0; GREEN. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; all assertions passed, including `DirEntry_fetch_stat`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before edit | 2; `_blake2/blake2s_impl.c` compiled; `total=40 compiled=39 cached=0 skipped=0`; first diagnostics at `posixmodule.c(13870)` C2143 and later C2065 for `path`. | +| Same forced frontier probe after edit | 2; `_blake2/blake2s_impl.c` compiled and 39 sources compiled; diagnostics at 13870/13901 are gone. New first stable diagnostic is `posixmodule.c(14024)` C2275/C2065 for the post-statement `_posixstate *state` declaration in `DirEntry_test_mode()`. Later cascades include distinct C89/API errors; summary `total=40 compiled=39 cached=0 skipped=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax and all applicable regression suites passed. | +| `git -C cpython diff --check` | 0. | +| `git -C cpython diff | cmp - compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; patch exactly matches the complete nested diff. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: inspect and re-probe the C89 declaration in `DirEntry_test_mode()` at `posixmodule.c(14024)` before any further edit. Preserve the later separate blockers and do not broaden into cascades. + +--- + +## Latest revision (`posixmodule.c` `DirEntry_dealloc()` C89 declaration slice) + +Date: 2026-09-23 21:08 UTC + +Baseline: CPython `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before work, read `PORT_STATUS.md`, `README.md`, and `legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md`. The top-level and detached CPython checkouts had pre-existing local modifications; all were preserved. No reset, clean, stash, discard, commit, push, or publication occurred. A requested focused coding-agent lane was attempted but could not launch because the Anthropic provider package is unavailable (`anthropic` required); I performed this single narrow slice directly and did not claim independent review. + +The recorded frontier was force-re-probed first and remained true: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(13820)` failed with C2275/C2146/C2065 for the `freefunc free_func` declaration after preceding DECREF statements. Hoisted only the bare `freefunc free_func;` declaration in `DirEntry_dealloc()` and changed its original initializer into an assignment at the same point. Cleanup, slot lookup, invocation, and decref order are unchanged. The focused assertion was introduced first and failed (exit 1) before the source change. The assertion initially also had a test-slice signature mismatch; corrected that harness expectation, then verified it green. + +Files changed in this slice: + +- `cpython/Modules/posixmodule.c` — one declaration-order fix in `DirEntry_dealloc()`. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — focused regression assertion. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated complete nested diff. +- `PORT_STATUS.md` — this entry. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before edit | 2; `_blake2/blake2s_impl.c` compiled; total=40, compiled=39, cached=0, skipped=0; first diagnostic family at `posixmodule.c(13820)`, C2275/C2146/C2065 for `freefunc`/`free_func`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` before source edit | 1; expected focused assertion failure. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after source edit and test expectation correction | 0; `ALL TESTS PASSED`, including the new assertion. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after edit | 2; `_blake2/blake2s_impl.c` still compiles and 39 sources compile. The 13820 family is gone. New first diagnostics are `posixmodule.c(13870)` C2143/C2065 for `const`/`path`, with later distinct issues; a second `freefunc` C2275/C2146/C2065 family appears at 14606. Summary total=40, compiled=39, cached=0, skipped=0. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed. | +| `git -C cpython diff --check` | 0. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; byte comparison with `cmp -s <(git -C cpython diff) ...` | 0; patch reproduces the complete nested diff byte-for-byte. | + +Compiler-probe progress only: no `pythoncore.dll`/`python.exe` link, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: `posixmodule.c(13870)` C89 declaration diagnostic in the following scandir-related function; inspect and re-probe before any further change. The separate later `freefunc` diagnostic at 14606 remains recorded, not included in this slice. + +--- + +## Latest revision (`posixmodule.c` `os_cpu_count_impl()` VC6 processor-count fallback) + +Date: 2026-09-23 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Read `PORT_STATUS.md`, `README.md`, and `legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before work. Existing top-level and detached CPython local modifications were preserved; no reset, clean, stash, discard, commit, push, or publication occurred. One focused Codex coding lane implemented only this slice. The forced probe was re-run directly outside its restricted Wine sandbox after that sandbox could not create Wine's server lock. + +The recorded frontier was force-re-probed before implementation and remained true: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(13612)` failed first because VC6 lacks `ALL_PROCESSOR_GROUPS`. Inspection established that merely defining the constant would still call `GetActiveProcessorCount`, a Windows 7 API unavailable to Win9x. The narrow fix uses the VC98-available `GetSystemInfo()` and `SYSTEM_INFO.dwNumberOfProcessors` only under `_MSC_VER < 1300`; modern builds retain `GetActiveProcessorCount(ALL_PROCESSOR_GROUPS)`. Existing count-or-`None` handling is unchanged. + +Files changed for this slice: + +- `cpython/Modules/posixmodule.c` — VC6-only processor-count implementation in `os_cpu_count_impl()`. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — focused assertion for the VC6 API path and modern behavior. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested CPython diff. +- `PORT_STATUS.md` — this entry. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| Focused assertion before source edit: `python3 /tmp/vc6-all-processors/focused.py cpython/Modules/posixmodule.c` | 1; expected RED: `AssertionError: os_cpu_count must use VC6-only GetSystemInfo and preserve modern all-group query/count-or-None`. | +| Same focused assertion after source edit | 0. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; `ALL TESTS PASSED`, including the new assertion. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` (fresh direct run) | 2; `_blake2/blake2s_impl.c` compiled and 39 sources compiled. `ALL_PROCESSOR_GROUPS` diagnostic is gone. First remaining diagnostics are `posixmodule.c(13820)` C2275/C2146/C2065 for `freefunc`/`free_func`, followed by additional distinct C89/API diagnostics. Summary `total=40 compiled=39 cached=0 skipped=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; shell syntax and all applicable regression suites passed. The delegated restricted-sandbox aggregate attempt failed due to Wine's read-only `/run/user/1000` server-lock path; the direct command above completed successfully. | +| `git -C cpython diff --check` | 0. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; regenerated patch matches the complete nested diff byte-for-byte. | + +Compiler-probe progress only. No `pythoncore.dll`/`python.exe` link, Wine interpreter execution, or real Windows 98 SE execution is verified. Next narrow source frontier: the `posixmodule.c(13820)` C89 `freefunc`/`free_func` declaration family; re-probe before changing it. + +## Latest revision (`posixmodule.c` `os_get_terminal_size_impl()` C89 declarations) + +Date: 2026-09-23 16:49 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Read `PORT_STATUS.md`, `README.md`, and the legacy-toolchain reference before work. The top-level `main` checkout and detached `cpython/` checkout both contained pre-existing local modifications; all were preserved. No reset, clean, stash, discard, commit, push, or publication occurred. The MSVC600 toolchain remains local-only and external. + +The recorded frontier was force-re-probed before implementation: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(13568)` was the first stable diagnostic family (C2275/C2065 for `TerminalSizeType`), with the following `pos` declaration producing C2143 and later unrelated errors cascading. One focused Codex coding lane performed the TDD edit in the existing checkout. Its first in-sandbox forced probe and aggregate regression command were blocked by Wine's read-only `/run/user/1000` server lock; these were rerun directly outside that sandbox and produced real results below. No independent review is claimed. + +The focused test was added and observed RED before the production edit (exit 1: assertion that `os_get_terminal_size_impl` had not yet hoisted its locals). The minimal edit hoists `PyObject *TerminalSizeType` and `int pos` into the opening declaration group and changes only their original initializers to assignments; evaluation, SET_TERMSIZE/error handling, and return order remain unchanged. + +Files changed for this slice: + +- `cpython/Modules/posixmodule.c` — C89 declaration-order correction in `os_get_terminal_size_impl()` only. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — focused full-function structural assertion. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested CPython diff. +- `PORT_STATUS.md` — this entry. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| Focused test before source edit (coding-lane run) | 1; expected RED assertion: `os_get_terminal_size_impl must declare TerminalSizeType/pos in the opening C89 group...`. | +| Focused test after source edit | 0; assertion passed. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; `ALL TESTS PASSED`, including the new full-function assertion. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` (fresh direct run) | 2; `_blake2/blake2s_impl.c` compiled; 39 sources compiled; `TerminalSizeType`/`pos` declaration diagnostics are gone. First remaining diagnostic: `posixmodule.c(13612)` C2065, `ALL_PROCESSOR_GROUPS` undeclared; later distinct C89/API diagnostics remain. Summary `total=40 compiled=39 cached=0 skipped=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; all applicable regression suites passed. The initial in-sandbox attempt exited 1 because Wine could not create its server lock on a read-only filesystem; the direct rerun passed. | +| `git -C cpython diff --check` | 0. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | regeneration succeeded; byte-for-byte comparison exit 0. | + +This is compiler-probe progress only. No `pythoncore.dll`/`python.exe` link, Wine interpreter execution, or real Windows 98 SE execution is verified. Next narrow frontier: the separate VC98/Win9x API constant blocker `ALL_PROCESSOR_GROUPS` at `posixmodule.c(13612)`; do not broaden into later cascades. + +## Latest revision (`posixmodule.c` VC6 `_MAX_ENV` fallback slice) + +Date: 2026-09-23 14:40 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine with `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. The requested one focused coding-agent lane could not launch because the runtime lacks the `anthropic` package; no child changes were used. I performed this one narrow TDD slice directly, preserving all local changes. No independent review is claimed. + +The recorded `_blake2/blake2s_impl.c` frontier was force-re-probed first and remained true: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(11238)` was the first stable blocker, C2065 for unavailable `_MAX_ENV`. The focused assertion was added and observed RED before the source edit, then GREEN after the edit. The smallest VC6-only fallback was added under `MS_WINDOWS` and `_MSC_VER < 1300`, protected by `#ifndef`; modern SDK definitions and the existing environment-length check are unchanged. The next first stable blocker is the distinct `posixmodule.c(13568)` C2275/C2065 family for `TerminalSizeType` (with later legacy API/C89 diagnostics cascading after it). + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — added a guarded `_MAX_ENV 32767` fallback for the VC98/MSVC6 path. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the guarded fallback. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | 2; first blocker `posixmodule.c(11238)` C2065 for `_MAX_ENV`; summary `total=40 compiled=39 cached=0`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after adding the assertion, before source edit | 1; expected RED: `VC6 _MAX_ENV fallback missing`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after source edit | 0; `ALL TESTS PASSED`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after source edit | 2; `_MAX_ENV` blocker is gone; first reported stable blocker is `posixmodule.c(13568)` C2275/C2065 for `TerminalSizeType`; summary `total=40 compiled=39 cached=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed. | +| `git -C cpython diff --check` | 0; nested source diff clean. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct `TerminalSizeType` C89/API blocker at `posixmodule.c(13568)`; do not broaden into later cascading diagnostics. + +## Latest revision (`posixmodule.c` `win32_putenv()` C89 declaration slice) + +Date: 2026-09-23 12:33 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine with `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. One focused Codex coding-agent lane implemented this exact TDD slice; no independent review is claimed. + +The recorded `_blake2/blake2s_impl.c` frontier was force-re-probed first and remained true: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(11217)` was the first stable blocker. MSVC6 reported C2275/C2065 for `PyObject *unicode`, followed by the same C89 declaration-order family for `size`, `env`, and `err`. The child first added a focused structural assertion and reported the required RED result (exit 1), then added only the C89 declaration/assignment slice. Afterward, the compiler advanced past those declarations; the next first stable blocker is the distinct VC98 SDK/API gap `posixmodule.c(11238)`, undeclared `_MAX_ENV`. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — hoisted `PyObject *unicode`, `Py_ssize_t size`, `wchar_t *env`, and `int err` into `win32_putenv()`'s opening declaration group; changed only the `env` and `err` initializers into assignments at their original points. Evaluation, cleanup, error, and return order are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the declaration group and preserved body order. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | 2; first stable blocker `posixmodule.c(11217)` C2275/C2065 for `unicode`; summary `total=40 compiled=39 cached=0`. | +| Focused `win32_putenv()` assertion before source edit | 1; expected RED, as reported by the delegated lane. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after source edit | 0; `ALL TESTS PASSED`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after source edit | 2; `posixmodule.c` passed the declaration family; first stable blocker is `posixmodule.c(11238)` C2065 for unavailable `_MAX_ENV`; summary `total=40 compiled=39 cached=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed. | +| `git -C cpython diff --check` | 0; nested source diff clean. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct VC98 SDK/API blocker at `posixmodule.c(11238)`, `_MAX_ENV`; do not broaden into later cascading diagnostics. + +## Latest revision (`posixmodule.c` VC6 `SYMBOLIC_LINK_FLAG_DIRECTORY` fallback slice) + +Date: 2026-09-23 10:26 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine with `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. One focused Codex coding-agent lane implemented this exact TDD slice; no independent review is claimed. + +The recorded `_blake2/blake2s_impl.c` frontier state was force-re-probed first and remained true: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and the recorded first blocker was `posixmodule.c(9076)` C2065 for unavailable `SYMBOLIC_LINK_FLAG_DIRECTORY`. The child first added a focused assertion and reported the required RED result, then added only a VC6-guarded fallback for the missing constant while preserving the modern path. After the slice, the compiler advanced past line 9076; the first stable frontier is now the later `os_putenv_impl()` C89 declaration family at `posixmodule.c(11217)`. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — added `_MSC_VER < 1300`/`#ifndef` fallback `SYMBOLIC_LINK_FLAG_DIRECTORY 0x1`; modern SDK behavior and the symlink API call remain unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the guarded fallback. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| Focused `SYMBOLIC_LINK_FLAG_DIRECTORY` assertion before source edit | 1; expected RED, as reported by the delegated coding lane. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after source edit | 0; `ALL TESTS PASSED`, including the new fallback assertion. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after source edit | 2; `_blake2/blake2s_impl.c` compiled and `posixmodule.c` compiled past line 9076. First stable blocker is `posixmodule.c(11217)` C2275/C2065 for later C89 declarations in `os_putenv_impl()`; summary `total=40 compiled=39 cached=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed. | +| `git -C cpython diff --check` | 0; nested source diff clean. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct C89 declaration-order blocker at `posixmodule.c(11217)` in `os_putenv_impl()`; do not broaden into later cascading diagnostics. + +## Latest revision (`posixmodule.c` `os_readlink_impl()` VC6 reparse-API gate slice) + +Date: 2026-09-23 08:18 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine with `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. The requested focused coding-agent delegation was attempted but could not launch because the runtime lacks the `anthropic` package; no child changes were used. + +The recorded `_blake2/blake2s_impl.c` frontier state was force-re-probed first and remained true: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(8885)` was the first stable blocker, C2065 for unavailable `FSCTL_GET_REPARSE_POINT`. This run implemented one narrow TDD compatibility slice only: gate the entire Windows `os_readlink_impl()` reparse-point implementation for MSVC6 and return `NotImplementedError` instead of referencing the unavailable VC98 SDK operation. The modern MSVC path remains unchanged. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — added `_MSC_VER < 1300` fallback in the `MS_WINDOWS` branch of `os_readlink_impl()`, returning `NotImplementedError`; the modern `DeviceIoControl`/reparse implementation remains under the `#else` branch. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the VC6 fallback and preservation of the modern `FSCTL_GET_REPARSE_POINT` path. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +|| Command | Exit/result | +|---|---| +|| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +|| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | 2; `_blake2/blake2s_impl.c` compiled, 39 sources compiled, first blocker `posixmodule.c(8885)` C2065 for `FSCTL_GET_REPARSE_POINT`; summary `total=40 compiled=39 cached=0`. | +|| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after adding the focused assertion, before source edit | 1; expected RED: `VC6 os_readlink fallback missing`. | +|| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after source edit | 0; GREEN, `ALL TESTS PASSED`. | +|| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after source edit | 2; `posixmodule.c` compiled past `FSCTL_GET_REPARSE_POINT`. The next first stable blocker is `posixmodule.c(9076)` C2065 for unavailable `SYMBOLIC_LINK_FLAG_DIRECTORY`; summary `total=40 compiled=39 cached=0`. | +|| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax and all applicable regression suites passed. | +|| `git -C cpython diff --check` | 0; nested source diff clean. | +|| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `git -C cpython diff \\| cmp - compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct VC98 SDK/API blocker `posixmodule.c(9076)`, `SYMBOLIC_LINK_FLAG_DIRECTORY`; do not broaden into later cascading diagnostics. + +## Latest revision (`posixmodule.c` `os_waitpid_impl()` C89 declaration-order slice) + +Date: 2026-09-23 06:11 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine with `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded `_blake2/blake2s_impl.c` frontier state was force-re-probed first and remained true: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(8741)` was the first stable blocker. After the prior 64-bit spelling slice, the stable diagnostic family was C2143/C2065 for the post-statement `PY_LONG_LONG ustatus` declaration/use. One focused Codex coding-agent lane implemented this exact TDD slice, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — moved `PY_LONG_LONG ustatus` into `os_waitpid_impl()`'s opening C89 declaration group and changed its original declaration initializer to `ustatus = (unsigned int)status;`. The conversion point, left shift, and `Py_BuildValue()` result order are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added the focused structural assertion; it was observed RED before the source edit and GREEN afterward. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| Focused waitpid assertion before source edit | 1; expected RED. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after source edit | 0; `ALL TESTS PASSED`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after source edit | 2; `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c` compiled past line 8741. The next first stable blocker is `posixmodule.c(8885)` C2065 for unavailable `FSCTL_GET_REPARSE_POINT`; summary `total=40 compiled=39 cached=0 skipped=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check` | 0; nested source diff clean. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `git -C cpython diff \| cmp - compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct VC98 SDK/API blocker `posixmodule.c(8885)`, `FSCTL_GET_REPARSE_POINT`; do not broaden into later cascading diagnostics. + +## Latest revision (`posixmodule.c` `os_waitpid_impl()` VC6 64-bit spelling slice) + +Date: 2026-09-23 04:03 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine with `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded `_blake2/blake2s_impl.c` frontier state was force-re-probed first and remained true: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(8741)` was the first stable blocker. The initial diagnostic family was MSVC6 C2632/C2143/C2065 for `unsigned long long ustatus` and its use. The requested focused coding-agent lane could not launch because the runtime lacks the `anthropic` package; no child changes were used. I then performed this one narrow TDD slice directly, preserving local changes. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — changed `unsigned long long ustatus` to CPython's `PY_LONG_LONG` abstraction in `os_waitpid_impl()`. The `(unsigned int)status` conversion, left shift, and `Py_BuildValue()` expression are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the VC6-compatible status type and preserved the status shift. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | 2; `_blake2/blake2s_impl.c` compiled, 39 sources compiled, first blocker `posixmodule.c(8741)` C2632/C2143/C2065 for `unsigned long long ustatus`; summary `total=40 compiled=39 cached=0 skipped=0`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after adding the focused assertion, before source edit | 1; expected RED: `os_waitpid_impl must use the VC6-compatible PY_LONG_LONG status type`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after source edit | 0; GREEN, `ALL TESTS PASSED`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | 2; `posixmodule.c` passed the `long long` spelling blocker. The next first stable blocker is the distinct C89 declaration-order error at `posixmodule.c(8741)` C2143/C2065 for the still post-statement `ustatus` declaration/use; summary `total=40 compiled=39 cached=0 skipped=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check` | 0; nested source diff clean. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `git -C cpython diff \| cmp - compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct C89 declaration-order defect at `posixmodule.c(8741)` for `ustatus`; do not broaden into the later `os_readlink_impl()` API/declaration diagnostics. + +## Latest revision (`posixmodule.c` `os_kill_impl()` C89 declaration-order slice) + +Date: 2026-09-23 01:56 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine with `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded `_blake2/blake2s_impl.c` frontier state was force-re-probed first and remained true: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(8177)` was the first stable blocker. MSVC6 reported C2275/C2065/C2146 for the Windows-local declarations in `os_kill_impl()`. One focused Codex coding-agent lane implemented only this TDD slice, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — hoisted the Windows `os_kill_impl()` locals (`result`, `sig`, `err`, and `handle`) into the opening declaration group and changed `sig` initialization to an assignment at its original evaluation point. Audit, signal conversion, error handling, process termination, cleanup, and return order are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the C89 declarations and preserved evaluation/error/cleanup order. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| Focused `os_kill_impl` assertion before source edit | 1; expected RED declaration/order assertion failure, as reported by the delegated lane. | +| Focused `os_kill_impl` assertion after source edit | 0; GREEN. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; `ALL TESTS PASSED`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c` compiled past line 8177. The next first stable blocker is `posixmodule.c(8741)` C2632/C2143/C2065 for the `long long` spelling in the `os_readlink_impl()` sequence; summary `total=40 compiled=39 cached=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check` | 0; nested source diff clean. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `git -C cpython diff \| cmp - compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct VC6 64-bit spelling defect at `posixmodule.c(8741)` in the `os_readlink_impl()` sequence; do not broaden into later cascading diagnostics. + +## Latest revision (`posixmodule.c` `os_spawnv_impl()` C89 `saved_errno` declaration slice) + +Date: 2026-09-22 23:47 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine with `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded `_blake2/blake2s_impl.c` frontier state was force-re-probed first and remained true: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(6699)` was the first stable blocker. MSVC6 reported C2143 for the mixed declaration `int saved_errno = errno;`, followed by C2065 at its use. The requested focused coding-agent delegation could not launch because the runtime lacks the `anthropic` package; no child changes were used. I then performed this one narrow TDD slice directly, preserving all local changes. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — hoisted `int saved_errno` into `os_spawnv_impl()`'s opening declaration group and changed the original post-thread assignment to `saved_errno = errno;`. The assignment, `free_string_array()` cleanup, errno restoration, and spawn-result error order are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the C89 declaration and preserved assignment/cleanup order. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | 2; `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and first blocker `posixmodule.c(6699)` C2143/C2065 for `saved_errno`; summary `total=40 compiled=39 cached=0`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after adding the focused assertion, before source edit | 1; expected RED: `os_spawnv_impl saved_errno declaration missing from opening group`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after source edit | 0; GREEN, `ALL TESTS PASSED`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | 2; `posixmodule.c` compiled past line 6699. The next first stable blocker is `posixmodule.c(8177)` C2275/C2065 for declarations after executable statements in `os_kill_impl()`; summary `total=40 compiled=39 cached=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh` | 0. | +| `bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed. | +| `git -C cpython diff --check` | 0; nested source diff clean. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct C89 declaration-order defect at `posixmodule.c(8177)` in `os_kill_impl()`; do not broaden into later cascading diagnostics. + +## Latest revision (`posixmodule.c` `parse_envlist()` C89 key/val declaration slice) + +Date: 2026-09-22 21:40 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before this run, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine with `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(5840)` was the first stable blocker. MSVC6 reported C2275/C2065 for the loop-local `PyObject *val` declaration. One focused Codex coding-agent lane implemented exactly this TDD slice, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — hoisted `PyObject *key, *val` into `parse_envlist()`'s opening declaration group and changed both loop declarations to assignments at their original `PyList_GetItem()` evaluation points. NULL checks, conversion order, cleanup, and error flow are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the C89 declarations and preserved `PyList_GetItem()`/evaluation/error/cleanup order. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| Focused parse-envlist assertion before source edit | 1; expected RED assertion failure: `parse_envlist must hoist key and val into the C89 declaration group and preserve PyList_GetItem/evaluation/error/cleanup order`. | +| Focused parse-envlist assertion after source edit | 0; GREEN, no output. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; `ALL TESTS PASSED`, including the new parse-envlist assertion. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; `posixmodule.c` compiled past line 5840. The next first stable blocker is `posixmodule.c(6699)` C2143 for `saved_errno`; summary `total=40 compiled=39 cached=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check` | 0; nested source diff clean. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct C89 declaration-order defect at `posixmodule.c(6699)` for `saved_errno`; do not broaden into later cascading diagnostics. + +## Latest revision (`posixmodule.c` VC6 reparse-tag constants slice) + +Date: 2026-09-22 19:31 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine with `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(5130)` was the first stable blocker. MSVC6 reported C2065 for the VC98 SDK's missing `IO_REPARSE_TAG_SYMLINK`; the paired mount-point constant was also unavailable. The requested focused coding-agent delegation could not launch because the runtime lacks the `anthropic` package; no child changes were used. I then performed this one narrow TDD slice directly, preserving all local changes. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — added `_MSC_VER < 1300`/`#ifndef` fallbacks for `IO_REPARSE_TAG_SYMLINK` (`0xA000000C`) and `IO_REPARSE_TAG_MOUNT_POINT` (`0xA0000003`). The existing `Py_DeleteFileW()` tag checks and modern SDK behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the VC6-only guarded constants and preservation of both tag checks. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | 2; first stable blocker `posixmodule.c(5130)` C2065 for `IO_REPARSE_TAG_SYMLINK`; summary `total=40 compiled=39 cached=0`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after adding the assertion, before source edit | 1; expected RED: `VC6 symlink reparse-tag fallback missing`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after source edit | 0; `ALL TESTS PASSED`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | 2; `posixmodule.c` compiled past line 5130. The next first stable blocker is `posixmodule.c(5840)` C2275/C2065 for the loop-local `PyObject *val`; summary `total=40 compiled=39 cached=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check` | 0; nested source diff clean. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct C89 declaration-order defect at `posixmodule.c(5840)` in the environment-building loop; do not broaden into later cascading diagnostics. + +## Latest revision (`posixmodule.c` `os__path_normpath_impl()` C89 declaration-order slice) + +Date: 2026-09-22 17:23 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine with `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(4614)` was the first stable blocker. MSVC6 reported C2275/C2146/C2065 for the mixed declarations in `os__path_normpath_impl()`. One focused Codex coding-agent lane implemented exactly this TDD slice, preserving local changes and avoiding commits, pushes, and publication. The lane reported the required focused RED test before the source edit and GREEN afterward; no independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — moved `len`, `buffer`, `norm_len`, `norm_path`, and `result` into the function's opening declaration group and changed initializers to assignments at their original evaluation points. Type checking, allocation, NULL handling, normalization, result creation, cleanup, and return order are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the C89-safe declaration group and preserved evaluation/error/cleanup order. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | 2; first blocker `posixmodule.c(4614)` C2275/C2146/C2065 for `len`, `buffer`, `norm_len`, `norm_path`, and `result`; summary `total=40 compiled=39 cached=0`. | +| Focused normpath structural assertion before source edit | 1 as reported by the delegated lane; expected RED. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation | 0; `ALL TESTS PASSED`, including the normpath assertion. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | 2; `posixmodule.c` compiled past line 4614. The next first stable blocker is `posixmodule.c(5130)` C2065 for unavailable `IO_REPARSE_TAG_SYMLINK`; summary `total=40 compiled=39 cached=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check` | 0; nested source diff clean. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct VC98 SDK/API blocker at `posixmodule.c(5130)`, `IO_REPARSE_TAG_SYMLINK`; do not broaden into later cascading diagnostics. + +## Latest revision (`posixmodule.c` `os__path_splitroot_impl()` C89 loop-counter slice) + +Date: 2026-09-22 15:14 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before this slice, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine with `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(4569)` was the first stable blocker. MSVC6 reported C2143/C2065 for the C99 `for (wchar_t *p = ...)` declaration in `os__path_splitroot_impl()`. One focused Codex coding-agent lane implemented exactly this TDD slice, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — hoisted `wchar_t *p` into `os__path_splitroot_impl()`'s opening declaration group and changed the loop initializer to `p = wcschr(...)`; slash replacement and loop evaluation order are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the C89-safe declaration and preserved copy/search/replace order. The delegated lane observed the required RED failure (exit 1) before the source edit and GREEN (exit 0) afterward. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | 2; first blocker `posixmodule.c(4569)` C2143/C2065 for the post-statement `wchar_t *p`; summary `total=40 compiled=39 cached=0`. | +| Focused split-root structural assertion before source edit | 1; expected RED assertion failure. | +| Focused split-root structural assertion after source edit | 0; expected GREEN result. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; `ALL TESTS PASSED`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | 2; `posixmodule.c` compiled past line 4569. The next first stable blocker is `posixmodule.c(4614)` C2275/C2146/C2065 for mixed declarations in `os__path_normpath_impl()` (`Py_ssize_t len`, `wchar_t *buffer`, and subsequent locals); summary `total=40 compiled=39 cached=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check` | 0; nested source diff clean. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct C89 declaration-order defect at `posixmodule.c(4614)` in `os__path_normpath_impl()`; do not broaden into later API or cascading diagnostics. + +## Latest revision (`posixmodule.c` `os__getfinalpathname_impl()` C89 `tmp` declaration slice) + +Date: 2026-09-22 13:07 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The current frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(4473)` was the first stable blocker, MSVC6 C2143/C2065 for the post-statement `wchar_t *tmp` declaration. One focused Codex coding-agent lane implemented exactly this TDD slice, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — hoisted `wchar_t *tmp` into `os__getfinalpathname_impl()`'s opening declaration group; the existing `PyMem_Realloc`, NULL-error, buffer-size, and target-path assignment sequence is unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the C89-safe declaration and preserved realloc/error/assignment order. The delegated lane reported the required RED failure before the source edit and GREEN afterward. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| Forced frontier probe before this slice | 2; first stable blocker `posixmodule.c(4473)` C2143/C2065 for `wchar_t *tmp`; `_blake2/blake2s_impl.c` compiled; summary `total=40 compiled=39 cached=0`. | +| Focused `tmp` assertion before source edit | 1; expected RED assertion failure. | +| Focused `tmp` assertion after source edit | 0; expected GREEN result. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; `ALL TESTS PASSED`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | 2; `posixmodule.c` compiled past line 4473. The next first stable blocker is `posixmodule.c(4569)` C2143/C2065/C2143 for the C99 `for (wchar_t *p = ...)` declaration; summary `total=40 compiled=39 cached=0`. | +| `bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/vc6-probe-stdint.test.sh && bash scripts/generate-getpath-frozen.test.sh` | 0; all applicable regression suites reported `ALL TESTS PASSED`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh` | 0. | +| `git -C cpython diff --check` | 0; nested source diff clean. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct C89/C99 loop-declaration defect at `posixmodule.c(4569)`; do not broaden into later cascading diagnostics. + +## Latest revision (`posixmodule.c` `os__getfullpathname_impl()` C89 declaration-order slice) + +Date: 2026-09-22 08:46 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with its pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`. + +The current frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(4402)` was the first stable blocker, MSVC6 C2275/C2065 for the post-statement `PyObject *str` declaration in `os__getfullpathname_impl()`. One focused Codex coding-agent lane implemented exactly this TDD slice, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — hoisted `PyObject *str` into `os__getfullpathname_impl()`'s opening declaration group and changed its initializer to an assignment at the original `PyUnicode_FromWideChar()` point. Freeing, error handling, `Py_SETREF`, and return behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the C89-safe declaration and preserved conversion/free/error/reference order. The delegated lane reported the required RED failure before the source edit; the assertion passed afterward. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | 2; first stable blocker `posixmodule.c(4402)` C2275/C2065 for `PyObject *str`; summary `total=40 compiled=39 cached=0`. `_blake2/blake2s_impl.c` compiled. | +| Focused `os__getfullpathname_impl()` assertion before source edit | 1 as reported by the delegated lane; expected RED. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation | 0; `ALL TESTS PASSED`, including the new `os__getfullpathname_impl` assertion. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | 2; `posixmodule.c` compiled past line 4402. The next first stable blocker is `posixmodule.c(4455)` C2065 for unavailable `VOLUME_NAME_DOS`; summary `total=40 compiled=39 cached=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check` | 0; nested source diff clean. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct VC98 SDK/API blocker at `posixmodule.c(4455)`, `VOLUME_NAME_DOS`; do not broaden into later cascading diagnostics. + +## Latest revision (`posix_getcwd()` `resobj` C89 declaration-order slice) + +Date: 2026-09-22 06:39 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with its pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`. + +The recorded frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(3874)` was the first stable blocker, MSVC6 C2275/C2065 for the post-statement `PyObject *resobj` declaration in `posix_getcwd()`. The requested focused coding-agent delegation failed before launch because the runtime lacks the `anthropic` package; no child changes were used. I then performed this one narrow TDD slice directly, preserving local changes. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — hoisted `PyObject *resobj` into `posix_getcwd()`'s opening declaration group and changed its initializer to an assignment at the original `PyUnicode_FromWideChar()` point. Freeing, `use_bytes` conversion, and return behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the C89-safe declaration and original result/free sequence. It failed before the source edit and passed afterward. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | 2; first stable blocker `posixmodule.c(3874)` C2275/C2065 for `PyObject *resobj`; summary `total=40 compiled=39 cached=0`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after adding the focused assertion, before the source edit | 1; expected RED: `posix_getcwd resobj declaration missing from opening group`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after the source edit | 0; `ALL TESTS PASSED`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | 2; `posix_getcwd()` compiled past the `resobj` blocker. The next first stable blocker is `posixmodule.c(4402)` C2275/C2065 for a later post-statement `PyObject *str`; summary `total=40 compiled=39 cached=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check` | 0; nested source diff clean. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct C89 declaration-order defect at `posixmodule.c(4402)` for `PyObject *str`; do not broaden into later API or cascading diagnostics. + +## Latest revision (`posixmodule.c` `_pystat_fromstructstat()` nanosecond declaration-order slice) + +Date: 2026-09-22 04:31 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with its pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`. + +The recorded frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(2493)` was the first stable blocker, an MSVC6 C2143 declaration-order error for `unsigned long ansec, mnsec, cnsec`. One focused Codex coding-agent lane implemented exactly this TDD slice, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — hoisted `unsigned long ansec, mnsec, cnsec` into `_pystat_fromstructstat()`'s opening declaration group and removed the later declaration. The feature-condition assignments and evaluation order are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the opening declaration group and original nanosecond assignment/branch sequence. The delegated lane reported the required RED failure before the source edit and GREEN afterward. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | 2; first stable blocker `posixmodule.c(2493)` C2143 for the post-statement nanosecond declaration; summary `total=40 compiled=39 cached=0`. | +| Focused nanosecond assertion before source edit | 1; expected RED assertion failure, as reported by the delegated lane. | +| Focused nanosecond assertion after source edit | 0; expected GREEN result. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; `ALL TESTS PASSED`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | 2; `posixmodule.c` compiled past line 2493. The next first stable blocker is `posixmodule.c(3874)` C2275/C2065 for a later mixed declaration (`PyObject *resobj`); summary `total=40 compiled=39 cached=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` | 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check` | 0; nested source diff clean. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct later C89 declaration-order defect at `posixmodule.c(3874)`; do not broaden into subsequent API or cascading diagnostics. + +## Latest revision (`posixmodule.c` `_pystat_fromstructstat()` VC6 64-bit spelling slice) + +Date: 2026-09-22 02:25 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`. + +The recorded frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(2472)`/`(2488)` were the first stable blockers, both MSVC6 `C2632` errors for `long long` spellings in `_pystat_fromstructstat()`. The requested coding-agent delegation was attempted but failed before launch because the runtime lacks the `anthropic` package; no child changes were used. I then performed this one narrow TDD slice directly, preserving all local changes. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — changed the two `_pystat_fromstructstat()` `static_assert` type spellings from `unsigned long long`/`long long` to `unsigned PY_LONG_LONG`/`PY_LONG_LONG`, preserving assertions and runtime behavior. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the VC6-compatible stat-size spelling. It failed before the source edit and passed afterward. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +||| Command | Exit/result | +|||---|---| +||| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +||| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | 2; first stable blockers `posixmodule.c(2472)` and `(2488)`, C2632 `long` followed by `long`; summary `total=40 compiled=39 cached=0`. | +||| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` before source edit | 1; expected RED: `VC6 stat-size assertion must use PY_LONG_LONG`, followed by the focused assertion failure. | +||| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after source edit | 0; `ALL TESTS PASSED`. | +||| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | 2; `posixmodule.c` compiled past both `long long` spelling blockers; next first stable blocker is `posixmodule.c(2493)` C2143, the post-statement `unsigned long ansec, mnsec, cnsec` declaration. Summary `total=40 compiled=39 cached=0`. | +||| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and shell syntax checks passed. | +||| `git -C cpython diff --check` | 0; nested source diff clean. | +||| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct C89 declaration-order defect at `posixmodule.c(2493)` in `_pystat_fromstructstat()`; do not broaden into later API or cascading diagnostics. + +## Latest revision (`posixmodule.c` `_pystat_fromstructstat()` C89 declaration-order slice) + +Date: 2026-09-22 00:18 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with pre-existing local changes preserved; the nested `cpython/` checkout remains detached at the baseline with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`. + +The recorded frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(2453)` was the first stable blocker, the C89 declaration-order family in `_pystat_fromstructstat()`. The requested coding-agent delegation was attempted but failed before launch because the runtime lacks the `anthropic` package; no child changes were used. I then performed this one narrow TDD slice directly, preserving all local changes. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — hoisted `PyObject *StatResultType` and `PyObject *v` before the initial assertion and changed their initializers to assignments at the original evaluation point. Evaluation order and behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the C89-safe declaration group and preserved assignment sequence. The assertion failed before the source edit and passed afterward. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +|| Command | Exit/result | +||---|---| +|| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +|| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | 2; `posixmodule.c(2453)` first stable blocker, C2143/C2275/C2065 for post-statement `StatResultType`/`v`; summary `total=40 compiled=39 cached=0`. | +|| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after adding the assertion, before source edit | 1; expected RED: `_pystat_fromstructstat declaration missing from opening group`. | +|| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after source edit | 0; `ALL TESTS PASSED`. | +|| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | 2; `posixmodule.c` compiled past line 2453; next first stable blocker is `posixmodule.c(2472)` C2632, `long long` spelling; summary `total=40 compiled=39 cached=0`. | +|| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and shell syntax checks passed. | +|| `git -C cpython diff --check` | 0; nested source diff clean. | +|| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct `posixmodule.c(2472)` `long long` defect family; do not broaden into later declaration, API, or cascading diagnostics. + +## Latest revision (`posixmodule.c` `fill_time()` C89 declaration-order slice) + +Date: 2026-09-21 22:10 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The top-level checkout remains on `main` with pre-existing local changes preserved; the nested `cpython/` checkout remains detached with its existing port edits. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`. + +The recorded frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(2400)` was the first stable blocker, the C89 declaration-order family in `fill_time()`. One focused Codex coding-agent lane implemented only this slice, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed; the resulting diff and all gates below were independently checked. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — hoisted `res`, `s_in_ns`, `ns_total`, `float_s`, `s`, and `ns_fractional` before the initial assertion; changed only the two object-creation declarations into assignments at their original evaluation point. Cleanup and evaluation order are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the opening declaration group, absence of mixed declarations, and preserved assignment/cleanup sequence. The delegated lane reported the required RED test before the source edit and GREEN afterward. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report only. + +**Exact validation commands and results:** + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | 0; `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | 2; `_blake2/blake2s_impl.c` compiled and `posixmodule.c(2400)` was first; C2143/C2275/C2065 followed for `res`, `s_in_ns`, `ns_total`, and `float_s`; summary `total=40 compiled=39 cached=0 skipped=0`. | +| focused `fill_time()` assertion before source edit | 1 as reported by the coding lane; expected RED declaration/order failure. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation | 0; `ALL TESTS PASSED`, including the new `fill_time()` assertion. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | 2; `posixmodule.c` compiled past `fill_time()`; next first stable blocker `posixmodule.c(2453)` is `_pystat_fromstructstat()`'s post-statement `PyObject *StatResultType` declaration (C2143/C2275/C2065). Summary `total=40 compiled=39 cached=0 skipped=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed. | +| `git -C cpython diff --check` | 0; nested source diff clean. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s /tmp/win9x-fill-time-diff.patch compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +This is compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine Python execution, and no real Windows 98 SE execution. The next narrow slice is the distinct C89 declaration-order defect at `posixmodule.c(2453)` in `_pystat_fromstructstat()`; do not broaden into the later `long long`, API, or cascading diagnostics. + +## Latest revision (`posixmodule.c` VC6 `fileAttributes` declaration/sentinel slice) + +Date: 2026-09-21 19:59 UTC + +Read `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before edits. Implemented exactly the recorded line-1996/1998 defect family in the existing checkout. All pre-existing local changes and the history below are preserved; nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. + +Files changed in this slice: + +- `cpython/Modules/posixmodule.c`: moved `DWORD fileAttributes` to the opening of its existing non-disk block, retaining the query after the unknown-file-type error check. Added an MS_WINDOWS/VC6-only, `#ifndef`-protected `INVALID_FILE_ATTRIBUTES` definition of `((DWORD)-1)`. Modern SDK definitions and query/cleanup behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh`: one focused assertion covering the declaration, original query order, and guarded sentinel fallback; observed RED before editing source and GREEN afterward. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch`: regenerated exactly from the complete nested diff, including all earlier local port changes. +- `PORT_STATUS.md`: prepended this entry only. + +Exact validation commands and exit codes (log paths are local `/tmp` artifacts): + +| Command | Exit/result | +|---|---| +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh > /tmp/win9x-fileattributes-red.log 2>&1` | 1; expected RED: only the new assertion failed, reporting `fileAttributes needs a C89 declaration, original-point query, and VC6-only SDK sentinel fallback`. Source had not yet been edited. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh > /tmp/win9x-fileattributes-green.log 2>&1` | 0; GREEN, `ALL TESTS PASSED`. This sandbox run is structural evidence; functioning Wine validation was repeated below. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh > /tmp/win9x-fileattributes-frontier.log 2>&1` (sandbox attempt) | 1; Wine could not create `/run/user/1000/wine/server-801-136fb0/lock` on the read-only filesystem; no compiler-frontier conclusion from this attempt. | +| Same forced-frontier command, retried with escalated Wine runtime access | 2; `total=40 compiled=39 cached=0 skipped=0`. First compiler error: `posixmodule.c(2400)` C2143, missing `;` before `type`, at `fill_time()`'s post-statement `int res = -1;`. No `fileAttributes` or `INVALID_FILE_ATTRIBUTES` diagnostics remain. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh > /tmp/win9x-fileattributes-green-live.log 2>&1` (with Wine runtime access) | 0; `ALL TESTS PASSED`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh > /tmp/win9x-fileattributes-stdint.log 2>&1` (with Wine runtime access) | 0; shell syntax and all five applicable regression suites passed. | +| `git -C cpython diff --check` | 0. | +| `git -C cpython diff | cmp - compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; byte-for-byte match. | +| `git diff --check > /tmp/win9x-fileattributes-diff-check.log` | 2; inherited whitespace-only context lines in the generated patch. Kept the required exact nested diff. | + +Snapshot/hash comparison confirms only `Modules/posixmodule.c` changed among the pre-existing modified nested source files. The next blocker is the distinct C89 declaration-order family in `fill_time()` beginning at line 2400; it was not changed. These are compile-probe results only: no linked `pythoncore`/`python.exe`, Wine Python execution, or Windows 98 runtime result is claimed. + +## Latest revision (`posixmodule.c` VC6 `FILE_ATTRIBUTE_TAG_INFO` API-gate slice) + +Date: 2026-09-21 17:49 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(1907)` was the first stable blocker because the VC98 SDK lacks `FILE_ATTRIBUTE_TAG_INFO`. One focused Codex coding-agent lane implemented only this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — added a VC6-local two-field tag-info shape and compiled out the unavailable `GetFileInformationByHandleEx`/`FileAttributeTagInfo` query block for `_MSC_VER < 1300`; the modern path is unchanged and existing `tagInfo` consumers remain valid. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the VC6 fallback and modern-path preservation. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | exit 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; first blocker `posixmodule.c(1907)` C2065/C2146/C2059 for `FILE_ATTRIBUTE_TAG_INFO`; summary `total=40 compiled=39 cached=0`. | +| focused TDD assertion before source edit | exit 1 as required by TDD. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation | exit 0; `ALL TESTS PASSED`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | exit 2; `posixmodule.c` compiled past the tag-info API family. The next first stable blocker is `posixmodule.c(1996)` C2275/C2146/C2065 for the post-statement `DWORD fileAttributes`; summary `total=40 compiled=39 cached=0`. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `git -C cpython diff \| cmp - compat/msvc600/cpython-3.11.16-vc6-headers.patch` | both exit 0; patch matches the complete nested source diff byte-for-byte. | +| `git -C cpython diff --check` | exit 0; nested source diff is clean. | +| top-level `git diff --check` | exit 2 only for inherited blank context lines in the generated compatibility patch; patch remains byte-for-byte reproducible. | + +**Current compile-frontier state:** `win32_xstat_impl()` now compiles past the VC98 `FILE_ATTRIBUTE_TAG_INFO`/`GetFileInformationByHandleEx` blocker under MSVC6 through Wine. The next narrow frontier is the distinct C89 declaration-order defect at `posixmodule.c(1996)`. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`posixmodule.c` `win32_wchdir()` C89 declaration-order slice) + +Date: 2026-09-21 15:42 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(1826)` was the first stable blocker, a C89 declaration-order error for `int is_unc_like_path`. One focused Codex coding-agent lane implemented only this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — moved `is_unc_like_path` into `win32_wchdir()`'s opening declaration group and assigned it at the original post-`GetCurrentDirectoryW()` point. Evaluation order and behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the declaration and assignment order. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | exit 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; `_blake2/blake2s_impl.c` compiled; first blocker `posixmodule.c(1826)` C2143/C2065 for the post-statement `is_unc_like_path` declaration; summary `total=40 compiled=39 cached=0`. | +| delegated `codex exec -s workspace-write --skip-git-repo-check ...` | exit 0; added the focused assertion, reported the required RED test, applied only `win32_wchdir()`, and regenerated the compatibility patch; no commit/push/publication. | +| focused TDD assertion before source edit | exit 1 as required by TDD. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation | exit 0; `ALL TESTS PASSED`. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; patch matches the complete nested source diff byte-for-byte. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | exit 2; `posixmodule.c` compiled past line 1826. The next first stable blocker is `FILE_ATTRIBUTE_TAG_INFO` at `posixmodule.c(1907)` (undeclared in the VC98 SDK), followed by cascading legacy API and C89 diagnostics; summary `total=40 compiled=39 cached=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | exit 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check` | exit 0; nested source diff is clean. | + +**Current compile-frontier state:** `win32_wchdir()` now compiles through its C89 declaration-order family under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is the distinct VC98 SDK/API blocker `FILE_ATTRIBUTE_TAG_INFO` at `posixmodule.c(1907)`. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`posixmodule.c` VC6 reparse-query API slice) + +Date: 2026-09-21 13:33 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded frontier was force-re-probed first and remained applicable: 39 sources compiled and `posixmodule.c` was first. The first stable blocker was the VC98 SDK's undeclared `FSCTL_GET_REPARSE_POINT` at `posixmodule.c(1582)`. One focused Codex coding lane implemented only this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — for `_MSC_VER < 1300`, `win32_get_reparse_tag()` now returns `FALSE` without referencing the unavailable reparse query operation; the modern helper body is unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the VC6 fallback and preserved modern body. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | exit 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; 39 sources compiled; first stable blocker `posixmodule.c(1582)` C2065, undeclared `FSCTL_GET_REPARSE_POINT`; later C89 declaration and legacy API diagnostics followed; summary `total=40 compiled=39 cached=0 skipped=0`. | +| focused delegated RED assertion | exit 1 as reported by the coding lane; expected assertion that the VC6 path must return `FALSE` without the unavailable operation. | +| focused delegated GREEN assertion | exit 0 as reported by the coding lane: `ok - VC6 reparse query returns FALSE and preserves the modern body`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | exit 0; `ALL TESTS PASSED`, including `VC6 reparse query returns FALSE and preserves the modern body` and `posix reparse query excludes the unavailable operation on VC6`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | exit 2; `posixmodule.c` compiled past `FSCTL_GET_REPARSE_POINT`. The next first stable blocker is `posixmodule.c(1826)` C2143, a later C89 declaration-order defect; legacy API blockers including `FILE_ATTRIBUTE_TAG_INFO` remain. Summary `total=40 compiled=39 cached=0 skipped=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | exit 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; the nested source diff is clean and the compatibility patch matches the complete nested source diff byte-for-byte. Top-level `git diff --check` still reports inherited whitespace-only lines in the generated compatibility patch. | + +**Current compile-frontier state:** `win32_get_reparse_tag()` now compiles past the VC6/VC98 missing-operation blocker under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is the distinct C89 declaration-order family at `posixmodule.c(1826)`. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`posixmodule.c` `Py_off_t` VC6 64-bit spelling slice) + +Date: 2026-09-21 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `posixmodule.c(1482)` was the first stable blocker, `C2632` because MSVC6 rejects `typedef long long Py_off_t`. One focused Codex coding lane implemented only this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. The lane reported the required focused RED assertion before the source edit and GREEN afterward; no independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — changed the Windows `Py_off_t` typedef from `long long` to CPython's `PY_LONG_LONG` alias. Non-Windows behavior is unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added a focused structural assertion for the VC6-compatible Windows `Py_off_t` spelling. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | exit 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; 39 sources compiled; first blocker `posixmodule.c(1482)` C2632 for `long long`; later declarations/APIs also failed; summary `total=40 compiled=39 cached=0`. | +| delegated focused TDD assertion before source edit | exit 1 as reported by the coding lane; expected failure `Windows Py_off_t must use PY_LONG_LONG`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation | exit 0; `ALL TESTS PASSED`, including `Windows Py_off_t uses PY_LONG_LONG`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after implementation | exit 2; `posixmodule.c` compiled past the `long long` spelling blocker. The next first stable blocker is `posixmodule.c(1582)` undeclared `FSCTL_GET_REPARSE_POINT`; later C89 declaration/API errors remain. Summary `total=40 compiled=39 cached=0`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | exit 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; nested source diff is clean and the compatibility patch matches the complete nested source diff byte-for-byte. | + +**Current compile-frontier state:** `posixmodule.c` now compiles through the VC6 `Py_off_t` 64-bit spelling family under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is the distinct legacy-SDK/API blocker at `posixmodule.c(1582)`, `FSCTL_GET_REPARSE_POINT`. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`posixmodule.c` VC6/Win9x SDDL header/API slice) + +Date: 2026-09-21 09:13 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded frontier was force-re-probed first and remained applicable through `_blake2/blake2s_impl.c`, `_operator.c`, and the prior 39 compiled sources. The first stable blocker was `posixmodule.c(39)`, fatal C1083 because the VC98 SDK has no `sddl.h`. One focused Codex coding lane implemented only this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. The lane reported the required focused RED assertion before the source edit and GREEN afterward; no independent review is claimed. + +This slice changed only: + +- `cpython/Modules/posixmodule.c` — excluded the NT-only `` include, security-descriptor allocation, SDDL conversion, and `LocalFree` cleanup for `_MSC_VER < 1300`; the VC6 path retains `CreateDirectoryW(path->wide, NULL)`. Modern MSVC behavior is unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added a focused structural assertion for the VC6 SDDL exclusion and null-security-attribute fallback. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | exit 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; `_blake2/blake2s_impl.c` compiled, 39 sources compiled; first blocker `posixmodule.c(39)`, fatal C1083 for missing `sddl.h`; summary `total=40 compiled=39 cached=0 skipped=0`. | +| focused delegated TDD assertion before source edit | exit 1 as reported by the coding lane; expected assertion failure for missing VC6 SDDL exclusion/fallback. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation | exit 0; `ALL TESTS PASSED`, including `posix mkdir excludes SDDL on VC6 and uses null security attributes`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after implementation | exit 2; `posixmodule.c` compiled past the missing `sddl.h` blocker. The next first stable blocker is `posixmodule.c(1482)` C2632 (`long long`), followed by missing legacy declarations/APIs including `FSCTL_GET_REPARSE_POINT` and `FILE_ATTRIBUTE_TAG_INFO`; summary `total=40 compiled=39 cached=0 skipped=0`. Existing warnings include `arraymodule.c(2247)` C4761, `audioop.c(1536)`/`(1609)` C4761, `faulthandler.c(542)` C4047, and `mathmodule.c(1336)` C4113/C4133. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | exit 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; nested source diff is clean and the compatibility patch matches the complete nested source diff byte-for-byte. | + +**Current compile-frontier state:** the `posixmodule.c` missing-`sddl.h` blocker is cleared under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is the distinct VC6 `long long` family at `posixmodule.c(1482)`. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`_operator.c` designated-initializer family slice) + +Date: 2026-09-21 07:04 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before this run, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 39 sources compiled, and `_operator.c` was the first blocker with the same C99 designated-initializer family at lines `(1814)` and `(1887)`. One focused external Codex coding lane implemented only this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. The lane reported the required RED focused assertions before the source edit and GREEN afterward; no independent review is claimed. + +This slice changed only: + +- `cpython/Modules/_operator.c` — converted `methodcaller_type_spec` and `operatormodule` from designated fields to declared-order positional fields. All values and module/type behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added focused structural assertions for both positional initializers. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | exit 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; `_blake2/blake2s_impl.c` compiled, 39 sources compiled; first blocker `_operator.c(1814)` and `(1887)`, both C2059 syntax errors for designated fields; summary `total=39 compiled=38 cached=0`. | +| delegated `codex exec -s workspace-write --skip-git-repo-check ...` | exit 0; added the two focused assertions, reported the expected RED failures, applied only the two `_operator.c` positional-initializer edits, regenerated the compatibility patch, and reported GREEN; no commit/push/publication. | +| focused RED assertions | exit 1 as required by TDD; both new positional-initializer assertions failed before the source edit. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | exit 0; `ALL TESTS PASSED`, including both new `_operator.c` assertions. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | exit 2; `_operator.c` compiled past both designated-initializer blockers. The next first blocker is `posixmodule.c(39)`, fatal C1083 because `sddl.h` is unavailable; summary `total=40 compiled=39 cached=0`. Existing warnings remain `arraymodule.c(2247)` C4761, `audioop.c(1536)`/`(1609)` C4761, `faulthandler.c(542)` C4047, and `mathmodule.c(1336)` C4113/C4133. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | exit 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check`; patch regeneration plus `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; nested source diff is clean and the compatibility patch matches the complete nested source diff byte-for-byte. | + +**Current compile-frontier state:** `_operator.c` now compiles through its remaining designated-initializer family under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is the distinct Win9x/SDK header blocker in `posixmodule.c(39)`: missing `sddl.h`. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`_operator.c` `methodcaller_new()` C89 declaration-order slice) + +Date: 2026-09-21 04:55 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 38 sources compiled, and `_operator.c(1596)` was the first stable blocker. One focused external Codex coding lane implemented only this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. The lane reported the required RED focused assertion before the source edit and GREEN afterward; no independent review is claimed. + +This slice changed only: + +- `cpython/Modules/_operator.c` — hoisted `_operator_state *state` into `methodcaller_new()`'s opening declaration group and changed its initializer to an assignment at the original `PyType_GetModuleState(type)` evaluation point. Allocation and methodcaller behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the C89-safe declaration and original-point assignment. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | exit 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; `_blake2/blake2s_impl.c` compiled, 38 sources compiled; first blocker `_operator.c(1596)` C2275/C2065/C2223/C2198 for the post-statement `_operator_state *state`; later designated-initializer blockers at `(1813)` and `(1886)`; summary `total=39 compiled=38 cached=0`. | +| delegated `codex exec -s workspace-write --skip-git-repo-check ...` | exit 0; added the focused assertion, reported the expected RED failure, applied only `methodcaller_new()`, regenerated the compatibility patch, and reported GREEN; no commit/push/publication. | +| focused RED assertion | exit 1 as required by TDD; `AssertionError: methodcaller_new needs an opening C89 state declaration and original-point assignment`. | +| focused GREEN assertion | exit 0; `ok - methodcaller_new uses an opening C89 state declaration and original-point assignment`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | exit 0; `ALL TESTS PASSED`, including the new `methodcaller_new` assertion. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | exit 2; `_operator.c` compiled past line 1596. The next first blockers are the distinct designated initializers at `(1814)` and `(1887)`; summary `total=39 compiled=38 cached=0`. Existing warnings remain `arraymodule.c(2247)` C4761, `audioop.c(1536)`/`(1609)` C4761, `faulthandler.c(542)` C4047, and `mathmodule.c(1336)` C4113/C4133. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | exit 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; nested source diff is clean and the compatibility patch matches the complete nested source diff byte-for-byte. | + +**Current compile-frontier state:** `methodcaller_new()` now compiles through its C89 declaration-order family under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is the distinct `_operator.c` designated-initializer family at lines 1814 and 1887. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`_operator.c` `attrgetter_type_spec` positional-initializer slice) + +Date: 2026-09-21 02:46 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the independently verified banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded `_blake2/blake2s_impl.c` sequence was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 38 sources compiled, and `_operator.c(1558)` was the first stable blocker. One focused external Codex coding lane implemented only this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. The lane reported the required RED focused assertion before the source edit and GREEN afterward; no independent review is claimed. + +This slice changed only: + +- `cpython/Modules/_operator.c` — converted `attrgetter_type_spec` from designated fields to declared-order positional fields; values and behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the positional initializer. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\\\home\\\\ubuntu\\\\projects\\\\python-win9x\\\\MSVC600\\\\VC98\\\\Bin\\\\VCVARS32.BAT && cl'` | exit 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; `_blake2/blake2s_impl.c` compiled, 38 sources compiled; first blocker `_operator.c(1558)` C2059 for the designated initializer; later blockers at `(1596)`, `(1813)`, and `(1886)`; summary `total=39 compiled=38 cached=0`. | +| delegated `codex exec --approve-for-me --skip-git-repo-check ...` | exit 0; added the focused assertion, reported the expected RED failure before the source edit, applied only `attrgetter_type_spec`, regenerated the compatibility patch, and reported GREEN; no commit/push/publication. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | exit 0; `ALL TESTS PASSED`, including `attrgetter_type_spec uses a C89-compatible declared-order positional initializer`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | exit 2; `_operator.c` compiled past line 1558. The next first blocker is the distinct C89 declaration-order family at `(1596)` for `_operator_state *state`; later designated-initializer blockers remain at `(1813)` and `(1886)`; summary `total=39 compiled=38 cached=0`. Existing warnings remain `arraymodule.c(2247)` C4761, `audioop.c(1536)`/`(1609)` C4761, `faulthandler.c(542)` C4047, and `mathmodule.c(1336)` C4113/C4133. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | exit 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check` | exit 0; nested source diff is clean. | +| `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; compatibility patch matches the complete nested source diff byte-for-byte. | + +**Current compile-frontier state:** `attrgetter_type_spec` now compiles through its designated-initializer family under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is `_operator.c(1596)`, the distinct `methodcaller_new()` C89 declaration-order family. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`_operator.c` `attrgetter_vectorcall()` C89 declaration-order slice) + +Date: 2026-09-21 00:38 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the independently verified banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded frontier was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 38 sources compiled, and `_operator.c(1402)` was the first stable blocker. One focused external Codex coding lane implemented only this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. The lane reported the required RED focused assertion before the source edit and GREEN afterward; no independent review is claimed. + +This slice changed only: + +- `cpython/Modules/_operator.c` — hoisted `Py_ssize_t nargs` into `attrgetter_vectorcall()`'s opening declaration group and changed its initializer to an assignment at the original evaluation point. Argument validation and call behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the C89-safe declaration and original-point assignment. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\\home\\ubuntu\\projects\\python-win9x\\MSVC600\\VC98\\Bin\\VCVARS32.BAT && cl'` | exit 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; `_blake2/blake2s_impl.c` compiled, 38 sources compiled; first blocker `_operator.c(1402)` C2275/C2146/C2065 for post-statement `Py_ssize_t nargs`; later blockers at `(1556)`, `(1594)`, `(1811)`, and `(1884)`; summary `total=39 compiled=38 cached=0 skipped=0`. | +| focused Codex lane | exit 0; added the focused assertion, reported the expected RED failure, applied only the `attrgetter_vectorcall()` declaration-order edit, ran GREEN, regenerated the compatibility patch, and made no commit/push/publication. | +| focused RED assertion | exit 1 as required by TDD; `AssertionError: attrgetter_vectorcall needs an opening C89 nargs declaration and original-point assignment`. | +| focused GREEN assertion | exit 0; `ok - attrgetter_vectorcall uses an opening C89 nargs declaration and original-point assignment`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | exit 0; `ALL TESTS PASSED`, including the new `attrgetter_vectorcall` assertion. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | exit 2; `_operator.c` compiled past line 1402. The next first blocker is the distinct designated initializer at `(1558)`; later blockers remain at `(1596)`, `(1813)`, and `(1886)`; summary `total=39 compiled=38 cached=0`. Existing warnings remain `arraymodule.c(2247)` C4761, `audioop.c(1536)`/`(1609)` C4761, `faulthandler.c(542)` C4047, and `mathmodule.c(1336)` C4113/C4133. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | exit 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check`; patch regeneration plus `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; nested source diff passed `--check` and the compatibility patch matches the complete nested source diff byte-for-byte. | + +**Current compile-frontier state:** `attrgetter_vectorcall()` now compiles through its C89 declaration-order family under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is `_operator.c(1558)`, the distinct designated-initializer family. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`_operator.c` `attrgetter_new()` C89 declaration-order slice) + +Date: 2026-09-20 22:30 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded `_blake2/blake2s_impl.c` sequence was force-re-probed first and remained applicable: `_blake2/blake2s_impl.c` compiled, 38 sources compiled, and `_operator.c(1311)` was the first stable blocker. One focused Codex coding lane implemented only this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. The lane reported the required RED focused assertion before the source edit and GREEN afterward; no independent review is claimed. + +This slice changed only: + +- `cpython/Modules/_operator.c` — hoisted `_operator_state *state` into `attrgetter_new()`'s opening declaration group and changed its initializer to an assignment at the original module-state lookup point. Attribute-chain construction and allocation behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the C89-safe declaration and original-point assignment. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call .../VCVARS32.BAT && cl'` | exit 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; `_blake2/blake2s_impl.c` compiled, 38 sources compiled; first blocker `_operator.c(1311)` C2275/C2065 for post-statement `_operator_state *state`; later blockers at `(1401)`, `(1555)`, `(1593)`, `(1810)`, and `(1883)`; summary `total=39 compiled=38 cached=0 skipped=0`. | +| `codex exec -s workspace-write --skip-git-repo-check ...` | exit 0; added the focused assertion, performed the requested RED/GREEN TDD cycle, applied only the `attrgetter_new()` declaration-order edit, regenerated the compatibility patch, and made no commit/push/publication. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | exit 0; `ALL TESTS PASSED`, including `attrgetter_new uses an opening C89 state declaration and original-point assignment`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | exit 2; `_operator.c` compiled past line 1311. The next first blocker is the distinct `itemgetter_vectorcall()` declaration-order family at `(1402)` with C2275/C2146/C2065; later blockers remain at `(1556)`, `(1594)`, `(1811)`, and `(1884)`; summary `total=39 compiled=38 cached=0 skipped=0`. Existing warnings remain `arraymodule.c(2247)` C4761, `audioop.c(1536)`/`(1609)` C4761, `faulthandler.c(542)` C4047, and `mathmodule.c(1336)` C4113/C4133. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` | exit 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check`; patch regeneration plus `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; nested source diff passed `--check` and the compatibility patch matches the complete nested source diff byte-for-byte. | + +**Current compile-frontier state:** `attrgetter_new()` now compiles through its C89 declaration-order family under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is `_operator.c(1402)`, the distinct `itemgetter_vectorcall()` declaration-order family. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`_operator.c` `itemgetter_type_spec` positional-initializer slice) + +Date: 2026-09-20 20:20 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded `_blake2/blake2s_impl.c` sequence was force-re-probed first and was no longer the active frontier: `_blake2/blake2s_impl.c` compiled, 38 sources compiled, and `_operator.c` remained first. The first stable blocker was the distinct C99 designated initializer in `itemgetter_type_spec` at `_operator.c(1189)`. The Hermes delegation tool failed before launch because the runtime lacks the `anthropic` package. One focused external Codex coding lane then implemented this exact TDD slice, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/_operator.c` — converted `itemgetter_type_spec` from designated fields to declared-order positional fields; values and behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the positional initializer. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; 38 sources compiled; first blocker `_operator.c(1189)` C2059 for the designated initializer; later blockers at `(1311)`, `(1401)`, `(1555)`, `(1593)`, `(1810)`, and `(1883)`; summary `total=39 compiled=38 cached=0 skipped=0`. | +| `codex exec -s workspace-write --skip-git-repo-check ...` | exit 0; added the focused assertion, performed the requested RED/GREEN TDD cycle, changed only `itemgetter_type_spec`, regenerated the compatibility patch, and made no commit/push/publication. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` | exit 0; `ALL TESTS PASSED`, including `itemgetter_type_spec uses a C89-compatible declared-order positional initializer`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | exit 2; `_operator.c` compiled past line 1189. The next first blocker is the distinct mixed declaration at `(1311)` (`_operator_state *state`); later blockers remain at `(1401)`, `(1555)`, `(1593)`, `(1810)`, and `(1883)`; summary `total=39 compiled=38 cached=0 skipped=0`. Existing warnings remain `arraymodule.c(2247)` C4761, `audioop.c(1536)`/`(1609)` C4761, `faulthandler.c(542)` C4047, and `mathmodule.c(1336)` C4113/C4133. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` | exit 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check`; patch regeneration plus `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; nested source diff passed `--check` and the compatibility patch matches the complete nested source diff byte-for-byte. | + +**Current compile-frontier state:** `itemgetter_type_spec` now compiles under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is `_operator.c(1311)`, the distinct C89 declaration-order family. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`_operator.c` `itemgetter_vectorcall()` C89 declaration-order slice) + +Date: 2026-09-20 18:11 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded frontier was force-re-probed first and remained applicable: 38 sources compiled and `_operator.c` remained first. The first stable defect family was MSVC6's rejection of `itemgetter_vectorcall()`'s post-statement `Py_ssize_t nargs` declaration at line 1081. The Hermes delegation path was unavailable because the runtime lacks the `anthropic` package. One focused Codex coding lane then implemented this exact TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/_operator.c` — hoisted `Py_ssize_t nargs` into `itemgetter_vectorcall()`'s opening declaration group and changed its initializer to an assignment at the original evaluation point. Argument validation and call behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the C89-safe declaration and original-point assignment. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +||| Command | Result | +||---|---| +|| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call .../VCVARS32.BAT && cl'` | exit 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +|| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; 38 sources compiled; first blocker `_operator.c(1081)` C2275/C2146/C2065 for post-statement `Py_ssize_t nargs`; summary `total=39 compiled=38 cached=0 skipped=0`. | +|| Hermes focused delegation | failed before launch: `The 'anthropic' package is required for the Anthropic provider`; no child files were used. | +|| `codex exec -s workspace-write --skip-git-repo-check ...` | exit 0; delegated lane added the focused assertion, reported the required RED focused failure before the source edit, applied only the `itemgetter_vectorcall()` declaration-order edit, and regenerated the compatibility patch; no commit, push, or publication. | +|| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation | exit 0; `ALL TESTS PASSED`, including `itemgetter_vectorcall uses an opening C89 nargs declaration and original-point assignment`. | +|| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after implementation | exit 2; `_operator.c` compiled past line 1081. The next first blocker is the distinct designated initializer at `_operator.c(1189)` (reported as line 1187 before the edit); later distinct blockers remain at `(1311)`, `(1401)`, `(1555)`, `(1810)`, and `(1883)`. Summary `total=39 compiled=38 cached=0 skipped=0`. Existing warnings remain `arraymodule.c(2247)` C4761, `audioop.c(1536)`/`(1609)` C4761, `faulthandler.c(542)` C4047, and `mathmodule.c(1336)` C4113/C4133. | +|| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` | exit 0; all applicable regression suites and shell syntax checks passed. | +|| `git -C cpython diff --check`; patch regeneration plus `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; nested source diff passed `--check` and the compatibility patch matches the complete nested source diff byte-for-byte. | + +**Current compile-frontier state:** `itemgetter_vectorcall()` now compiles through its C89 declaration-order family under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is the distinct `_operator.c` designated initializer at line 1189. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`_operator.c` `itemgetter_new()` C89 declaration-order slice) + +Date: 2026-09-20 16:05 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changes, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified banner remains `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded frontier was force-re-probed first and remained applicable: 38 sources compiled and `_operator.c` remained first. The first stable defect family was MSVC6's rejection of `itemgetter_new()`'s post-statement `_operator_state *state` declaration at line 1006. One focused Codex coding-agent delegation implemented exactly this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/_operator.c` — hoisted `_operator_state *state` into `itemgetter_new()`'s opening declaration group and changed its initializer to an assignment at the original module-state lookup point. Allocation and itemgetter behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the C89-safe declaration and original-point assignment. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +|| Command | Result | +|---|---| +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; 38 sources compiled; first blocker `_operator.c(1006)` C2275/C2065 for post-statement `_operator_state *state`; summary `total=39 compiled=38 cached=0 skipped=0`. | +| `codex exec -s workspace-write --skip-git-repo-check ...` | exit 0; delegated lane added the focused assertion, reported the required RED focused failure before the source edit, applied only the `itemgetter_new()` declaration-order slice, regenerated the compatibility patch, and ran GREEN; no commit, push, or publication. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation | exit 0; `ALL TESTS PASSED`, including `itemgetter_new uses an opening C89 state declaration and original-point assignment`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after implementation | exit 2; `_operator.c` compiled past line 1006. The next first blocker is `_operator.c(1081)` C2275/C2146/C2065 for `Py_ssize_t nargs`; later distinct designated-initializer blockers remain at `(1187)`, `(1553)`, `(1808)`, and `(1881)`. Summary `total=39 compiled=38 cached=0 skipped=0`. Existing warnings remain `arraymodule.c(2247)` C4761, `audioop.c(1536)`/`(1609)` C4761, `faulthandler.c(542)` C4047, and `mathmodule.c(1336)` C4113/C4133. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` | exit 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; nested source diff passed `--check` and the compatibility patch matches the complete nested source diff byte-for-byte. | + +**Current compile-frontier state:** `itemgetter_new()` now compiles through its C89 declaration-order family under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is `_operator.c(1081)`, the distinct `Py_ssize_t nargs` declaration-order family. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`_opcode.c` positional-initializer slice) + +Date: 2026-09-20 13:38 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changes, `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner remains `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded frontier was force-re-probed first and remained applicable: 37 sources compiled and `_opcode.c` remained first. The first stable defect was MSVC6's rejection of `opcodemodule`'s C99 designated initializer at line 104. One focused Codex coding-agent delegation implemented exactly this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/_opcode.c` — converted `opcodemodule` to declared-order positional fields, with explicit `NULL` values for omitted slots. Module metadata and behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the positional initializer. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; 37 sources compiled; first blocker `_opcode.c(104)` C2059 syntax error for `.m_name`; summary `total=38 compiled=37 cached=0 skipped=0`. | +| `codex exec -s workspace-write --skip-git-repo-check ...` | exit 0; delegated lane added the focused assertion, reported the required RED focused failure before the source edit, applied only the `_opcode.c` positional initializer, and ran GREEN; no commit, push, or publication. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation | exit 0; `ALL TESTS PASSED`, including `opcodemodule uses a C89-compatible declared-order positional initializer`. | +| `git diff --check` | exit 2 due only to pre-existing generated-patch trailing whitespace in `compat/msvc600/cpython-3.11.16-vc6-headers.patch`; source diff checks were run separately. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after implementation | exit 2; `_opcode.c` compiled successfully. The next first blocker is `_operator.c(1006)` with C89 mixed declaration errors, followed by distinct `_operator.c` designated initializers at `(1186)`, `(1552)`, `(1807)`, and `(1880)`; summary `total=39 compiled=38 cached=0 skipped=0`. Existing warnings remain `arraymodule.c(2247)` C4761, `audioop.c(1536)`/`(1609)` C4761, `faulthandler.c(542)` C4047, and `mathmodule.c(1336)` C4113/C4133. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh` | exit 0; all applicable regression suites and shell syntax checks passed. | +| `git -C cpython diff --check`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; nested source diff passed `--check` and the compatibility patch matches the complete nested source diff byte-for-byte. | + +**Current compile-frontier state:** `_opcode.c` now compiles under MSVC6 (`cl.exe` 12.00.8804) through Wine after its designated-initializer family was removed. The next narrow frontier is `_operator.c(1006)`, beginning with its distinct C89 declaration-order family. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`mmap_gfind()` C89 declaration-order slice) + +Date: 2026-09-20 09:21 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changes, `PORT_STATUS.md`, `README.md`, and `references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner remains `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded `_blake2/blake2s_impl.c` sequence was force-re-probed first and remained applicable: 36 sources compiled and `mmapmodule.c` remained first. The first stable defect family was MSVC6's rejection of the post-statement `Py_ssize_t res` declaration in `mmap_gfind()`. One focused Codex coding-agent delegation implemented exactly this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/mmapmodule.c` — hoisted `Py_ssize_t res` into `mmap_gfind()`'s opening declaration group and removed the later declaration. Search result assignments and control flow are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the C89-safe declaration and unchanged branch assignments. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; 36 sources compiled; first stable blocker `mmapmodule.c(357)` C2275/C2146/C2065 for post-statement `Py_ssize_t res`; later designated-initializer blockers remained at `(1207)` and `(1788)`; summary `total=37 compiled=36 cached=0 skipped=0`. | +| `codex exec -s workspace-write --skip-git-repo-check ...` | exit 0; delegated lane added the focused assertion and implemented only the `mmap_gfind()` declaration-order slice; no commit, push, or publication. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation | exit 0; `ALL TESTS PASSED`, including `mmap_gfind uses an opening C89 res declaration and original-point assignments`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after implementation | exit 2; `mmap_gfind()` compiled past the declaration-order family. The next first blockers are the distinct designated initializers at `mmapmodule.c(1207)` and `(1788)`; summary `total=37 compiled=36 cached=0`. Existing warnings remain `arraymodule.c(2247)` C4761, `audioop.c(1536)`/`(1609)` C4761, `faulthandler.c(542)` C4047, and `mathmodule.c(1336)` C4113/C4133. | +| `bash -n` plus public/core/PathCch/getpath/stdint/frontier regression suites | exit 0; all applicable suites passed and the focused frontier suite ended with `ALL TESTS PASSED`. | +| `git -C cpython diff --check`; patch regeneration plus `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; the nested source diff passed `--check` and the compatibility patch matches the complete nested source diff byte-for-byte. The generated patch retains pre-existing whitespace-only lines, so checking the patch file itself with outer `git diff --check` reports those inherited lines. | + +**Current compile-frontier state:** `mmap_gfind()` now compiles through its C89 declaration-order family under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is the distinct designated-initializer family in `mmapmodule.c` at lines 1207 and 1788. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`mmap_close_method()` C89 declaration-order slice) + +Date: 2026-09-20 07:13 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner remains `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded `_blake2/blake2s_impl.c` sequence was force-re-probed first and remained applicable: 36 sources compiled and `mmapmodule.c` remained first. The first stable defect family was MSVC6's rejection of post-statement declarations in `mmap_close_method()`. One focused Codex coding-agent delegation implemented exactly this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/mmapmodule.c` — hoisted the Windows-only `HANDLE map_handle`, `HANDLE file_handle`, and `char *data` declarations before executable statements, then assigned them at their original evaluation point. Resource cleanup order and behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the C89-safe declarations and original-point assignments. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; 36 sources compiled; first stable blocker `mmapmodule.c(194)` C2275/C2146/C2065 for post-statement `HANDLE` declarations, with later distinct blockers at `(352)`, `(1202)`, and `(1783)`; summary `total=37 compiled=36 cached=0 skipped=0`. | +| `codex exec -s workspace-write --skip-git-repo-check ...` | exit 0; delegated lane added the focused assertion, reported the required RED failure before the source edit, applied only the `mmap_close_method()` declaration-order slice, and reported GREEN; no commit, push, or publication. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation | exit 0; `ALL TESTS PASSED`, including `mmap_close_method uses C89-safe Windows declarations and original-point assignments`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after implementation | exit 2; `mmap_close_method()` compiled past the declaration-order family. The next first blocker is `mmapmodule.c(357)` C2275/C2146/C2065 for `Py_ssize_t res`; later distinct designated initializers remain at `(1207)` and `(1788)`. Summary `total=37 compiled=36 cached=0 skipped=0`. | +| `bash -n` plus public/core/PathCch/getpath/stdint/frontier regression suites | exit 0; all applicable suites passed and the focused frontier suite ended with `ALL TESTS PASSED`. | +| `git -C cpython diff --check`; patch regeneration plus `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; the nested source diff is clean and the compatibility patch matches the complete nested source diff byte-for-byte. | + +**Current compile-frontier state:** `mmap_close_method()` now compiles through its C89 declaration-order family under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is the distinct `Py_ssize_t res` declaration-order family in `mmapmodule.c(357)`. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`mmapmodule.c` MSVC6 `PY_LONG_LONG` spelling slice) + +Date: 2026-09-20 05:03 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified compiler banner remains `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded `_blake2/blake2s_impl.c` sequence was force-re-probed first and remained applicable: 36 sources compiled and `mmapmodule.c` remained first. The first stable defect family was MSVC6's rejection of `long long` spellings in the Windows mmap offset/size declarations and casts. One focused Codex coding-agent delegation implemented exactly this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/mmapmodule.c` — replaced the five active Windows `long long` declarations/casts with `PY_LONG_LONG`; behavior and arithmetic are unchanged. The explanatory comment remains untouched. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the mmap 64-bit spellings and size-combining casts. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; 36 sources compiled; first stable blocker `mmapmodule.c(103)` C2632 (`'long' followed by 'long' is illegal`), with later declaration-order/designated-initializer diagnostics; summary `total=37 compiled=36 cached=0 skipped=0`. | +| `codex exec -s workspace-write --skip-git-repo-check ...` | exit 0; delegated lane added the focused assertion, reported the required RED focused failure before the source edit, applied only the mmap `PY_LONG_LONG` spelling slice, and ran GREEN; no commit, push, or publication. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation | exit 0; `ALL TESTS PASSED`, including `mmap preserves signed 64-bit sizes and offsets using PY_LONG_LONG`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after implementation | exit 2; `mmapmodule.c` compiled past the `long long` family. The next first blocker is `mmapmodule.c(194)` C2275/C2146/C2065 for post-statement `HANDLE` declarations; later distinct blockers remain at `(352)`, `(1202)`, and `(1783)`. Summary `total=37 compiled=36 cached=0 skipped=0`. | +| `bash -n` plus public/core/PathCch/getpath/stdint/frontier regression suites | exit 0; all applicable suites passed and the focused frontier suite ended with `ALL TESTS PASSED`. | +| `git -C cpython diff --check`; patch regeneration plus `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; the nested source diff is clean and the compatibility patch matches the complete nested source diff byte-for-byte. | + +**Current compile-frontier state:** `mmapmodule.c` now compiles through its MSVC6 `long long` spelling family under `cl.exe` 12.00.8804 through Wine. The next narrow frontier is the distinct C89 declaration-order/API family at `mmapmodule.c(194)`. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`md5module.c` `_md5module` positional-initializer slice) + +Date: 2026-09-20 02:55 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; independently verified compiler banner: `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded `_blake2/blake2s_impl.c` sequence was force-re-probed first, but it was no longer the active frontier: 36 sources compiled and `md5module.c` remained first. The first stable blocker was the distinct C99 designated initializer in `_md5module` at line 612. One focused Codex coding-agent delegation implemented exactly this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/md5module.c` — replaced `_md5module`'s designated fields with declared-order positional fields, including the explicit `NULL` `m_doc` field. Values and behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the positional initializer. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; 35 sources compiled; first blocker `md5module.c(612)` C2059 `syntax error: '.'`; summary `total=36 compiled=35 cached=0 skipped=0`. Existing warnings remained in `arraymodule.c`, `audioop.c`, `faulthandler.c`, and `mathmodule.c`. | +| `codex exec -s workspace-write --skip-git-repo-check ...` | exit 0; delegated lane added the focused assertion, reported the required RED focused failure before the source edit, applied only the `_md5module` positional initializer, ran GREEN, and did not commit, push, or publish. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation | exit 0; `ALL TESTS PASSED`, including `_md5module uses a C89-compatible declared-order positional initializer`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | exit 2; `md5module.c` compiled past line 612. The next first blocker is `mmapmodule.c(103)` C2632 (`'long' followed by 'long' is illegal`), followed by distinct declaration-order and designated-initializer diagnostics; summary `total=37 compiled=36 cached=0 skipped=0`. | +| `bash scripts/vc6-probe.sh` | exit 0; the independent `Programs/python.c` probe still links no artifact and its compile-only `cl.exe` invocation exited 0. | +| `wine cmd /c 'call .../VCVARS32.BAT && cl'` | exit 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `bash -n` plus public/core/PathCch/getpath/stdint/frontier regression suites; `git -C cpython diff --check`; patch regeneration and `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; all applicable suites passed and the compatibility patch matches the complete nested source diff byte-for-byte. | + +**Current compile-frontier state:** `_md5module` now compiles through its designated-initializer family under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is the distinct `mmapmodule.c(103)` `long long` spelling family. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`md5module.c` `_md5_md5_impl` C89 declaration-order slice) + +Date: 2026-09-20 00:46 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the recorded compiler banner remains `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded `md5module.c` frontier was force-re-probed first and remained applicable: 35 sources compiled and `md5module.c` remained first. The first stable blocker was the distinct C89 declaration-order error at `_md5_md5_impl()` line 528 for `MD5State *st`; the later designated initializer at line 611 was left untouched. One focused Codex coding-agent delegation implemented exactly this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/md5module.c` — moved `MD5State *st` into the opening declaration group and changed its initializer to an assignment after buffer acquisition. Evaluation order and behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the declaration and original-point assignment. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; 35 sources compiled; first blocker `md5module.c(528)` C2143/C2065 for the post-statement `MD5State *st`; the later designated initializer was reported at `(611)`; summary `total=36 compiled=35 cached=0 skipped=0`. | +| `codex exec -s workspace-write --skip-git-repo-check ...` | exit 0; added the focused assertion, performed the requested red/green TDD cycle, made only the `_md5_md5_impl` declaration-order edit, and did not commit, push, or publish. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation | exit 0; `ALL TESTS PASSED`, including the new `_md5_md5_impl` assertion. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | exit 2; `md5module.c` compiled past line 528. The next first blocker is the distinct designated initializer at `(612)`; summary `total=36 compiled=35 cached=0 skipped=0`. Existing warnings remain `arraymodule.c(2247)` C4761, `audioop.c(1536)`/`(1609)` C4761, `faulthandler.c(542)` C4047, and the `mathmodule.c(1336)` C4113/C4133 pair. | +| full applicable regression command (`bash -n` plus public/core/PathCch/getpath/stdint/frontier suites) | exit 0; all applicable suites passed. | +| `git -C cpython diff --check`; patch regeneration plus `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; the compatibility patch matches the complete nested source diff byte-for-byte. | + +**Current compile-frontier state:** `_md5_md5_impl()` now compiles through its C89 declaration-order family under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is the distinct `md5_type_spec`-adjacent designated initializer at `md5module.c(612)`. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`md5module.c` `md5_type_spec` positional-initializer slice) + +Date: 2026-09-19 22:39 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before changing anything, `PORT_STATUS.md`, `README.md`, and `references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the verified banner remains `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded frontier was force-re-probed first and remained applicable: 35 sources compiled and `md5module.c` remained first. The prior `MD5_INT64` defect was already cleared. The first stable blocker was the distinct C99 designated initializer in `md5_type_spec` at line 498. One focused Codex coding-agent delegation implemented exactly this TDD slice in the existing checkout, preserving local changes and avoiding commits, pushes, and publication. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/md5module.c` — replaced `md5_type_spec`'s designated fields with declared-order positional fields, including the zero `itemsize` field; values and behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion for the positional initializer. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +| Command | Result | +|---|---| +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; 35 sources compiled; first blocker `md5module.c(498)` C2059 for the designated initializer, followed by declaration-order errors at `(527)` and another designated initializer at `(610)`; summary `total=36 compiled=35 cached=0 skipped=0`. | +| `codex exec -s workspace-write --skip-git-repo-check ...` | exit 0; added the focused assertion, performed the requested red/green TDD cycle, made only the `md5_type_spec` positional-initializer edit, and did not commit, push, or publish. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after implementation | exit 0; `ALL TESTS PASSED`, including the new `md5_type_spec` assertion. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after this slice | exit 2; `md5module.c` compiled past line 498. Next first blocker is the distinct C89 declaration-order error at `(527)` for `MD5State *st`; the later designated initializer remains at `(611)`; summary `total=36 compiled=35 cached=0 skipped=0`. Existing warnings remain `arraymodule.c(2247)` C4761, `audioop.c(1536)`/`(1609)` C4761, `faulthandler.c(542)` C4047, and the `mathmodule.c(1336)` C4113/C4133 pair. | +| full applicable regression command (`bash -n` plus public/core/PathCch/getpath/stdint/frontier suites) | exit 0; all applicable suites passed. | +| `git -C cpython diff > compat/msvc600/cpython-3.11.16-vc6-headers.patch`; `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; the compatibility patch matches the complete nested source diff byte-for-byte. `git -C cpython diff --check` also exited 0. | + +**Current compile-frontier state:** `md5_type_spec` now compiles through its designated-initializer family under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is the distinct `MD5State *st` declaration-order family at `md5module.c(527)`. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + +## Latest revision (`md5module.c` `MD5_INT64` VC6 64-bit spelling slice) + +Date: 2026-09-19 20:30 UTC + +Baseline remains CPython `v3.11.16` at commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Before this run, `PORT_STATUS.md`, `README.md`, and `references/python-3-win9x-msvc6.md` were read. The nested `cpython/` checkout remains detached with all pre-existing local port edits preserved. Nothing was reset, cleaned, stashed, discarded, committed, pushed, or published. MSVC6 remains external/local-only through Wine using `WINEPREFIX=/home/ubuntu/.wine-win9xport`; the independently verified banner is `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. + +The recorded `mathmodule.c` `fast_comb_limits2` frontier was force-re-probed first and remained applicable: 35 sources compiled and `md5module.c` was the first blocker. The first stable defect was the distinct VC6 `long long` spelling failure at `md5module.c(36)` C2632. One focused Codex coding-agent delegation implemented exactly this TDD slice in the existing checkout; it preserved local changes and did not commit, push, or publish. No independent review is claimed. + +This slice changed only: + +- `cpython/Modules/md5module.c` — changed `typedef long long MD5_INT64` to `typedef PY_LONG_LONG MD5_INT64`; MD5 layout and behavior are unchanged. +- `scripts/vc6-probe-pythoncore-frontier.test.sh` — added one focused structural assertion requiring the compiler-compatible typedef. +- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated from the complete nested `git -C cpython diff`. +- `PORT_STATUS.md` — this report. + +**Exact command results:** + +|| Command | Result | +|---|---| +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` before this slice | exit 2; 35 sources compiled; first blocker `md5module.c(36)` C2632 for `long long`; later blockers at `(498)` designated initializer, `(527)` mixed declaration, and `(610)` designated initializer; summary `total=36 compiled=35 cached=0 skipped=0`. | +| `codex exec -s workspace-write --skip-git-repo-check ...` | exit 0; added the focused assertion, ran the required RED test, made the minimal typedef edit, and ran GREEN; no commit/push/publish. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after adding the assertion, before source edit | exit 1 as required by TDD; only the new assertion failed: `MD5_INT64 must use CPython's compiler-compatible PY_LONG_LONG`. | +| `bash scripts/vc6-probe-pythoncore-frontier.test.sh` after source edit | exit 0; `ALL TESTS PASSED`. | +| `VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` after source edit | exit 2; `md5module.c` compiled past the `long long` family. Next first blocker is the distinct designated initializer at `(498)`, followed by declaration-order errors at `(527)` and another designated initializer at `(610)`; summary `total=36 compiled=35 cached=0 skipped=0`. Existing warnings remain `mathmodule.c(1336)` C4113/C4133, `arraymodule.c(2247)` C4761, `audioop.c(1536)`/`(1609)` C4761, and `faulthandler.c(542)` C4047. | +| `bash -n` plus public/core/PathCch/getpath/stdint/frontier regression suites | exit 0; all applicable suites passed and the focused frontier suite ended with `ALL TESTS PASSED`. | +| `git diff --check`; `git -C cpython diff --check`; patch regeneration plus `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | exit 0; nested source diff is clean and the compatibility patch matches the complete nested source diff byte-for-byte. | + +**Current compile-frontier state:** `md5module.c` now compiles through the distinct VC6 `long long` spelling family under MSVC6 (`cl.exe` 12.00.8804) through Wine. The next narrow frontier is the designated initializer at `md5module.c(498)`. This remains compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, no Wine execution of Python, and no real Windows 98 SE execution. + ## Latest revision (`mathmodule.c` `perm_comb_small` `fast_comb_limits2` C89 declaration-order slice) Date: 2026-09-19 18:22 UTC @@ -4123,3 +7413,378 @@ file is seen, so the true scope is not yet known. Recommended next steps: declarations (declaration-order slice) and replace compound-literal/ ## Latest revision (`mathmodule.c` guarded VC6 `cbrt` fallback slice) +--- + +## Diagnostic-only confirmation of Win32 loader-search constants frontier + +Date: 2026-09-25 02:50 UTC + +Before probing, read `PORT_STATUS.md`, `README.md`, and the active-profile reference `skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md`. Verified CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The outer checkout already had modifications to this status, the generated compatibility patch, and the frontier regression script; the nested CPython checkout already had extensive local port edits. Preserved all existing state. No reset, clean, stash, discard, commit, push, or publication occurred. + +Attempted the requested single focused coding-agent delegation for read-only frontier review, but the delegation tool failed before launch because the runtime lacks the required `anthropic` package. No child ran and no independent review is claimed. Since the actual frontier is an unresolved Win9x-visible API-contract choice, no implementation delegation or source edit was appropriate; this run is diagnostics-only. + +Forced a fresh MSVC600 source-list probe. `_blake2/blake2s_impl.c` still compiles, as do 39 of 40 sources. The first stable blocker is unchanged: five C2065 diagnostics at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. They are exported as Windows `os` constants; inventing fallback values or omitting exports would alter the Win9x-visible API contract. Later C89 diagnostics begin at line 16173 and remain separate. No compatibility behavior was guessed or changed. + +Files changed by this run: `PORT_STATUS.md` only. No CPython source, test, or compatibility patch was modified. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; preserved pre-existing outer and nested modifications; baseline remains `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; `_blake2/blake2s_impl.c` compiled; 39/40 sources compiled. First errors are the five C2065 loader-search constants at `posixmodule.c(15856)`–`(15860)`; later C89 diagnostics begin at 16173. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\home\ubuntu\projects\python-win9x\MSVC600\VC98\Bin\VCVARS32.BAT && cl'` | 0; banner `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax and all applicable regression suites passed. | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested whitespace check passed, generated patch reproduces full nested diff byte-for-byte, and non-generated outer diff check passed. | + +This is compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next step: resolve and document intended Win9x `os` API behavior for the five Windows-7-era loader-search constants, then add a focused failing regression test before any source change. Keep later C89 diagnostics separate. + +--- +## Latest revision (diagnostic-only forced compiler frontier confirmation) + +Date: 2026-09-25 15:15 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before acting. Preserved the pre-existing outer modifications to this status, the compatibility patch, and the frontier regression script, along with all nested CPython edits. The nested checkout remains detached at CPython `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. No reset, clean, stash, discard, commit, push, or publication occurred. + +The `_blake2/blake2s_impl.c` compile state is still true, but it is not the active frontier: a forced fresh source-list probe compiled 39 of 40 listed sources, including `_blake2/blake2s_impl.c`. The first stable blocker remains five C2065 undeclared identifiers in `posixmodule.c(15856)`–`(15860)`: `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. These are exported as `_LOAD_LIBRARY_SEARCH_*` integer constants by the Windows `os` module. Later C89 errors begin at `posixmodule.c(16173)` and are separate. Since deciding whether to expose fallback values or suppress exports changes the Win9x-visible API contract, and the available evidence does not establish that intended behavior, this run made no source/test/patch implementation and delegated no coding agent. This is compiler-probe progress only. + +Files changed in this run: `PORT_STATUS.md` only. Existing compatibility work remains preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch` | 0; outer checkout already had modifications to `PORT_STATUS.md`, `compat/msvc600/cpython-3.11.16-vc6-headers.patch`, and `scripts/vc6-probe-pythoncore-frontier.test.sh`. | +| `git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; existing nested edits preserved; detached at `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; total=40, compiled=39, cached=0, skipped=0. `_blake2/blake2s_impl.c` compiled. First stable errors are the five C2065 loader-search identifiers at `posixmodule.c(15856)`–`(15860)` above; later C89 diagnostics start at 16173. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax and all applicable regression suites passed. | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; nested diff passes whitespace checks and the generated compatibility patch matches the complete nested diff byte-for-byte. | +| `git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; non-generated outer diff passes whitespace check. | + +No `pythoncore.dll`/`python.exe` link, Wine interpreter execution, or real Windows 98 SE execution is verified. Next: establish the intended Win9x-visible behavior/API contract for the five loader-search constants from authoritative API evidence before writing a focused failing test and making any source change. Keep the later C89 diagnostics separate. + +--- +## Latest revision (forced fresh VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-25 17:18 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before acting. Preserved all pre-existing outer and nested modifications. CPython remains detached at baseline tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. No reset, clean, stash, discard, commit, push, or publication occurred. + +Re-forced the full source-list probe before deciding whether to continue the earlier `_blake2/blake2s_impl.c` sequence. That file still compiles, but is not the current frontier: 39/40 listed sources compile. First stable blocker remains five C2065 undeclared identifiers in `posixmodule.c(15856)`–`(15860)`: `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. These are exported as `_LOAD_LIBRARY_SEARCH_*` constants by the Windows `os` module initialization. Arbitrary fallback values or suppressing those exports would determine Win9x-visible API behavior, still not established by available evidence. Therefore this run was diagnostic-only; no source/test edits or coding-agent delegation were appropriate. Separate C89 diagnostics begin at line 16173 and are not addressed. + +Files changed by this run: `PORT_STATUS.md` only. Existing work in the compatibility patch, regression script, and detached CPython checkout was preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git rev-parse HEAD && git remote -v` | 0; outer pre-existing changes remain; baseline checkout untouched. | +| `git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && rg -n -C 4 'LOAD_LIBRARY_SEARCH_(DEFAULT_DIRS|APPLICATION_DIR|SYSTEM32|USER_DIRS|DLL_LOAD_DIR)|_LOAD_LIBRARY_SEARCH_' cpython/Modules/posixmodule.c` | 0; nested pre-existing edits preserved; confirmed tag/commit and the five exports in the Windows module block. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; total=40, compiled=39, cached=0, skipped=0. `_blake2/blake2s_impl.c` compiles. First errors are the five C2065 diagnostics above; separate C89 diagnostics begin at line 16173. | + +Compiler-probe progress only. No `pythoncore.dll`/`python.exe` link, Wine interpreter execution, or real Windows 98 SE execution is verified. Next: obtain authoritative API evidence establishing the intended Win9x behavior for these five exported constants; then add a focused failing regression test before any source change. Keep later C89 diagnostics separate. + +--- +## Latest revision (forced frontier re-probe; loader-search constants remain first blocker) + +Date: 2026-09-26 03:34 UTC + +Before probing, read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference at `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md`. Preserved all existing work: outer modifications to this status, the generated compatibility patch, and the frontier regression script; detached `cpython/` checkout remains locally modified at baseline tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. No reset, clean, stash, discard, commit, push, or publication occurred. + +Forced a fresh source-list probe to test whether the recorded `_blake2/blake2s_impl.c` frontier was still active. It is not: the file compiles, and 39/40 listed sources compile. First stable blocker is five C2065 errors in `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later C89 diagnostics begin at `posixmodule.c(16173)` and are a distinct family. Since the first blocker concerns Win9x-visible `os` constants whose intended API contract is not established, no implementation or coding-agent delegation was appropriate; no source/test edit was made. This is diagnostic-only. + +Files changed this run: `PORT_STATUS.md` only. All prior CPython source edits, test edits, and generated compatibility patch were preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git rev-parse HEAD` | 0; outer `main...gitea/main`; pre-existing modifications remain in `PORT_STATUS.md`, compatibility patch, and frontier test. | +| `git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; detached, locally modified checkout at `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; total=40, compiled=39, cached=0, skipped=0. `_blake2/blake2s_impl.c` compiled. First five diagnostics are C2065 for the loader-search constants listed above; next distinct C89 family starts at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; nested whitespace check passed and generated patch matches the complete nested diff byte-for-byte. | +| `git diff --check -- . ':!compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; non-generated outer diff whitespace check passed. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next: resolve the intended Win9x `os` API contract for the five exported loader-search constants using authoritative Windows API evidence; then add a focused failing regression before any source change. Keep subsequent C89 diagnostics separate. + +--- +## Latest revision (scheduled forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-27 12:38 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference before probing. Preserved all existing outer and nested modifications; no reset, clean, stash, discard, commit, push, or publication. CPython baseline remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The recorded `_blake2/blake2s_impl.c` state remains true, but it is not the current frontier, so no implementation subagent was appropriate. + +Forced a fresh compiler probe. It compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics remain C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later separate C89 diagnostics start at line 16173 (`PyObject ScandirIteratorType`), followed by line 16220 (`PyObject list`) and 16224 (`trace`). No source/test implementation was safe because the Win9x-visible contract for the five exported constants remains unresolved; this run was diagnostic-only. + +Files changed by this run: `PORT_STATUS.md` only. Existing changes to the generated compatibility patch, frontier regression script, and nested CPython sources were preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; existing modifications preserved; baseline/tag unchanged. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, `_blake2/blake2s_impl.c` compiled; first diagnostics are the five C2065 constants above; later C89 diagnostics begin at `posixmodule.c(16173)`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check` | 0. | +| `cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; generated compatibility patch matches the complete nested diff byte-for-byte. | +| `git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; outer non-generated diff check passed. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish the intended Win9x-visible contract for the five exported loader-search constants using authoritative API evidence; then add a focused failing test before any source change. Keep later C89 diagnostics separate. + +--- +## Latest revision (scheduled forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-27 14:42 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile legacy-toolchain reference before probing. Preserved all existing outer and nested modifications; no reset, clean, stash, discard, commit, push, or publication. CPython remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The recorded `_blake2/blake2s_impl.c` state remains true, but it is not the active frontier. No implementation subagent or source change was appropriate: the first blocker is still the unresolved Win9x-visible contract for the five exported loader-search constants. + +Forced a fresh VC6 source-list probe: 39/40 listed sources compiled (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. The first stable diagnostics remain C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Later C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), followed by line 16220 (`PyObject list`) and line 16224 (`trace`); these are separate and were not modified. + +Files changed by this run: `PORT_STATUS.md` only. Existing edits to the generated compatibility patch, frontier regression script, and nested CPython source were preserved. + +Exact commands and results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch; git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match` | 0; existing modifications preserved; baseline/tag unchanged. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled; `_blake2/blake2s_impl.c` compiled; first C2065 errors are the five loader-search constants above; subsequent C89 diagnostics begin at line 16173. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check; cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested whitespace check passed, generated patch still matches the complete nested diff byte-for-byte, and outer non-generated diff check passed before this status entry. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish the intended Win9x-visible contract for the five exported loader-search constants from authoritative API evidence; then write a focused failing test before changing source. Keep subsequent C89 diagnostics separate. + +--- +## Latest revision (scheduled forced VC6 frontier confirmation; loader-search constants remain blocker) + +Date: 2026-09-28 07:12 UTC + +Read `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before acting. Preserved all existing outer and nested modifications; no reset, clean, stash, discard, overwrite of unrelated work, commit, push, or publication. CPython baseline remains tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. A forced fresh source-list probe confirms `_blake2/blake2s_impl.c` compiles; it is not the active frontier. The first blocker remains five undeclared loader-search constants in `posixmodule.c`; because they are exported `os` constants and the Win9x-visible contract remains unresolved, this run made no speculative source/test changes and did not delegate implementation. + +The probe compiled 39/40 sources (`total=40 compiled=39 cached=0 skipped=0`). First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Distinct later C89 diagnostics begin at line 16173 (`PyObject ScandirIteratorType`), then line 16220 (`list`) and 16224 (`trace`); left untouched. + +Files changed this run: `PORT_STATUS.md` only. Existing compatibility patch, regression tests, and nested CPython edits remain preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled. First stable diagnostics are the five C2065 errors above; distinct later C89 diagnostics follow. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all wine cmd /c 'call Z:\home\ubuntu\projects\python-win9x\MSVC600\VC98\Bin\VCVARS32.BAT && cl'` | 0; `Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested diff clean, compatibility patch matches complete nested diff byte-for-byte, outer non-generated diff clean. | +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; baseline remains `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: establish the intended Win9x-visible contract for the five exported loader-search constants from authoritative API evidence; then add a focused failing test before source changes. Keep later C89 diagnostics separate. + +--- +## Latest revision (fresh forced frontier check; posixmodule remains first blocker) + +Date: 2026-09-28 19:39 UTC + +Read `PORT_STATUS.md`, `README.md`, and the active-profile CPython/Win9x/MSVC6 reference. Preserved all pre-existing work, including the extensive detached CPython edits and outer modifications; no reset, clean, stash, discard, commit, push, or publication. Baseline remains CPython tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. `_blake2/blake2s_impl.c` still compiles and is not the current frontier. A fresh forced probe reconfirmed the first stable blocker in `posixmodule.c`; the Win9x-visible contract for its five exported loader-search constants remains unspecified in project evidence, so no speculative implementation slice was made and no coding subagent was appropriate. + +Fresh probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Distinct later C89-related diagnostics begin at line 16173 (`ScandirIteratorType`), then 16220 (`list`) and 16224 (`trace`); left untouched. + +Files changed this run: `PORT_STATUS.md` only. No CPython source or test edits. Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && date -u '+%Y-%m-%d %H:%M UTC'` | 0; existing changes preserved; baseline `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`; `2026-09-28 19:39 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; 39/40 compiled, zero cached; first stable diagnostics are the five C2065 constants above; subsequent separate C89 diagnostics remain. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax checks and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch` | 0; nested diff clean; tracked compatibility patch matches complete nested diff byte-for-byte. | +| `git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; outer non-generated diff check passed. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution is verified. Next frontier: resolve the intended Win9x-visible contract for the five exported constants from authoritative API evidence; then write a focused failing test before a narrowly scoped implementation. Keep subsequent C89 diagnostics distinct. + +--- +## Latest revision (scheduled forced fresh VC6 frontier recheck; loader-search constants remain first blocker) + +Date: 2026-09-29 08:00 UTC + +Read `PORT_STATUS.md`, `README.md`, and `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before probing. Preserved all existing outer and nested changes. No reset, clean, stash, discard, overwrite of unrelated work, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. `_blake2/blake2s_impl.c` remains successful but is not the active frontier. Forced fresh probe reconfirmed the first blocker in `posixmodule.c`; the Win9x API contract for the five exported loader-search constants is unresolved, so this was diagnostic-only and no coding subagent or implementation change was appropriate. + +The probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate subsequent C89 diagnostics start at line 16173 (`ScandirIteratorType`), followed by errors at 16220 (`list`), 16224 (`trace`), and 16246; untouched. + +Files changed this run: `PORT_STATUS.md` only. Existing compatibility patch changes, frontier test edits, and nested CPython changes were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && git -C cpython status --short --branch && date -u '+%Y-%m-%d %H:%M UTC'` | 0; baseline/tag unchanged; existing nested modifications preserved; `2026-09-29 08:00 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; fresh forced probe compiled 39/40, zero cached; first diagnostics are the five C2065 loader-search constants above; separate C89 diagnostics follow. Full log: `/home/ubuntu/.hermes/profiles/maxwell/cache/scratch/python-win9x-frontier-20260929-0800.log`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and syntax checks passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested and outer diff checks passed; generated compatibility patch matches the complete nested diff byte-for-byte. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants; then write a focused failing test before any source edit. Keep later C89 blockers separate. + +--- +## Latest revision (scheduled forced fresh VC6 frontier recheck; loader-search constants remain first blocker) + +Date: 2026-09-29 12:15 UTC + +Read `PORT_STATUS.md`, `README.md`, and the project legacy-toolchain reference before probing. The reference is at `/home/ubuntu/.hermes/profiles/maxwell/skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md`; an initial lookup using the default-profile skill path failed (exit 1), then the active-profile reference was read. Preserved all existing work. No reset, clean, stash, discard, commit, push, or publication. CPython remains detached at `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The recorded `_blake2/blake2s_impl.c` compile state is true, but that source is not the active frontier. Forced fresh probe confirms `posixmodule.c` remains the first blocker. Its Win9x-visible behavior for five exported loader-search constants remains unresolved, so this was diagnostic-only; no implementation subagent or speculative edit was appropriate. + +Fresh probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics: C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Distinct subsequent C89 diagnostics begin at line 16173 (`PyObject`/`ScandirIteratorType`), then lines 16220 (`PyObject`/`list`), 16224 (`trace` and cascading errors), and 16246. None were changed. + +Files changed this run: `PORT_STATUS.md` only. All existing outer changes and extensive nested CPython edits were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch && git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match` | 0; existing edits preserved; baseline remains `v3.11.16` / `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh probe compiled 39/40, zero cached; first diagnostics are the five C2065 loader-search constants above; later C89 diagnostics remain. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites and syntax checks passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch && git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested and outer whitespace checks passed; compatibility patch matches complete nested diff byte-for-byte. | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants, then add a focused failing test before a narrowly scoped source change. Keep the distinct later C89 blockers separate. + +--- +## Latest revision (scheduled forced fresh VC6 frontier confirmation; posixmodule remains first blocker) + +Date: 2026-09-30 02:41 UTC + +Read `PORT_STATUS.md`, `README.md`, and `python-3-win9x-msvc6.md` before probing. Preserved all existing outer and nested changes; no reset, clean, stash, discard, unrelated overwrite, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. `_blake2/blake2s_impl.c` remains compilable but is not the active frontier. A forced fresh probe reconfirmed `posixmodule.c` as the first blocker. The intended Win9x-visible contract for its five exported loader-search constants remains unresolved; this was diagnostic-only, with no source/test edit or coding subagent. + +The forced probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`), including `_blake2/blake2s_impl.c`. First stable diagnostics: C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate subsequent C89 diagnostics begin at line 16173 (`ScandirIteratorType`), then 16220 (`list`), 16224 (`trace`), and 16246; left untouched. + +Files changed this run: `PORT_STATUS.md` only. Existing outer patch/test edits and all nested CPython modifications were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch; git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match; date -u '+%Y-%m-%d %H:%M UTC'` | 0; pre-existing modifications preserved; baseline/tag unchanged; timestamp `2026-09-30 02:41 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh probe compiled 39/40 with zero cached sources; first stable errors are the five C2065 constants listed above; later C89 diagnostics remain distinct. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; shell syntax and all applicable regression suites passed (`ALL TESTS PASSED`). | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants, then add a focused failing test before any source edit. Keep later C89 diagnostics separate. + +--- +## Latest revision (scheduled forced fresh VC6 frontier recheck; posixmodule remains blocker) + +Date: 2026-09-30 06:52 UTC + +Read `PORT_STATUS.md`, `README.md`, and `python-3-win9x-msvc6.md` before probing. Preserved all pre-existing outer and nested modifications; no reset, clean, stash, discard, unrelated overwrite, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Forced fresh probe confirms `_blake2/blake2s_impl.c` compiles and is not the active frontier. The first blocker remains five undeclared `LOAD_LIBRARY_SEARCH_*` constants in `posixmodule.c`; intended Win9x-visible API contract remains unresolved, so this was diagnostic-only with no source/test edits and no coding subagent. + +The probe compiled 39/40 sources (`total=40 compiled=39 cached=0 skipped=0`). First stable errors are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate subsequent C89 diagnostics begin at line 16173 (`ScandirIteratorType`), followed by line 16220 (`list`), line 16224 (`trace`), and line 16246. No compatibility implementation was guessed. + +Files changed this run: `PORT_STATUS.md` only. Existing modifications to the outer compatibility patch and frontier test, and all nested CPython changes, were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `date -u '+%Y-%m-%d %H:%M UTC'; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match; git status --short --branch; git -C cpython status --short --branch` | 0; timestamp `2026-09-30 06:52 UTC`; CPython tag/commit unchanged and all pre-existing modifications preserved. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh result 39/40 compiled, zero cached. First diagnostics are the five C2065 errors above; distinct later C89 diagnostics remain. Compiler output showed VC6 environment startup and source compilation; this is a compile probe only. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check; cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0; nested diff clean, generated patch matches complete nested diff byte-for-byte, outer non-generated diff clean. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants, then add a focused failing test before any narrow implementation. Keep later C89 diagnostics separate. + +--- +## Latest revision (scheduled forced fresh VC6 frontier recheck; posixmodule blocker unchanged) + +Date: 2026-09-30 08:53 UTC + +Read `PORT_STATUS.md`, `README.md`, and `references/python-3-win9x-msvc6.md` before probing. Preserved all pre-existing outer and nested modifications; no reset, clean, stash, discard, unrelated overwrite, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Although `_blake2/blake2s_impl.c` remains in the successfully compiled portion, the recorded state is no longer the active frontier: the forced fresh probe still stops first at `posixmodule.c`. No source/test implementation or coding subagent was appropriate because the Win9x-visible contract for the five exported loader-search constants remains unresolved. + +Probe compiled 39/40 listed sources (`total=40 compiled=39 cached=0 skipped=0`). First stable diagnostics are C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate later C89 diagnostics begin at line 16173 (`ScandirIteratorType`), followed at lines 16220 (`list`), 16224 (`trace`), and 16246; left untouched. + +Files changed this run: `PORT_STATUS.md` only. Pre-existing outer patch/test modifications and all nested CPython changes were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git -C cpython status --short --branch && git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && date -u '+%Y-%m-%d %H:%M UTC'` | 0; existing nested modifications preserved; baseline/tag unchanged; `2026-09-30 08:53 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh probe compiled 39/40 with zero cached sources; exact first stable errors are five C2065 identifiers listed above. Full output: `/home/ubuntu/.hermes/profiles/maxwell/cache/scratch/frontier-cron-current.log`. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax validation and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check; cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0 for all; nested/outer whitespace checks passed and generated compatibility patch byte-matches complete nested diff. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five exported constants, then add a focused failing test before any narrow source change. Keep later C89 diagnostics separate. + +--- +## Latest revision (scheduled forced fresh VC6 frontier recheck; posixmodule remains blocker) + +Date: 2026-09-30 11:00 UTC + +Read `PORT_STATUS.md` and `README.md` before probing. The referenced `python-3-win9x-msvc6.md` was not found in the active profile or project checkout; used the supplied porting instructions and prior status as the available reference. Preserved all pre-existing outer and nested modifications; no reset, clean, stash, discard, unrelated overwrite, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Forced fresh probe confirms `_blake2/blake2s_impl.c` compiles but is not the active frontier. First blocker remains five undeclared `LOAD_LIBRARY_SEARCH_*` constants in `posixmodule.c`; the Win9x-visible exported API contract remains unresolved, so no speculative implementation/test edit or coding subagent was appropriate. + +Probe compiled 39/40 sources (`total=40 compiled=39 cached=0 skipped=0`). First stable diagnostics: C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate later C89 diagnostics start at line 16173 (`ScandirIteratorType`), then line 16220 (`list`), line 16224 (`trace`), and line 16246. They remain distinct and untouched. + +Files changed this run: `PORT_STATUS.md` only. All pre-existing compatibility patch/test modifications and nested CPython edits were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git -C cpython rev-parse HEAD && git -C cpython describe --tags --exact-match && git status --short --branch && git -C cpython status --short --branch && git -C cpython diff --check && cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; printf 'patch_cmp_exit=%s\\n' "$?"; date -u '+%Y-%m-%d %H:%M UTC'` | 0; baseline/tag unchanged, all existing outer/nested modifications preserved, nested diff clean, compatibility patch matches complete nested diff byte-for-byte; timestamp `2026-09-30 11:00 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; forced fresh 39/40 compile; first stable errors are the five C2065 constants above, followed by separate C89 diagnostics. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax checks and all applicable regression suites passed (`ALL TESTS PASSED`). | + +Compiler-probe progress only: no linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants, then add a focused failing test before a narrow source change. Keep later C89 diagnostics separate. + +--- +## Latest revision (scheduled forced fresh VC6 frontier recheck; posixmodule remains blocker) + +Date: 2026-09-30 23:24 UTC + +Read `PORT_STATUS.md`, `README.md`, and the CPython 3.11.16/Win9x/MSVC6 reference before acting. Preserved all pre-existing outer and nested changes; no reset, clean, stash, discard, unrelated overwrite, commit, push, or publication. CPython remains detached at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. Forced fresh probing confirmed `_blake2/blake2s_impl.c` compiles and is not the current frontier. `posixmodule.c` remains the first blocker. The intended Win9x-visible contract for the five exported `LOAD_LIBRARY_SEARCH_*` constants remains unestablished; no source/test implementation or coding subagent was appropriate, so this run was diagnostic-only. + +The forced fresh probe compiled 39/40 sources (`total=40 compiled=39 cached=0 skipped=0`). First stable diagnostics: C2065 at `posixmodule.c(15856)`–`(15860)` for `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS`, `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`, `LOAD_LIBRARY_SEARCH_SYSTEM32`, `LOAD_LIBRARY_SEARCH_USER_DIRS`, and `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`. Separate later C89 diagnostics begin at line 16173 (`ScandirIteratorType`), 16220 (`list`), 16224 (`trace`), and 16246; left untouched. + +Files changed this run: `PORT_STATUS.md` only. Existing outer changes and all nested CPython changes were preserved. + +Exact commands/results: + +| Command | Exit/result | +|---|---| +| `git status --short --branch; git -C cpython status --short --branch; git -C cpython rev-parse HEAD; git -C cpython describe --tags --exact-match; date -u '+%Y-%m-%d %H:%M UTC'` | 0; all prior modifications preserved, baseline/tag unchanged; timestamp `2026-09-30 23:24 UTC`. | +| `WINEPREFIX=/home/ubuntu/.wine-win9xport WINEDEBUG=-all VC6_FRONTIER_FORCE=1 bash scripts/vc6-probe-pythoncore-frontier.sh` | 2; fresh compile-only probe compiled 39/40, zero cached; first stable diagnostics are the five C2065 errors above; later C89 diagnostics remain distinct. | +| `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe-pythoncore-frontier.test.sh && bash scripts/vc6-probe.test.sh && bash scripts/vc6-probe-core.test.sh && bash scripts/vc6-probe-pathcch.test.sh && bash scripts/generate-getpath-frozen.test.sh && bash scripts/vc6-probe-stdint.test.sh` | 0; syntax checks and all applicable regression suites passed (`ALL TESTS PASSED`). | +| `git -C cpython diff --check; cmp -s <(git -C cpython diff) compat/msvc600/cpython-3.11.16-vc6-headers.patch; git diff --check -- . ':(exclude)compat/msvc600/cpython-3.11.16-vc6-headers.patch'` | 0 before this status update; nested/outer diff checks passed and generated compatibility patch matches the complete nested diff byte-for-byte. | + +Compiler-probe progress only. No linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or real Windows 98 SE execution verified. Next frontier: establish authoritative intended Win9x behavior for the five exported loader-search constants, then add a focused failing test before a narrow source change. Keep later C89 diagnostics separate. + +--- +## Latest revision (fresh VC6 probe; `_winapi.c` is the actual frontier) + +Date: 2026-10-01 20:44 UTC + +Read `PORT_STATUS.md`, `README.md`, and the CPython 3.11.16/MSVC6 reference before probing. Preserved all entry-state work in the outer repository and detached CPython checkout; no reset, clean, stash, discard, commit, push, publication, or compatibility-patch regeneration. CPython remains at tag `v3.11.16`, commit `41388c9cb160d0886d5ca00d2e6c878260a8a4549`. The older `_blake2/blake2s_impl.c` frontier is stale: a forced fresh source-list probe compiled it successfully and found `_winapi.c` as the actual blocker. No source/test implementation was made because the first missing names are part of the `LCMapStringEx`/locale-mapping path, whose Win9x-compatible behavior must be established rather than addressed by speculative constant definitions. + +Exact verification: + +- `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=69 compiled=68 cached=0 skipped=0`. `_blake2/blake2s_impl.c` compiles. First stable `_winapi.c` diagnostics: C2065 for `LCMAP_SORTHANDLE` and `LCMAP_HASH` at line 1827. Subsequent distinct diagnostics include C89 declaration-order errors at lines 1832 onward and later missing SDK identifiers; no later cascade is treated as the first blocker. Full output: `/home/ubuntu/.hermes/profiles/maxwell/cache/terminal-output/out-1790887487-1598189-2410.log`. +- `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 0. `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1: the established `console type detection preserves control/cleanup order` assertion failed; the `_winapi` regressions and the other listed checks, including the new console readall/Clinic check, passed. This pre-existing suite failure is not attributed to the probe or fixed in this diagnostic run. +- `git -C cpython diff --check -- Modules/_winapi.c && git diff --check -- scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 0. + +Files changed this run: `PORT_STATUS.md` only. This is compiler-probe progress, not a linked `pythoncore.dll`/`python.exe`, Wine interpreter execution, or Windows 98 SE result. Compatibility patch synchronization remains unresolved. Next frontier: determine and document a valid Win9x-compatible replacement or explicit feature behavior for the `LCMapStringEx` locale-mapping path before editing; then address its C89 declarations in a separate test-first slice. Do not report a completed port. + +--- +## Latest revision (_PyCode_GetExtra/_PyCode_SetExtra C89 declarations cleared; frontier advances) + +Date: 2026-10-02 14:02 UTC + +Read `PORT_STATUS.md`, `README.md`, and `skills/software-development/legacy-toolchain-os-porting/references/python-3-win9x-msvc6.md` before acting. 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`. Used one focused Codex CLI coding lane (0.153.4, workspace-write; no approval override) for the narrowly scoped test-first edit. + +The recorded `_blake2/blake2s_impl.c` state was verified by a forced fresh probe: it compiles, but the actual first blocker is `Objects/codeobject.c`. One C89 declaration-order slice hoisted only locals in `_PyCode_GetExtra` and `_PyCode_SetExtra`, preserving validation, realloc, initialization, free callback, and assignment execution order. Added exact-body assertions to `scripts/vc6-codeobject-c89.test.py`. + +Exact commands/results: + +- RED: `python3 scripts/vc6-codeobject-c89.test.py cpython/Objects/codeobject.c` — exit 1, expected `_PyCode_GetExtra locals must be declared at function entry`. +- GREEN: same command — exit 0, `codeobject C89 declarations and ownership order: PASS`. +- `bash -n scripts/vc6-probe-pythoncore-frontier.sh scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 0. +- `bash scripts/vc6-probe-pythoncore-frontier.test.sh` — exit 1; codeobject check passes, while two unrelated existing assertions fail: console type detection (`console type lookup and cleanup order must remain unchanged`) and numeric dispatch (`ValueError: substring not found`). No unrelated changes attempted. +- Forced fresh probe before edit: `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=80 compiled=79 cached=0 skipped=0`. Initial first stable diagnostics were C2275/C2065 in `_PyCode_GetExtra` at lines 1399–1400, followed by declaration-order diagnostics in this file. +- Forced fresh probe after edit (same command) — exit 2, `total=80 compiled=79 cached=0 skipped=0`. `codeobject.c` passes the former `_PyCode_GetExtra`/`_PyCode_SetExtra` diagnostics. The next first stable errors are C2275/C2146/C2065 for late `Py_hash_t h4` at line 1830 and `h5` at 1834; separate later declaration errors remain. The terminal returned the full diagnostic output directly; no stable saved-log path was provided. +- `git diff --check -- scripts/vc6-codeobject-c89.test.py` and `git -C cpython diff --check -- Objects/codeobject.c` — exit 0. A baseline comparison confirmed source edits are confined to these two functions and existing test assertions are preserved. + +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. + +--- + diff --git a/compat/msvc600/cpython-3.11.16-vc6-headers.patch b/compat/msvc600/cpython-3.11.16-vc6-headers.patch index b07d060..fdf4e95 100644 --- a/compat/msvc600/cpython-3.11.16-vc6-headers.patch +++ b/compat/msvc600/cpython-3.11.16-vc6-headers.patch @@ -14,6 +14,27 @@ index b018dab..8691994 100644 #define _Py_static_string(varname, value) static _Py_Identifier varname = _Py_static_string_init(value) #define _Py_IDENTIFIER(varname) _Py_static_string(PyId_##varname, #varname) +diff --git a/Include/cpython/pytime.h b/Include/cpython/pytime.h +index 23d4f16..929bf04 100644 +--- a/Include/cpython/pytime.h ++++ b/Include/cpython/pytime.h +@@ -58,9 +58,14 @@ extern "C" { + UNIX epoch). */ + typedef int64_t _PyTime_t; + // _PyTime_MIN nanoseconds is around -292.3 years +-#define _PyTime_MIN INT64_MIN + // _PyTime_MAX nanoseconds is around +292.3 years +-#define _PyTime_MAX INT64_MAX ++#if defined(_MSC_VER) && _MSC_VER < 1300 ++# define _PyTime_MIN PY_LLONG_MIN ++# define _PyTime_MAX PY_LLONG_MAX ++#else ++# define _PyTime_MIN INT64_MIN ++# define _PyTime_MAX INT64_MAX ++#endif + #define _SIZEOF_PYTIME_T 8 + + typedef enum { diff --git a/Include/internal/pycore_atomic.h b/Include/internal/pycore_atomic.h index 425d69f..748de28 100644 --- a/Include/internal/pycore_atomic.h @@ -474,6 +495,22 @@ index f022f82..d1c55ae 100644 _PyGCHead_SET_NEXT(prev, next); _PyGCHead_SET_PREV(next, prev); gc->_gc_next = 0; +diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h +index 7c5aba1..8818e63 100644 +--- a/Include/internal/pycore_pystate.h ++++ b/Include/internal/pycore_pystate.h +@@ -101,6 +101,11 @@ _Py_EnsureFuncTstateNotNULL(const char *func, PyThreadState *tstate) + } + } + ++/* Visual C++ 6.0 predates C99's __func__ identifier. */ ++#if defined(_MSC_VER) && _MSC_VER < 1300 && !defined(__func__) ++# define __func__ "" ++#endif ++ + // Call Py_FatalError() if tstate is NULL + #define _Py_EnsureTstateNotNULL(tstate) \ + _Py_EnsureFuncTstateNotNULL(__func__, tstate) diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index 13eae1e..91faff4 100644 --- a/Include/internal/pycore_runtime_init.h @@ -562,10 +599,22 @@ index 772b67e..7b90016 100644 // NAN is a float, Py_NAN is a double: cast to double. # define Py_NAN ((double)NAN) diff --git a/Include/pyport.h b/Include/pyport.h -index b3b8b6f..938d131 100644 +index b3b8b6f..58b5d58 100644 --- a/Include/pyport.h +++ b/Include/pyport.h -@@ -97,6 +97,13 @@ Used in: Py_SAFE_DOWNCAST +@@ -13,6 +13,11 @@ + # error "Python's source code assumes C's unsigned char is an 8-bit type" + #endif + ++/* Visual C++ 6.0 predates C99's __func__ identifier. */ ++#if defined(_MSC_VER) && _MSC_VER < 1300 && !defined(__func__) ++# define __func__ "" ++#endif ++ + + // Macro to use C++ static_cast<> in the Python C API. + #ifdef __cplusplus +@@ -97,6 +102,13 @@ Used in: Py_SAFE_DOWNCAST #define PY_ULLONG_MAX ULLONG_MAX #endif @@ -579,7 +628,7 @@ index b3b8b6f..938d131 100644 #define PY_UINT32_T uint32_t #define PY_UINT64_T uint64_t -@@ -334,9 +341,12 @@ extern "C" { +@@ -334,9 +346,12 @@ extern "C" { #if defined(__GNUC__) \ && ((__GNUC__ >= 4) || (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)) #define Py_DEPRECATED(VERSION_UNUSED) __attribute__((__deprecated__)) @@ -593,7 +642,7 @@ index b3b8b6f..938d131 100644 #else #define Py_DEPRECATED(VERSION_UNUSED) #endif -@@ -352,10 +362,14 @@ extern "C" { +@@ -352,10 +367,14 @@ extern "C" { #define _Py_COMP_DIAG_IGNORE_DEPR_DECLS \ _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") #define _Py_COMP_DIAG_POP _Pragma("GCC diagnostic pop") @@ -609,7 +658,7 @@ index b3b8b6f..938d131 100644 #else #define _Py_COMP_DIAG_PUSH #define _Py_COMP_DIAG_IGNORE_DEPR_DECLS -@@ -425,7 +439,7 @@ extern "C" { +@@ -425,7 +444,7 @@ extern "C" { // Py_NO_INLINE static int random(void) { return 4; } #if defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER) # define Py_NO_INLINE __attribute__ ((noinline)) @@ -2624,6 +2673,654 @@ index 3dbaaa0..5b17473 100644 Py_INCREF(top); cmp = PyObject_RichCompareBool(top, item, Py_LT); Py_DECREF(top); +diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c +index aa87717..6395120 100644 +--- a/Modules/_io/_iomodule.c ++++ b/Modules/_io/_iomodule.c +@@ -472,6 +472,7 @@ static PyObject * + _io_text_encoding_impl(PyObject *module, PyObject *encoding, int stacklevel) + /*[clinic end generated code: output=91b2cfea6934cc0c input=4999aa8b3d90f3d4]*/ + { ++ const PyPreConfig *preconfig; + if (encoding == NULL || encoding == Py_None) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (_PyInterpreterState_GetConfig(interp)->warn_default_encoding) { +@@ -480,7 +481,7 @@ _io_text_encoding_impl(PyObject *module, PyObject *encoding, int stacklevel) + return NULL; + } + } +- const PyPreConfig *preconfig = &_PyRuntime.preconfig; ++ preconfig = &_PyRuntime.preconfig; + if (preconfig->utf8_mode) { + _Py_DECLARE_STR(utf_8, "utf-8"); + encoding = &_Py_STR(utf_8); +@@ -670,7 +671,8 @@ static PyTypeObject* static_types[] = { + void + _PyIO_Fini(void) + { +- for (Py_ssize_t i=Py_ARRAY_LENGTH(static_types) - 1; i >= 0; i--) { ++ Py_ssize_t i; ++ for (i = Py_ARRAY_LENGTH(static_types) - 1; i >= 0; i--) { + PyTypeObject *exc = static_types[i]; + _PyStaticType_Dealloc(exc); + } +@@ -680,6 +682,7 @@ _PyIO_Fini(void) + PyMODINIT_FUNC + PyInit__io(void) + { ++ size_t i; + PyObject *m = PyModule_Create(&_PyIO_Module); + _PyIO_State *state = NULL; + if (m == NULL) +@@ -722,7 +725,7 @@ PyInit__io(void) + PyTextIOWrapper_Type.tp_base = &PyTextIOBase_Type; + + // Add types +- for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) { ++ for (i = 0; i < Py_ARRAY_LENGTH(static_types); i++) { + PyTypeObject *type = static_types[i]; + // Private type not exposed in the _io module + if (type == &_PyBytesIOBuffer_Type) { +diff --git a/Modules/_io/_iomodule.h b/Modules/_io/_iomodule.h +index c260080..c53ada0 100644 +--- a/Modules/_io/_iomodule.h ++++ b/Modules/_io/_iomodule.h +@@ -92,12 +92,12 @@ extern int _PyIO_trap_eintr(void); + #ifdef MS_WINDOWS + + /* Windows uses long long for offsets */ +-typedef long long Py_off_t; ++typedef PY_LONG_LONG Py_off_t; + # define PyLong_AsOff_t PyLong_AsLongLong + # define PyLong_FromOff_t PyLong_FromLongLong +-# define PY_OFF_T_MAX LLONG_MAX +-# define PY_OFF_T_MIN LLONG_MIN +-# define PY_OFF_T_COMPAT long long /* type compatible with off_t */ ++# define PY_OFF_T_MAX PY_LLONG_MAX ++# define PY_OFF_T_MIN PY_LLONG_MIN ++# define PY_OFF_T_COMPAT PY_LONG_LONG /* type compatible with off_t */ + # define PY_PRIdOFF "lld" /* format to use for that type */ + + #else +diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c +index e84eef4..0435b44 100644 +--- a/Modules/_io/bytesio.c ++++ b/Modules/_io/bytesio.c +@@ -124,12 +124,13 @@ unshare_buffer(bytesio *self, size_t size) + static int + resize_buffer(bytesio *self, size_t size) + { ++ size_t alloc; + assert(self->buf != NULL); + assert(self->exports == 0); + + /* Here, unsigned types are used to avoid dealing with signed integer + overflow, which is undefined in C. */ +- size_t alloc = PyBytes_GET_SIZE(self->buf); ++ alloc = PyBytes_GET_SIZE(self->buf); + + /* For simplicity, stay in the range of the signed type. Anyway, Python + doesn't allow strings to be longer than this. */ +@@ -180,6 +181,9 @@ resize_buffer(bytesio *self, size_t size) + Py_NO_INLINE static Py_ssize_t + write_bytes(bytesio *self, PyObject *b) + { ++ Py_buffer buf; ++ Py_ssize_t len; ++ size_t endpos; + if (check_closed(self)) { + return -1; + } +@@ -187,17 +191,16 @@ write_bytes(bytesio *self, PyObject *b) + return -1; + } + +- Py_buffer buf; + if (PyObject_GetBuffer(b, &buf, PyBUF_CONTIG_RO) < 0) { + return -1; + } +- Py_ssize_t len = buf.len; ++ len = buf.len; + if (len == 0) { + goto done; + } + + assert(self->pos >= 0); +- size_t endpos = (size_t)self->pos + len; ++ endpos = (size_t)self->pos + len; + if (endpos > (size_t)PyBytes_GET_SIZE(self->buf)) { + if (resize_buffer(self, endpos) < 0) { + len = -1; +diff --git a/Modules/_io/clinic/_iomodule.c.h b/Modules/_io/clinic/_iomodule.c.h +index 0249dd1..9e774ef 100644 +--- a/Modules/_io/clinic/_iomodule.c.h ++++ b/Modules/_io/clinic/_iomodule.c.h +@@ -145,6 +145,7 @@ _io_open(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw + const char *newline = NULL; + int closefd = 1; + PyObject *opener = Py_None; ++ Py_ssize_t mode_length; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 8, 0, argsbuf); + if (!args) { +@@ -159,7 +160,6 @@ _io_open(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw + _PyArg_BadArgument("open", "argument 'mode'", "str", args[1]); + goto exit; + } +- Py_ssize_t mode_length; + mode = PyUnicode_AsUTF8AndSize(args[1], &mode_length); + if (mode == NULL) { + goto exit; +diff --git a/Modules/_io/clinic/fileio.c.h b/Modules/_io/clinic/fileio.c.h +index bb0b36c..53f18c3 100644 +--- a/Modules/_io/clinic/fileio.c.h ++++ b/Modules/_io/clinic/fileio.c.h +@@ -59,6 +59,7 @@ _io_FileIO___init__(PyObject *self, PyObject *args, PyObject *kwargs) + const char *mode = "r"; + int closefd = 1; + PyObject *opener = Py_None; ++ Py_ssize_t mode_length; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 4, 0, argsbuf); + if (!fastargs) { +@@ -73,7 +74,6 @@ _io_FileIO___init__(PyObject *self, PyObject *args, PyObject *kwargs) + _PyArg_BadArgument("FileIO", "argument 'mode'", "str", fastargs[1]); + goto exit; + } +- Py_ssize_t mode_length; + mode = PyUnicode_AsUTF8AndSize(fastargs[1], &mode_length); + if (mode == NULL) { + goto exit; +diff --git a/Modules/_io/clinic/winconsoleio.c.h b/Modules/_io/clinic/winconsoleio.c.h +index 75102a3..dc45220 100644 +--- a/Modules/_io/clinic/winconsoleio.c.h ++++ b/Modules/_io/clinic/winconsoleio.c.h +@@ -58,6 +58,7 @@ _io__WindowsConsoleIO___init__(PyObject *self, PyObject *args, PyObject *kwargs) + const char *mode = "r"; + int closefd = 1; + PyObject *opener = Py_None; ++ Py_ssize_t mode_length; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 4, 0, argsbuf); + if (!fastargs) { +@@ -72,7 +73,6 @@ _io__WindowsConsoleIO___init__(PyObject *self, PyObject *args, PyObject *kwargs) + _PyArg_BadArgument("_WindowsConsoleIO", "argument 'mode'", "str", fastargs[1]); + goto exit; + } +- Py_ssize_t mode_length; + mode = PyUnicode_AsUTF8AndSize(fastargs[1], &mode_length); + if (mode == NULL) { + goto exit; +diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c +index e9dd85f..011d17d 100644 +--- a/Modules/_io/textio.c ++++ b/Modules/_io/textio.c +@@ -507,7 +507,7 @@ _io_IncrementalNewlineDecoder_getstate_impl(nldecoder_object *self) + /*[clinic end generated code: output=f0d2c9c136f4e0d0 input=f8ff101825e32e7f]*/ + { + PyObject *buffer; +- unsigned long long flag; ++ unsigned PY_LONG_LONG flag; + + CHECK_INITIALIZED_DECODER(self); + +@@ -553,7 +553,7 @@ _io_IncrementalNewlineDecoder_setstate(nldecoder_object *self, + /*[clinic end generated code: output=c10c622508b576cb input=c53fb505a76dbbe2]*/ + { + PyObject *buffer; +- unsigned long long flag; ++ unsigned PY_LONG_LONG flag; + + CHECK_INITIALIZED_DECODER(self); + +@@ -950,19 +950,22 @@ _textiowrapper_set_encoder(textio *self, PyObject *codec_info, + static int + _textiowrapper_fix_encoder_state(textio *self) + { ++ PyObject *cookieObj; ++ PyObject *res; ++ int cmp; + if (!self->seekable || !self->encoder) { + return 0; + } + + self->encoding_start_of_stream = 1; + +- PyObject *cookieObj = PyObject_CallMethodNoArgs( ++ cookieObj = PyObject_CallMethodNoArgs( + self->buffer, &_Py_ID(tell)); + if (cookieObj == NULL) { + return -1; + } + +- int cmp = PyObject_RichCompareBool(cookieObj, _PyLong_GetZero(), Py_EQ); ++ cmp = PyObject_RichCompareBool(cookieObj, _PyLong_GetZero(), Py_EQ); + Py_DECREF(cookieObj); + if (cmp < 0) { + return -1; +@@ -970,7 +973,7 @@ _textiowrapper_fix_encoder_state(textio *self) + + if (cmp == 0) { + self->encoding_start_of_stream = 0; +- PyObject *res = PyObject_CallMethodOneArg( ++ res = PyObject_CallMethodOneArg( + self->encoder, &_Py_ID(setstate), _PyLong_GetZero()); + if (res == NULL) { + return -1; +@@ -984,9 +987,13 @@ _textiowrapper_fix_encoder_state(textio *self) + static int + io_check_errors(PyObject *errors) + { ++ PyInterpreterState *interp; ++ Py_ssize_t name_length; ++ const char *name; ++ PyObject *handler; + assert(errors != NULL && errors != Py_None); + +- PyInterpreterState *interp = _PyInterpreterState_GET(); ++ interp = _PyInterpreterState_GET(); + #ifndef Py_DEBUG + /* In release mode, only check in development mode (-X dev) */ + if (!_PyInterpreterState_GetConfig(interp)->dev_mode) { +@@ -1002,8 +1009,7 @@ io_check_errors(PyObject *errors) + return 0; + } + +- Py_ssize_t name_length; +- const char *name = PyUnicode_AsUTF8AndSize(errors, &name_length); ++ name = PyUnicode_AsUTF8AndSize(errors, &name_length); + if (name == NULL) { + return -1; + } +@@ -1011,7 +1017,7 @@ io_check_errors(PyObject *errors) + PyErr_SetString(PyExc_ValueError, "embedded null character in errors"); + return -1; + } +- PyObject *handler = PyCodec_LookupError(name); ++ handler = PyCodec_LookupError(name); + if (handler != NULL) { + Py_DECREF(handler); + return 0; +@@ -1069,6 +1075,8 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer, + { + PyObject *raw, *codec_info = NULL; + PyObject *res; ++ Py_ssize_t errors_len; ++ const char *errors_str; + int r; + + self->ok = 0; +@@ -1099,8 +1107,7 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer, + else if (io_check_errors(errors)) { + return -1; + } +- Py_ssize_t errors_len; +- const char *errors_str = PyUnicode_AsUTF8AndSize(errors, &errors_len); ++ errors_str = PyUnicode_AsUTF8AndSize(errors, &errors_len); + if (errors_str == NULL) { + return -1; + } +@@ -1256,6 +1263,9 @@ static int + textiowrapper_change_encoding(textio *self, PyObject *encoding, + PyObject *errors, int newline_changed) + { ++ const char *c_encoding; ++ const char *c_errors; ++ PyObject *codec_info; + /* Use existing settings where new settings are not specified */ + if (encoding == Py_None && errors == Py_None && !newline_changed) { + return 0; // no change +@@ -1283,13 +1293,13 @@ textiowrapper_change_encoding(textio *self, PyObject *encoding, + } + Py_INCREF(errors); + +- const char *c_encoding = PyUnicode_AsUTF8(encoding); ++ c_encoding = PyUnicode_AsUTF8(encoding); + if (c_encoding == NULL) { + Py_DECREF(encoding); + Py_DECREF(errors); + return -1; + } +- const char *c_errors = PyUnicode_AsUTF8(errors); ++ c_errors = PyUnicode_AsUTF8(errors); + if (c_errors == NULL) { + Py_DECREF(encoding); + Py_DECREF(errors); +@@ -1297,7 +1307,7 @@ textiowrapper_change_encoding(textio *self, PyObject *encoding, + } + + // Create new encoder & decoder +- PyObject *codec_info = _PyCodec_LookupTextEncoding( ++ codec_info = _PyCodec_LookupTextEncoding( + c_encoding, "codecs.open()"); + if (codec_info == NULL) { + Py_DECREF(encoding); +@@ -1344,6 +1354,7 @@ _io_TextIOWrapper_reconfigure_impl(textio *self, PyObject *encoding, + int line_buffering; + int write_through; + const char *newline = NULL; ++ PyObject *res; + + if (encoding != Py_None && !PyUnicode_Check(encoding)) { + PyErr_Format(PyExc_TypeError, +@@ -1392,7 +1403,7 @@ _io_TextIOWrapper_reconfigure_impl(textio *self, PyObject *encoding, + return NULL; + } + +- PyObject *res = PyObject_CallMethodNoArgs((PyObject *)self, &_Py_ID(flush)); ++ res = PyObject_CallMethodNoArgs((PyObject *)self, &_Py_ID(flush)); + if (res == NULL) { + return NULL; + } +@@ -1546,11 +1557,16 @@ _io_TextIOWrapper_detach_impl(textio *self) + static int + _textiowrapper_writeflush(textio *self) + { ++ PyObject *pending; ++ PyObject *b; ++ char *buf; ++ Py_ssize_t pos; ++ Py_ssize_t i; ++ PyObject *ret; + if (self->pending_bytes == NULL) + return 0; + +- PyObject *pending = self->pending_bytes; +- PyObject *b; ++ pending = self->pending_bytes; + + if (PyBytes_Check(pending)) { + b = pending; +@@ -1572,10 +1588,10 @@ _textiowrapper_writeflush(textio *self) + return -1; + } + +- char *buf = PyBytes_AsString(b); +- Py_ssize_t pos = 0; ++ buf = PyBytes_AsString(b); ++ pos = 0; + +- for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pending); i++) { ++ for (i = 0; i < PyList_GET_SIZE(pending); i++) { + PyObject *obj = PyList_GET_ITEM(pending, i); + char *src; + Py_ssize_t len; +@@ -1601,7 +1617,6 @@ _textiowrapper_writeflush(textio *self) + self->pending_bytes = NULL; + Py_DECREF(pending); + +- PyObject *ret; + do { + ret = PyObject_CallMethodOneArg(self->buffer, &_Py_ID(write), b); + } while (ret == NULL && _PyIO_trap_eintr()); +@@ -1627,6 +1642,8 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject *text) + PyObject *ret; + PyObject *b; + Py_ssize_t textlen; ++ Py_ssize_t bytes_len; ++ int list_ret; + int haslf = 0; + int needflush = 0, text_needflush = 0; + +@@ -1692,7 +1709,6 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject *text) + return NULL; + } + +- Py_ssize_t bytes_len; + if (b == text) { + bytes_len = PyUnicode_GET_LENGTH(b); + } +@@ -1745,9 +1761,9 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject *text) + } + } + +- int ret = PyList_Append(self->pending_bytes, b); ++ list_ret = PyList_Append(self->pending_bytes, b); + Py_DECREF(b); +- if (ret < 0) { ++ if (list_ret < 0) { + return NULL; + } + } +@@ -1836,6 +1852,7 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint) + PyObject *decoded_chars, *chunk_size; + Py_ssize_t nbytes, nchars; + int eof; ++ PyObject *snapshot; + + /* The return value is True unless EOF was reached. The decoded string is + * placed in self._decoded_chars (replacing its previous value). The +@@ -1935,7 +1952,7 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint) + if (next_input == NULL) { + goto fail; + } +- PyObject *snapshot = Py_BuildValue("NN", dec_flags, next_input); ++ snapshot = Py_BuildValue("NN", dec_flags, next_input); + if (snapshot == NULL) { + dec_flags = NULL; + goto fail; +@@ -2511,6 +2528,7 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) + PyObject *res; + int cmp; + PyObject *snapshot; ++ PyObject *zero; + + CHECK_ATTACHED(self); + CHECK_CLOSED(self); +@@ -2522,7 +2540,7 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) + goto fail; + } + +- PyObject *zero = _PyLong_GetZero(); // borrowed reference ++ zero = _PyLong_GetZero(); // borrowed reference + + switch (whence) { + case SEEK_CUR: +diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c +index dcb7d32..ca4ce32 100644 +--- a/Modules/_io/winconsoleio.c ++++ b/Modules/_io/winconsoleio.c +@@ -62,18 +62,23 @@ char _get_console_type(HANDLE handle) { + } + + char _PyIO_get_console_type(PyObject *path_or_fd) { +- int fd = PyLong_AsLong(path_or_fd); ++ int fd; ++ HANDLE handle; ++ PyObject *decoded; ++ wchar_t *decoded_wstr; ++ char m; ++ DWORD length; ++ wchar_t name_buf[MAX_PATH]; ++ wchar_t *pname_buf; ++ fd = PyLong_AsLong(path_or_fd); + PyErr_Clear(); + if (fd >= 0) { +- HANDLE handle = _Py_get_osfhandle_noraise(fd); ++ handle = _Py_get_osfhandle_noraise(fd); + if (handle == INVALID_HANDLE_VALUE) + return '\0'; + return _get_console_type(handle); + } + +- PyObject *decoded; +- wchar_t *decoded_wstr; +- + if (!PyUnicode_FSDecoder(path_or_fd, &decoded)) { + PyErr_Clear(); + return '\0'; +@@ -85,7 +90,7 @@ char _PyIO_get_console_type(PyObject *path_or_fd) { + return '\0'; + } + +- char m = '\0'; ++ m = '\0'; + if (!_wcsicmp(decoded_wstr, L"CONIN$")) { + m = 'r'; + } else if (!_wcsicmp(decoded_wstr, L"CONOUT$")) { +@@ -98,9 +103,7 @@ char _PyIO_get_console_type(PyObject *path_or_fd) { + return m; + } + +- DWORD length; +- wchar_t name_buf[MAX_PATH], *pname_buf = name_buf; +- ++ pname_buf = name_buf; + length = GetFullPathNameW(decoded_wstr, MAX_PATH, pname_buf, NULL); + if (length > MAX_PATH) { + pname_buf = PyMem_New(wchar_t, length); +@@ -519,7 +522,8 @@ _io__WindowsConsoleIO_writable_impl(winconsoleio *self) + static DWORD + _buflen(winconsoleio *self) + { +- for (DWORD i = 0; i < SMALLBUF; ++i) { ++ DWORD i; ++ for (i = 0; i < SMALLBUF; ++i) { + if (!self->buf[i]) + return i; + } +@@ -529,11 +533,12 @@ _buflen(winconsoleio *self) + static DWORD + _copyfrombuf(winconsoleio *self, char *buf, DWORD len) + { ++ int i; + DWORD n = 0; + + while (self->buf[0] && len--) { + buf[n++] = self->buf[0]; +- for (int i = 1; i < SMALLBUF; ++i) ++ for (i = 1; i < SMALLBUF; ++i) + self->buf[i - 1] = self->buf[i]; + self->buf[SMALLBUF - 1] = 0; + } +@@ -544,6 +549,8 @@ _copyfrombuf(winconsoleio *self, char *buf, DWORD len) + static wchar_t * + read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) { + int err = 0, sig = 0; ++ DWORD off; ++ HANDLE hInterruptEvent; + + wchar_t *buf = (wchar_t*)PyMem_Malloc(maxlen * sizeof(wchar_t)); + if (!buf) +@@ -553,12 +560,15 @@ read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) { + + //DebugBreak(); + Py_BEGIN_ALLOW_THREADS +- DWORD off = 0; ++ off = 0; + while (off < maxlen) { +- DWORD n = (DWORD)-1; +- DWORD len = min(maxlen - off, BUFSIZ); ++ DWORD n; ++ DWORD len; ++ BOOL res; ++ n = (DWORD)-1; ++ len = min(maxlen - off, BUFSIZ); + SetLastError(0); +- BOOL res = ReadConsoleW(handle, &buf[off], len, &n, NULL); ++ res = ReadConsoleW(handle, &buf[off], len, &n, NULL); + + if (!res) { + err = GetLastError(); +@@ -572,7 +582,7 @@ read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) { + if (err != ERROR_OPERATION_ABORTED) + break; + err = 0; +- HANDLE hInterruptEvent = _PyOS_SigintEvent(); ++ hInterruptEvent = _PyOS_SigintEvent(); + if (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE) + == WAIT_OBJECT_0) { + ResetEvent(hInterruptEvent); +@@ -594,10 +604,9 @@ read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) { + break; + /* If the buffer ends with a high surrogate, expand the + buffer and read an extra character. */ +- WORD char_type; + if (off + BUFSIZ >= maxlen && +- GetStringTypeW(CT_CTYPE3, &buf[*readlen - 1], 1, &char_type) && +- char_type == C3_HIGHSURROGATE) { ++ buf[*readlen - 1] >= 0xD800 && ++ buf[*readlen - 1] <= 0xDBFF) { + wchar_t *newbuf; + maxlen += 1; + Py_BLOCK_THREADS +@@ -646,6 +655,14 @@ read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) { + static Py_ssize_t + readinto(winconsoleio *self, char *buf, Py_ssize_t len) + { ++ HANDLE handle; ++ DWORD wlen; ++ DWORD read_len; ++ DWORD n; ++ wchar_t *wbuf; ++ int err; ++ DWORD u8n; ++ + if (self->fd == -1) { + err_closed(); + return -1; +@@ -661,7 +678,7 @@ readinto(winconsoleio *self, char *buf, Py_ssize_t len) + return -1; + } + +- HANDLE handle = _Py_get_osfhandle(self->fd); ++ handle = _Py_get_osfhandle(self->fd); + if (handle == INVALID_HANDLE_VALUE) + return -1; + +@@ -671,12 +688,12 @@ readinto(winconsoleio *self, char *buf, Py_ssize_t len) + If the caller requests fewer than 4 bytes, we buffer one + character. + */ +- DWORD wlen = (DWORD)(len / 4); ++ wlen = (DWORD)(len / 4); + if (wlen == 0) { + wlen = 1; + } + +- DWORD read_len = _copyfrombuf(self, buf, (DWORD)len); ++ read_len = _copyfrombuf(self, buf, (DWORD)len); + if (read_len) { + buf = &buf[read_len]; + len -= read_len; +@@ -685,8 +702,7 @@ readinto(winconsoleio *self, char *buf, Py_ssize_t len) + if (len == read_len || wlen == 0) + return read_len; + +- DWORD n; +- wchar_t *wbuf = read_console_w(handle, wlen, &n); ++ wbuf = read_console_w(handle, wlen, &n); + if (wbuf == NULL) + return -1; + if (n == 0) { +@@ -694,8 +710,8 @@ readinto(winconsoleio *self, char *buf, Py_ssize_t len) + return read_len; + } + +- int err = 0; +- DWORD u8n = 0; ++ err = 0; ++ u8n = 0; + + Py_BEGIN_ALLOW_THREADS + if (len < 4) { +@@ -809,6 +825,7 @@ _io__WindowsConsoleIO_readall_impl(winconsoleio *self) + + while (1) { + wchar_t *subbuf; ++ wchar_t *tmp; + + if (len >= (Py_ssize_t)bufsize) { + DWORD newsize = new_buffersize(self, len); +@@ -823,7 +840,7 @@ _io__WindowsConsoleIO_readall_impl(winconsoleio *self) + } + bufsize = newsize; + +- wchar_t *tmp = PyMem_Realloc(buf, ++ tmp = PyMem_Realloc(buf, + (bufsize + 1) * sizeof(wchar_t)); + if (tmp == NULL) { + PyMem_Free(buf); diff --git a/Modules/_json.c b/Modules/_json.c index 9464b9f..eada42f 100644 --- a/Modules/_json.c @@ -2896,6 +3593,192 @@ index a0e262b..f785e94 100644 + _lsprof_free }; + PyMODINIT_FUNC +diff --git a/Modules/_opcode.c b/Modules/_opcode.c +index 4812716..ca8de7f 100644 +--- a/Modules/_opcode.c ++++ b/Modules/_opcode.c +@@ -101,10 +101,14 @@ opcode_functions[] = { + + static struct PyModuleDef opcodemodule = { + PyModuleDef_HEAD_INIT, +- .m_name = "_opcode", +- .m_doc = "Opcode support module.", +- .m_size = 0, +- .m_methods = opcode_functions ++ "_opcode", ++ "Opcode support module.", ++ 0, ++ opcode_functions, ++ NULL, ++ NULL, ++ NULL, ++ NULL + }; + + PyMODINIT_FUNC +diff --git a/Modules/_operator.c b/Modules/_operator.c +index 6a25205..2c0985d 100644 +--- a/Modules/_operator.c ++++ b/Modules/_operator.c +@@ -992,6 +992,7 @@ itemgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + PyObject *item; + Py_ssize_t nitems; + Py_ssize_t index; ++ _operator_state *state; + + if (!_PyArg_NoKeywords("itemgetter", kwds)) + return NULL; +@@ -1003,7 +1004,7 @@ itemgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + } else { + item = args; + } +- _operator_state *state = PyType_GetModuleState(type); ++ state = PyType_GetModuleState(type); + /* create itemgetterobject structure */ + ig = PyObject_GC_New(itemgetterobject, (PyTypeObject *) state->itemgetter_type); + if (ig == NULL) { +@@ -1074,10 +1075,12 @@ static PyObject * + itemgetter_vectorcall(PyObject *ig, PyObject *const *args, + size_t nargsf, PyObject *kwnames) + { ++ Py_ssize_t nargs; ++ + if (!_PyArg_NoKwnames("itemgetter", kwnames)) { + return NULL; + } +- Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); ++ nargs = PyVectorcall_NARGS(nargsf); + if (!_PyArg_CheckPositional("itemgetter", nargs, 1, 1)) { + return NULL; + } +@@ -1183,12 +1186,12 @@ static PyType_Slot itemgetter_type_slots[] = { + }; + + static PyType_Spec itemgetter_type_spec = { +- .name = "operator.itemgetter", +- .basicsize = sizeof(itemgetterobject), +- .itemsize = 0, +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | +- Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_VECTORCALL), +- .slots = itemgetter_type_slots, ++ "operator.itemgetter", ++ sizeof(itemgetterobject), ++ 0, ++ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | ++ Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_VECTORCALL), ++ itemgetter_type_slots, + }; + + /* attrgetter object **********************************************************/ +@@ -1213,6 +1216,7 @@ attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + attrgetterobject *ag; + PyObject *attr; + Py_ssize_t nattrs, idx, char_idx; ++ _operator_state *state; + + if (!_PyArg_NoKeywords("attrgetter", kwds)) + return NULL; +@@ -1305,7 +1309,7 @@ attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + } + } + +- _operator_state *state = PyType_GetModuleState(type); ++ state = PyType_GetModuleState(type); + /* create attrgetterobject structure */ + ag = PyObject_GC_New(attrgetterobject, (PyTypeObject *)state->attrgetter_type); + if (ag == NULL) { +@@ -1392,10 +1396,12 @@ attrgetter_call(attrgetterobject *ag, PyObject *args, PyObject *kw) + static PyObject * + attrgetter_vectorcall(PyObject *ag, PyObject *const *args, size_t nargsf, PyObject *kwnames) + { ++ Py_ssize_t nargs; ++ + if (!_PyArg_NoKwnames("attrgetter", kwnames)) { + return NULL; + } +- Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); ++ nargs = PyVectorcall_NARGS(nargsf); + if (!_PyArg_CheckPositional("attrgetter", nargs, 1, 1)) { + return NULL; + } +@@ -1549,12 +1555,12 @@ static PyType_Slot attrgetter_type_slots[] = { + }; + + static PyType_Spec attrgetter_type_spec = { +- .name = "operator.attrgetter", +- .basicsize = sizeof(attrgetterobject), +- .itemsize = 0, +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | +- Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_VECTORCALL), +- .slots = attrgetter_type_slots, ++ "operator.attrgetter", ++ sizeof(attrgetterobject), ++ 0, ++ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | ++ Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_VECTORCALL), ++ attrgetter_type_slots, + }; + + +@@ -1573,6 +1579,7 @@ methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + { + methodcallerobject *mc; + PyObject *name; ++ _operator_state *state; + + if (PyTuple_GET_SIZE(args) < 1) { + PyErr_SetString(PyExc_TypeError, "methodcaller needs at least " +@@ -1587,7 +1594,7 @@ methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + return NULL; + } + +- _operator_state *state = PyType_GetModuleState(type); ++ state = PyType_GetModuleState(type); + /* create methodcallerobject structure */ + mc = PyObject_GC_New(methodcallerobject, (PyTypeObject *)state->methodcaller_type); + if (mc == NULL) { +@@ -1804,12 +1811,12 @@ static PyType_Slot methodcaller_type_slots[] = { + }; + + static PyType_Spec methodcaller_type_spec = { +- .name = "operator.methodcaller", +- .basicsize = sizeof(methodcallerobject), +- .itemsize = 0, +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | +- Py_TPFLAGS_IMMUTABLETYPE), +- .slots = methodcaller_type_slots, ++ "operator.methodcaller", ++ sizeof(methodcallerobject), ++ 0, ++ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | ++ Py_TPFLAGS_IMMUTABLETYPE), ++ methodcaller_type_slots, + }; + + static int +@@ -1877,14 +1884,14 @@ operator_free(void *module) + + static struct PyModuleDef operatormodule = { + PyModuleDef_HEAD_INIT, +- .m_name = "_operator", +- .m_doc = operator_doc, +- .m_size = sizeof(_operator_state), +- .m_methods = operator_methods, +- .m_slots = operator_slots, +- .m_traverse = operator_traverse, +- .m_clear = operator_clear, +- .m_free = operator_free, ++ "_operator", ++ operator_doc, ++ sizeof(_operator_state), ++ operator_methods, ++ operator_slots, ++ operator_traverse, ++ operator_clear, ++ operator_free, + }; + PyMODINIT_FUNC diff --git a/Modules/_pickle.c b/Modules/_pickle.c index f0cb302..47ba9e5 100644 @@ -3900,6 +4783,2207 @@ index 9d66568..97921b1 100644 }; PyMODINIT_FUNC +diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c +index 625e4e6..0459350 100644 +--- a/Modules/_threadmodule.c ++++ b/Modules/_threadmodule.c +@@ -57,6 +57,8 @@ lock_traverse(lockobject *self, visitproc visit, void *arg) + static void + lock_dealloc(lockobject *self) + { ++ PyTypeObject *tp; ++ + PyObject_GC_UnTrack(self); + if (self->in_weakreflist != NULL) { + PyObject_ClearWeakRefs((PyObject *) self); +@@ -67,7 +69,7 @@ lock_dealloc(lockobject *self) + PyThread_release_lock(self->lock_lock); + PyThread_free_lock(self->lock_lock); + } +- PyTypeObject *tp = Py_TYPE(self); ++ tp = Py_TYPE(self); + tp->tp_free((PyObject*)self); + Py_DECREF(tp); + } +@@ -82,11 +84,11 @@ static PyLockStatus + acquire_timed(PyThread_type_lock lock, _PyTime_t timeout) + { + _PyTime_t endtime = 0; ++ PyLockStatus r; + if (timeout > 0) { + endtime = _PyDeadline_Init(timeout); + } + +- PyLockStatus r; + do { + _PyTime_t microseconds; + microseconds = _PyTime_AsMicroseconds(timeout, _PyTime_ROUND_CEILING); +@@ -173,10 +175,11 @@ static PyObject * + lock_PyThread_acquire_lock(lockobject *self, PyObject *args, PyObject *kwds) + { + _PyTime_t timeout; ++ PyLockStatus r; + if (lock_acquire_parse_args(args, kwds, &timeout) < 0) + return NULL; + +- PyLockStatus r = acquire_timed(self->lock_lock, timeout); ++ r = acquire_timed(self->lock_lock, timeout); + if (r == PY_LOCK_INTR) { + return NULL; + } +@@ -306,11 +309,12 @@ static PyType_Slot lock_type_slots[] = { + }; + + static PyType_Spec lock_type_spec = { +- .name = "_thread.lock", +- .basicsize = sizeof(lockobject), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | +- Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE), +- .slots = lock_type_slots, ++ "_thread.lock", ++ sizeof(lockobject), ++ 0, ++ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | ++ Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE), ++ lock_type_slots, + }; + + /* Recursive lock objects */ +@@ -334,6 +338,8 @@ rlock_traverse(rlockobject *self, visitproc visit, void *arg) + static void + rlock_dealloc(rlockobject *self) + { ++ PyTypeObject *tp; ++ + PyObject_GC_UnTrack(self); + if (self->in_weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *) self); +@@ -346,7 +352,7 @@ rlock_dealloc(rlockobject *self) + + PyThread_free_lock(self->rlock_lock); + } +- PyTypeObject *tp = Py_TYPE(self); ++ tp = Py_TYPE(self); + tp->tp_free(self); + Py_DECREF(tp); + } +@@ -603,11 +609,12 @@ static PyType_Slot rlock_type_slots[] = { + }; + + static PyType_Spec rlock_type_spec = { +- .name = "_thread.RLock", +- .basicsize = sizeof(rlockobject), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | +- Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), +- .slots = rlock_type_slots, ++ "_thread.RLock", ++ sizeof(rlockobject), ++ 0, ++ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | ++ Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), ++ rlock_type_slots, + }; + + static lockobject * +@@ -683,9 +690,11 @@ typedef struct { + static void + localdummy_dealloc(localdummyobject *self) + { ++ PyTypeObject *tp; ++ + if (self->weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *) self); +- PyTypeObject *tp = Py_TYPE(self); ++ tp = Py_TYPE(self); + tp->tp_free((PyObject*)self); + Py_DECREF(tp); + } +@@ -703,11 +712,12 @@ static PyType_Slot local_dummy_type_slots[] = { + }; + + static PyType_Spec local_dummy_type_spec = { +- .name = "_thread._localdummy", +- .basicsize = sizeof(localdummyobject), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | +- Py_TPFLAGS_IMMUTABLETYPE), +- .slots = local_dummy_type_slots, ++ "_thread._localdummy", ++ sizeof(localdummyobject), ++ 0, ++ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | ++ Py_TPFLAGS_IMMUTABLETYPE), ++ local_dummy_type_slots, + }; + + +@@ -735,6 +745,7 @@ _local_create_dummy(localobject *self, thread_module_state *state) + PyObject *ldict = NULL, *wr = NULL; + localdummyobject *dummy = NULL; + PyTypeObject *type = state->local_dummy_type; ++ int r; + + PyObject *tdict = PyThreadState_GetDict(); + if (tdict == NULL) { +@@ -759,7 +770,7 @@ _local_create_dummy(localobject *self, thread_module_state *state) + + /* As a side-effect, this will cache the weakref's hash before the + dummy gets deleted */ +- int r = PyDict_SetItem(self->dummies, wr, ldict); ++ r = PyDict_SetItem(self->dummies, wr, ldict); + if (r < 0) { + goto err; + } +@@ -787,6 +798,11 @@ local_new(PyTypeObject *type, PyObject *args, PyObject *kw) + "_localdummy_destroyed", (PyCFunction) _localdummy_destroyed, METH_O + }; + ++ PyObject *module; ++ thread_module_state *state; ++ localobject *self; ++ PyObject *wr; ++ + if (type->tp_init == PyBaseObject_Type.tp_init) { + int rc = 0; + if (args != NULL) +@@ -802,10 +818,10 @@ local_new(PyTypeObject *type, PyObject *args, PyObject *kw) + } + } + +- PyObject *module = PyType_GetModuleByDef(type, &thread_module); +- thread_module_state *state = get_thread_state(module); ++ module = PyType_GetModuleByDef(type, &thread_module); ++ state = get_thread_state(module); + +- localobject *self = (localobject *)type->tp_alloc(type, 0); ++ self = (localobject *)type->tp_alloc(type, 0); + if (self == NULL) { + return NULL; + } +@@ -824,7 +840,7 @@ local_new(PyTypeObject *type, PyObject *args, PyObject *kw) + + /* We use a weak reference to self in the callback closure + in order to avoid spurious reference cycles */ +- PyObject *wr = PyWeakref_NewRef((PyObject *) self, NULL); ++ wr = PyWeakref_NewRef((PyObject *) self, NULL); + if (wr == NULL) { + goto err; + } +@@ -869,8 +885,9 @@ local_clear(localobject *self) + if (self->key) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyRuntimeState *runtime = &_PyRuntime; ++ PyThreadState *tstate; + HEAD_LOCK(runtime); +- PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); ++ tstate = PyInterpreterState_ThreadHead(interp); + HEAD_UNLOCK(runtime); + while (tstate) { + if (tstate->dict) { +@@ -893,6 +910,8 @@ local_clear(localobject *self) + static void + local_dealloc(localobject *self) + { ++ PyTypeObject *tp; ++ + /* Weakrefs must be invalidated right now, otherwise they can be used + from code called below, which is very dangerous since Py_REFCNT(self) == 0 */ + if (self->weakreflist != NULL) { +@@ -904,7 +923,7 @@ local_dealloc(localobject *self) + local_clear(self); + Py_XDECREF(self->key); + +- PyTypeObject *tp = Py_TYPE(self); ++ tp = Py_TYPE(self); + tp->tp_free((PyObject*)self); + Py_DECREF(tp); + } +@@ -913,15 +932,18 @@ local_dealloc(localobject *self) + static PyObject * + _ldict(localobject *self, thread_module_state *state) + { +- PyObject *tdict = PyThreadState_GetDict(); ++ PyObject *tdict; ++ PyObject *ldict; ++ PyObject *dummy; ++ ++ tdict = PyThreadState_GetDict(); + if (tdict == NULL) { + PyErr_SetString(PyExc_SystemError, + "Couldn't get thread-state dictionary"); + return NULL; + } + +- PyObject *ldict; +- PyObject *dummy = PyDict_GetItemWithError(tdict, self->key); ++ dummy = PyDict_GetItemWithError(tdict, self->key); + if (dummy == NULL) { + if (PyErr_Occurred()) { + return NULL; +@@ -951,15 +973,20 @@ _ldict(localobject *self, thread_module_state *state) + static int + local_setattro(localobject *self, PyObject *name, PyObject *v) + { +- PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &thread_module); +- thread_module_state *state = get_thread_state(module); ++ PyObject *module; ++ thread_module_state *state; ++ PyObject *ldict; ++ int r; + +- PyObject *ldict = _ldict(self, state); ++ module = PyType_GetModuleByDef(Py_TYPE(self), &thread_module); ++ state = get_thread_state(module); ++ ++ ldict = _ldict(self, state); + if (ldict == NULL) { + return -1; + } + +- int r = PyObject_RichCompareBool(name, &_Py_ID(__dict__), Py_EQ); ++ r = PyObject_RichCompareBool(name, &_Py_ID(__dict__), Py_EQ); + if (r == -1) { + return -1; + } +@@ -993,24 +1020,31 @@ static PyType_Slot local_type_slots[] = { + }; + + static PyType_Spec local_type_spec = { +- .name = "_thread._local", +- .basicsize = sizeof(localobject), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | +- Py_TPFLAGS_IMMUTABLETYPE), +- .slots = local_type_slots, ++ "_thread._local", ++ sizeof(localobject), ++ 0, ++ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | ++ Py_TPFLAGS_IMMUTABLETYPE), ++ local_type_slots, + }; + + static PyObject * + local_getattro(localobject *self, PyObject *name) + { +- PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &thread_module); +- thread_module_state *state = get_thread_state(module); ++ PyObject *module; ++ thread_module_state *state; ++ PyObject *ldict; ++ int r; ++ PyObject *value; ++ ++ module = PyType_GetModuleByDef(Py_TYPE(self), &thread_module); ++ state = get_thread_state(module); + +- PyObject *ldict = _ldict(self, state); ++ ldict = _ldict(self, state); + if (ldict == NULL) + return NULL; + +- int r = PyObject_RichCompareBool(name, &_Py_ID(__dict__), Py_EQ); ++ r = PyObject_RichCompareBool(name, &_Py_ID(__dict__), Py_EQ); + if (r == 1) { + return Py_NewRef(ldict); + } +@@ -1025,7 +1059,7 @@ local_getattro(localobject *self, PyObject *name) + } + + /* Optimization: just look in dict ourselves */ +- PyObject *value = PyDict_GetItemWithError(ldict, name); ++ value = PyDict_GetItemWithError(ldict, name); + if (value != NULL) { + return Py_NewRef(value); + } +@@ -1042,15 +1076,18 @@ local_getattro(localobject *self, PyObject *name) + static PyObject * + _localdummy_destroyed(PyObject *localweakref, PyObject *dummyweakref) + { ++ PyObject *obj; ++ localobject *self; ++ + assert(PyWeakref_CheckRef(localweakref)); +- PyObject *obj = PyWeakref_GET_OBJECT(localweakref); ++ obj = PyWeakref_GET_OBJECT(localweakref); + if (obj == Py_None) { + Py_RETURN_NONE; + } + + /* If the thread-local object is still alive and not being cleared, + remove the corresponding local dict */ +- localobject *self = (localobject *)Py_NewRef(obj); ++ self = (localobject *)Py_NewRef(obj); + if (self->dummies != NULL) { + PyObject *ldict; + ldict = PyDict_GetItemWithError(self->dummies, dummyweakref); +@@ -1089,8 +1126,12 @@ thread_bootstate_free(struct bootstate *boot, int decref) + static void + thread_run(void *boot_raw) + { +- struct bootstate *boot = (struct bootstate *) boot_raw; +- PyThreadState *tstate = boot->tstate; ++ struct bootstate *boot; ++ PyThreadState *tstate; ++ PyObject *res; ++ ++ boot = (struct bootstate *) boot_raw; ++ tstate = boot->tstate; + + // gh-108987: If _thread.start_new_thread() is called before or while + // Python is being finalized, thread_run() can called *after*. +@@ -1121,7 +1162,7 @@ thread_run(void *boot_raw) + PyEval_AcquireThread(tstate); + tstate->interp->threads.count++; + +- PyObject *res = PyObject_Call(boot->func, boot->args, boot->kwargs); ++ res = PyObject_Call(boot->func, boot->args, boot->kwargs); + if (res == NULL) { + if (PyErr_ExceptionMatches(PyExc_SystemExit)) + /* SystemExit is ignored silently */ +@@ -1151,6 +1192,9 @@ static PyObject * + thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) + { + PyObject *func, *args, *kwargs = NULL; ++ PyInterpreterState *interp; ++ struct bootstate *boot; ++ unsigned long ident; + + if (!PyArg_UnpackTuple(fargs, "start_new_thread", 2, 3, + &func, &args, &kwargs)) +@@ -1171,7 +1215,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) + return NULL; + } + +- PyInterpreterState *interp = _PyInterpreterState_GET(); ++ interp = _PyInterpreterState_GET(); + if (interp->config._isolated_interpreter) { + PyErr_SetString(PyExc_RuntimeError, + "thread is not supported for isolated subinterpreters"); +@@ -1181,7 +1225,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) + // gh-109795: Use PyMem_RawMalloc() instead of PyMem_Malloc(), + // because it should be possible to call thread_bootstate_free() + // without holding the GIL. +- struct bootstate *boot = PyMem_RawMalloc(sizeof(struct bootstate)); ++ boot = PyMem_RawMalloc(sizeof(struct bootstate)); + if (boot == NULL) { + return PyErr_NoMemory(); + } +@@ -1194,7 +1238,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) + boot->args = Py_NewRef(args); + boot->kwargs = Py_XNewRef(kwargs); + +- unsigned long ident = PyThread_start_new_thread(thread_run, (void*) boot); ++ ident = PyThread_start_new_thread(thread_run, (void*) boot); + if (ident == PYTHREAD_INVALID_THREAD_ID) { + PyErr_SetString(ThreadError, "can't start new thread"); + PyThreadState_Clear(boot->tstate); +@@ -1445,12 +1489,15 @@ static int + thread_excepthook_file(PyObject *file, PyObject *exc_type, PyObject *exc_value, + PyObject *exc_traceback, PyObject *thread) + { ++ PyObject *name; ++ PyObject *res; ++ + /* print(f"Exception in thread {thread.name}:", file=file) */ + if (PyFile_WriteString("Exception in thread ", file) < 0) { + return -1; + } + +- PyObject *name = NULL; ++ name = NULL; + if (thread != Py_None) { + if (_PyObject_LookupAttr(thread, &_Py_ID(name), &name) < 0) { + return -1; +@@ -1490,7 +1537,7 @@ thread_excepthook_file(PyObject *file, PyObject *exc_type, PyObject *exc_value, + _PyErr_Display(file, exc_type, exc_value, exc_traceback); + + /* Call file.flush() */ +- PyObject *res = PyObject_CallMethodNoArgs(file, &_Py_ID(flush)); ++ res = PyObject_CallMethodNoArgs(file, &_Py_ID(flush)); + if (!res) { + return -1; + } +@@ -1514,17 +1561,26 @@ static PyStructSequence_Field ExceptHookArgs_fields[] = { + }; + + static PyStructSequence_Desc ExceptHookArgs_desc = { +- .name = "_thread._ExceptHookArgs", +- .doc = ExceptHookArgs__doc__, +- .fields = ExceptHookArgs_fields, +- .n_in_sequence = 4 ++ "_thread._ExceptHookArgs", ++ ExceptHookArgs__doc__, ++ ExceptHookArgs_fields, ++ 4 + }; + + + static PyObject * + thread_excepthook(PyObject *module, PyObject *args) + { +- thread_module_state *state = get_thread_state(module); ++ thread_module_state *state; ++ PyObject *exc_type; ++ PyObject *exc_value; ++ PyObject *exc_tb; ++ PyObject *thread; ++ PyThreadState *tstate; ++ PyObject *file; ++ int res; ++ ++ state = get_thread_state(module); + + if (!Py_IS_TYPE(args, state->excepthook_type)) { + PyErr_SetString(PyExc_TypeError, +@@ -1534,19 +1590,19 @@ thread_excepthook(PyObject *module, PyObject *args) + } + + /* Borrowed reference */ +- PyObject *exc_type = PyStructSequence_GET_ITEM(args, 0); ++ exc_type = PyStructSequence_GET_ITEM(args, 0); + if (exc_type == PyExc_SystemExit) { + /* silently ignore SystemExit */ + Py_RETURN_NONE; + } + + /* Borrowed references */ +- PyObject *exc_value = PyStructSequence_GET_ITEM(args, 1); +- PyObject *exc_tb = PyStructSequence_GET_ITEM(args, 2); +- PyObject *thread = PyStructSequence_GET_ITEM(args, 3); ++ exc_value = PyStructSequence_GET_ITEM(args, 1); ++ exc_tb = PyStructSequence_GET_ITEM(args, 2); ++ thread = PyStructSequence_GET_ITEM(args, 3); + +- PyThreadState *tstate = _PyThreadState_GET(); +- PyObject *file = _PySys_GetAttr(tstate, &_Py_ID(stderr)); ++ tstate = _PyThreadState_GET(); ++ file = _PySys_GetAttr(tstate, &_Py_ID(stderr)); + if (file == NULL || file == Py_None) { + if (thread == Py_None) { + /* do nothing if sys.stderr is None and thread is None */ +@@ -1568,8 +1624,8 @@ thread_excepthook(PyObject *module, PyObject *args) + Py_INCREF(file); + } + +- int res = thread_excepthook_file(file, exc_type, exc_value, exc_tb, +- thread); ++ res = thread_excepthook_file(file, exc_type, exc_value, exc_tb, ++ thread); + Py_DECREF(file); + if (res < 0) { + return NULL; +@@ -1621,8 +1677,14 @@ static PyMethodDef thread_methods[] = { + static int + thread_module_exec(PyObject *module) + { +- thread_module_state *state = get_thread_state(module); +- PyObject *d = PyModule_GetDict(module); ++ thread_module_state *state; ++ PyObject *d; ++ PyTypeObject *rlock_type; ++ double timeout_max; ++ double time_max; ++ ++ state = get_thread_state(module); ++ d = PyModule_GetDict(module); + + // Initialize the C thread library + PyThread_init_thread(); +@@ -1637,7 +1699,7 @@ thread_module_exec(PyObject *module) + } + + // RLock +- PyTypeObject *rlock_type = (PyTypeObject *)PyType_FromSpec(&rlock_type_spec); ++ rlock_type = (PyTypeObject *)PyType_FromSpec(&rlock_type_spec); + if (rlock_type == NULL) { + return -1; + } +@@ -1677,8 +1739,8 @@ thread_module_exec(PyObject *module) + } + + // TIMEOUT_MAX +- double timeout_max = (double)PY_TIMEOUT_MAX * 1e-6; +- double time_max = _PyTime_AsSecondsDouble(_PyTime_MAX); ++ timeout_max = (double)PY_TIMEOUT_MAX * 1e-6; ++ time_max = _PyTime_AsSecondsDouble(_PyTime_MAX); + timeout_max = Py_MIN(timeout_max, time_max); + // Round towards minus infinity + timeout_max = floor(timeout_max); +@@ -1733,14 +1795,14 @@ static PyModuleDef_Slot thread_module_slots[] = { + + static struct PyModuleDef thread_module = { + PyModuleDef_HEAD_INIT, +- .m_name = "_thread", +- .m_doc = thread_doc, +- .m_size = sizeof(thread_module_state), +- .m_methods = thread_methods, +- .m_traverse = thread_module_traverse, +- .m_clear = thread_module_clear, +- .m_free = thread_module_free, +- .m_slots = thread_module_slots, ++ "_thread", ++ thread_doc, ++ sizeof(thread_module_state), ++ thread_methods, ++ thread_module_slots, ++ thread_module_traverse, ++ thread_module_clear, ++ thread_module_free, + }; + + PyMODINIT_FUNC +diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c +index 44a1f7b..dd181fb 100644 +--- a/Modules/_tracemalloc.c ++++ b/Modules/_tracemalloc.c +@@ -280,6 +280,7 @@ hashtable_compare_traceback(const void *key1, const void *key2) + { + const traceback_t *traceback1 = (const traceback_t *)key1; + const traceback_t *traceback2 = (const traceback_t *)key2; ++ int i; + + if (traceback1->nframe != traceback2->nframe) { + return 0; +@@ -288,7 +289,7 @@ hashtable_compare_traceback(const void *key1, const void *key2) + return 0; + } + +- for (int i=0; i < traceback1->nframe; i++) { ++ for (i=0; i < traceback1->nframe; i++) { + const frame_t *frame1 = &traceback1->frames[i]; + const frame_t *frame2 = &traceback2->frames[i]; + +@@ -307,14 +308,18 @@ hashtable_compare_traceback(const void *key1, const void *key2) + static void + tracemalloc_get_frame(_PyInterpreterFrame *pyframe, frame_t *frame) + { ++ int lineno; ++ PyObject *filename; ++ _Py_hashtable_entry_t *entry; ++ + frame->filename = &_Py_STR(anon_unknown); +- int lineno = _PyInterpreterFrame_GetLine(pyframe); ++ lineno = _PyInterpreterFrame_GetLine(pyframe); + if (lineno < 0) { + lineno = 0; + } + frame->lineno = (unsigned int)lineno; + +- PyObject *filename = pyframe->f_code->co_filename; ++ filename = pyframe->f_code->co_filename; + + if (filename == NULL) { + #ifdef TRACE_DEBUG +@@ -339,7 +344,6 @@ tracemalloc_get_frame(_PyInterpreterFrame *pyframe, frame_t *frame) + } + + /* intern the filename */ +- _Py_hashtable_entry_t *entry; + entry = _Py_hashtable_get_entry(tracemalloc_filenames, filename); + if (entry != NULL) { + filename = (PyObject *)entry->key; +@@ -391,7 +395,10 @@ traceback_hash(traceback_t *traceback) + static void + traceback_get_frames(traceback_t *traceback) + { +- PyThreadState *tstate = PyGILState_GetThisThreadState(); ++ PyThreadState *tstate; ++ _PyInterpreterFrame *pyframe; ++ ++ tstate = PyGILState_GetThisThreadState(); + if (tstate == NULL) { + #ifdef TRACE_DEBUG + tracemalloc_error("failed to get the current thread state"); +@@ -399,7 +406,7 @@ traceback_get_frames(traceback_t *traceback) + return; + } + +- _PyInterpreterFrame *pyframe = tstate->cframe->current_frame; ++ pyframe = tstate->cframe->current_frame; + for (;;) { + while (pyframe && _PyFrame_IsIncomplete(pyframe)) { + pyframe = pyframe->previous; +@@ -505,14 +512,17 @@ tracemalloc_get_traces_table(unsigned int domain) + static void + tracemalloc_remove_trace(unsigned int domain, uintptr_t ptr) + { ++ _Py_hashtable_t *traces; ++ trace_t *trace; ++ + assert(_Py_tracemalloc_config.tracing); + +- _Py_hashtable_t *traces = tracemalloc_get_traces_table(domain); ++ traces = tracemalloc_get_traces_table(domain); + if (!traces) { + return; + } + +- trace_t *trace = _Py_hashtable_steal(traces, TO_PTR(ptr)); ++ trace = _Py_hashtable_steal(traces, TO_PTR(ptr)); + if (!trace) { + return; + } +@@ -529,14 +539,19 @@ static int + tracemalloc_add_trace(unsigned int domain, uintptr_t ptr, + size_t size) + { ++ traceback_t *traceback; ++ _Py_hashtable_t *traces; ++ trace_t *trace; ++ int res; ++ + assert(_Py_tracemalloc_config.tracing); + +- traceback_t *traceback = traceback_new(); ++ traceback = traceback_new(); + if (traceback == NULL) { + return -1; + } + +- _Py_hashtable_t *traces = tracemalloc_get_traces_table(domain); ++ traces = tracemalloc_get_traces_table(domain); + if (traces == NULL) { + traces = tracemalloc_create_traces_table(); + if (traces == NULL) { +@@ -549,7 +564,7 @@ tracemalloc_add_trace(unsigned int domain, uintptr_t ptr, + } + } + +- trace_t *trace = _Py_hashtable_get(traces, TO_PTR(ptr)); ++ trace = _Py_hashtable_get(traces, TO_PTR(ptr)); + if (trace != NULL) { + /* the memory block is already tracked */ + assert(tracemalloc_traced_memory >= trace->size); +@@ -566,7 +581,7 @@ tracemalloc_add_trace(unsigned int domain, uintptr_t ptr, + trace->size = size; + trace->traceback = traceback; + +- int res = _Py_hashtable_set(traces, TO_PTR(ptr), trace); ++ res = _Py_hashtable_set(traces, TO_PTR(ptr), trace); + if (res != 0) { + raw_free(trace); + return res; +@@ -1103,6 +1118,7 @@ static PyObject* + traceback_to_pyobject(traceback_t *traceback, _Py_hashtable_t *intern_table) + { + PyObject *frames; ++ int i; + + if (intern_table != NULL) { + frames = _Py_hashtable_get(intern_table, (const void *)traceback); +@@ -1116,7 +1132,7 @@ traceback_to_pyobject(traceback_t *traceback, _Py_hashtable_t *intern_table) + if (frames == NULL) + return NULL; + +- for (int i=0; i < traceback->nframe; i++) { ++ for (i=0; i < traceback->nframe; i++) { + PyObject *frame = frame_to_pyobject(&traceback->frames[i]); + if (frame == NULL) { + Py_DECREF(frames); +@@ -1215,12 +1231,15 @@ tracemalloc_copy_trace(_Py_hashtable_t *traces, + static _Py_hashtable_t* + tracemalloc_copy_traces(_Py_hashtable_t *traces) + { +- _Py_hashtable_t *traces2 = tracemalloc_create_traces_table(); ++ _Py_hashtable_t *traces2; ++ int err; ++ ++ traces2 = tracemalloc_create_traces_table(); + if (traces2 == NULL) { + return NULL; + } + +- int err = _Py_hashtable_foreach(traces, ++ err = _Py_hashtable_foreach(traces, + tracemalloc_copy_trace, + traces2); + if (err) { +@@ -1256,12 +1275,15 @@ tracemalloc_copy_domain(_Py_hashtable_t *domains, + static _Py_hashtable_t* + tracemalloc_copy_domains(_Py_hashtable_t *domains) + { +- _Py_hashtable_t *domains2 = tracemalloc_create_domains_table(); ++ _Py_hashtable_t *domains2; ++ int err; ++ ++ domains2 = tracemalloc_create_domains_table(); + if (domains2 == NULL) { + return NULL; + } + +- int err = _Py_hashtable_foreach(domains, ++ err = _Py_hashtable_foreach(domains, + tracemalloc_copy_domain, + domains2); + if (err) { +@@ -1277,17 +1299,22 @@ tracemalloc_get_traces_fill(_Py_hashtable_t *traces, + const void *key, const void *value, + void *user_data) + { +- get_traces_t *get_traces = user_data; ++ get_traces_t *get_traces; ++ const trace_t *trace; ++ PyObject *tuple; ++ int res; ++ ++ get_traces = user_data; + +- const trace_t *trace = (const trace_t *)value; ++ trace = (const trace_t *)value; + +- PyObject *tuple = trace_to_pyobject(get_traces->domain, trace, +- get_traces->tracebacks); ++ tuple = trace_to_pyobject(get_traces->domain, trace, ++ get_traces->tracebacks); + if (tuple == NULL) { + return 1; + } + +- int res = PyList_Append(get_traces->list, tuple); ++ res = PyList_Append(get_traces->list, tuple); + Py_DECREF(tuple); + if (res < 0) { + return 1; +@@ -1339,6 +1366,7 @@ _tracemalloc__get_traces_impl(PyObject *module) + /*[clinic end generated code: output=e9929876ced4b5cc input=6c7d2230b24255aa]*/ + { + get_traces_t get_traces; ++ int err; + get_traces.domain = DEFAULT_DOMAIN; + get_traces.traces = NULL; + get_traces.domains = NULL; +@@ -1380,7 +1408,7 @@ _tracemalloc__get_traces_impl(PyObject *module) + + // Convert traces to a list of tuples + set_reentrant(1); +- int err = _Py_hashtable_foreach(get_traces.traces, ++ err = _Py_hashtable_foreach(get_traces.traces, + tracemalloc_get_traces_fill, + &get_traces); + if (!err) { +@@ -1419,13 +1447,14 @@ _tracemalloc__get_traces_impl(PyObject *module) + static traceback_t* + tracemalloc_get_traceback(unsigned int domain, uintptr_t ptr) + { ++ trace_t *trace; ++ _Py_hashtable_t *traces; + + if (!_Py_tracemalloc_config.tracing) + return NULL; + +- trace_t *trace; + TABLES_LOCK(); +- _Py_hashtable_t *traces = tracemalloc_get_traces_table(domain); ++ traces = tracemalloc_get_traces_table(domain); + if (traces) { + trace = _Py_hashtable_get(traces, TO_PTR(ptr)); + } +@@ -1777,6 +1806,11 @@ PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr) + int + _PyTraceMalloc_NewReference(PyObject *op) + { ++ uintptr_t ptr; ++ PyTypeObject *type; ++ int res; ++ trace_t *trace; ++ + assert(PyGILState_Check()); + + if (!_Py_tracemalloc_config.tracing) { +@@ -1784,8 +1818,7 @@ _PyTraceMalloc_NewReference(PyObject *op) + return -1; + } + +- uintptr_t ptr; +- PyTypeObject *type = Py_TYPE(op); ++ type = Py_TYPE(op); + if (PyType_IS_GC(type)) { + ptr = (uintptr_t)((char *)op - sizeof(PyGC_Head)); + } +@@ -1793,10 +1826,10 @@ _PyTraceMalloc_NewReference(PyObject *op) + ptr = (uintptr_t)op; + } + +- int res = -1; ++ res = -1; + + TABLES_LOCK(); +- trace_t *trace = _Py_hashtable_get(tracemalloc_traces, TO_PTR(ptr)); ++ trace = _Py_hashtable_get(tracemalloc_traces, TO_PTR(ptr)); + if (trace != NULL) { + /* update the traceback of the memory block */ + traceback_t *traceback = traceback_new(); +diff --git a/Modules/_winapi.c b/Modules/_winapi.c +index 0376cc0..dbdfbb3 100644 +--- a/Modules/_winapi.c ++++ b/Modules/_winapi.c +@@ -40,6 +40,57 @@ + + + #include "windows.h" ++/* Legacy SDK names only; these definitions do not add Win9x API support. */ ++#if defined(_MSC_VER) && _MSC_VER < 1300 ++# ifndef INVALID_FILE_ATTRIBUTES ++# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) ++# endif ++# ifndef FSCTL_SET_REPARSE_POINT ++# define FSCTL_SET_REPARSE_POINT 0x000900A4 ++# endif ++# ifndef LOCALE_NAME_INVARIANT ++# define LOCALE_NAME_INVARIANT L"" ++# endif ++# ifndef LOCALE_NAME_MAX_LENGTH ++# define LOCALE_NAME_MAX_LENGTH 85 ++# endif ++# ifndef LOCALE_NAME_SYSTEM_DEFAULT ++# define LOCALE_NAME_SYSTEM_DEFAULT L"!sys-default-locale" ++# endif ++# ifndef LOCALE_NAME_USER_DEFAULT ++# define LOCALE_NAME_USER_DEFAULT NULL ++# endif ++# ifndef LCMAP_SORTHANDLE ++# define LCMAP_SORTHANDLE 0x20000000 ++# endif ++# ifndef LCMAP_HASH ++# define LCMAP_HASH 0x00040000 ++# endif ++# ifndef LCMAP_TITLECASE ++# define LCMAP_TITLECASE 0x00000300 ++# endif ++# ifndef FILE_FLAG_FIRST_PIPE_INSTANCE ++# define FILE_FLAG_FIRST_PIPE_INSTANCE 0x00080000 ++# endif ++# ifndef FILE_MAP_EXECUTE ++# define FILE_MAP_EXECUTE 0x0020 ++# endif ++# ifndef SEC_LARGE_PAGES ++# define SEC_LARGE_PAGES 0x80000000 ++# endif ++# ifndef SEC_WRITECOMBINE ++# define SEC_WRITECOMBINE 0x40000000 ++# endif ++# ifndef ABOVE_NORMAL_PRIORITY_CLASS ++# define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000 ++# endif ++# ifndef BELOW_NORMAL_PRIORITY_CLASS ++# define BELOW_NORMAL_PRIORITY_CLASS 0x00004000 ++# endif ++# ifndef CREATE_BREAKAWAY_FROM_JOB ++# define CREATE_BREAKAWAY_FROM_JOB 0x01000000 ++# endif ++#endif + #include + #include "winreparse.h" + +@@ -130,6 +181,7 @@ overlapped_traverse(OverlappedObject *self, visitproc visit, void *arg) + static void + overlapped_dealloc(OverlappedObject *self) + { ++ PyTypeObject *tp; + DWORD bytes; + int err = GetLastError(); + +@@ -169,7 +221,7 @@ overlapped_dealloc(OverlappedObject *self) + if (self->write_buffer.obj) + PyBuffer_Release(&self->write_buffer); + Py_CLEAR(self->read_buffer); +- PyTypeObject *tp = Py_TYPE(self); ++ tp = Py_TYPE(self); + tp->tp_free(self); + Py_DECREF(tp); + } +@@ -346,11 +398,12 @@ static PyType_Slot winapi_overlapped_type_slots[] = { + }; + + static PyType_Spec winapi_overlapped_type_spec = { +- .name = "_winapi.Overlapped", +- .basicsize = sizeof(OverlappedObject), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | +- Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), +- .slots = winapi_overlapped_type_slots, ++ "_winapi.Overlapped", ++ sizeof(OverlappedObject), ++ 0, ++ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | ++ Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE, ++ winapi_overlapped_type_slots, + }; + + static OverlappedObject * +@@ -818,17 +871,23 @@ static PyMethodDef sortenvironmentkey_def = { + static int + sort_environment_keys(PyObject *keys) + { +- PyObject *keyfunc = PyCFunction_New(&sortenvironmentkey_def, NULL); ++ PyObject *keyfunc; ++ PyObject *kwnames; ++ PyObject *args[2]; ++ PyObject *ret; ++ ++ keyfunc = PyCFunction_New(&sortenvironmentkey_def, NULL); + if (keyfunc == NULL) { + return -1; + } +- PyObject *kwnames = Py_BuildValue("(s)", "key"); ++ kwnames = Py_BuildValue("(s)", "key"); + if (kwnames == NULL) { + Py_DECREF(keyfunc); + return -1; + } +- PyObject *args[] = { keys, keyfunc }; +- PyObject *ret = PyObject_VectorcallMethod(&_Py_ID(sort), args, 1, kwnames); ++ args[0] = keys; ++ args[1] = keyfunc; ++ ret = PyObject_VectorcallMethod(&_Py_ID(sort), args, 1, kwnames); + Py_DECREF(keyfunc); + Py_DECREF(kwnames); + if (ret == NULL) { +@@ -842,11 +901,14 @@ sort_environment_keys(PyObject *keys) + static int + compare_string_ordinal(PyObject *str1, PyObject *str2, int *result) + { +- wchar_t *s1 = PyUnicode_AsWideCharString(str1, NULL); ++ wchar_t *s1; ++ wchar_t *s2; ++ ++ s1 = PyUnicode_AsWideCharString(str1, NULL); + if (s1 == NULL) { + return -1; + } +- wchar_t *s2 = PyUnicode_AsWideCharString(str2, NULL); ++ s2 = PyUnicode_AsWideCharString(str2, NULL); + if (s2 == NULL) { + PyMem_Free(s1); + return -1; +@@ -860,7 +922,10 @@ compare_string_ordinal(PyObject *str1, PyObject *str2, int *result) + static PyObject * + dedup_environment_keys(PyObject *keys) + { +- PyObject *result = PyList_New(0); ++ PyObject *result; ++ Py_ssize_t i; ++ ++ result = PyList_New(0); + if (result == NULL) { + return NULL; + } +@@ -869,8 +934,12 @@ dedup_environment_keys(PyObject *keys) + // to the next key (ignoring case), if different, insert the current value + // into the result list. If they are equal, do nothing because we always + // want to keep the last inserted one. +- for (Py_ssize_t i = 0; i < PyList_GET_SIZE(keys); i++) { +- PyObject *key = PyList_GET_ITEM(keys, i); ++ for (i = 0; i < PyList_GET_SIZE(keys); i++) { ++ PyObject *key; ++ PyObject *next_key; ++ int compare_result; ++ ++ key = PyList_GET_ITEM(keys, i); + + // The last key will always be kept. + if (i + 1 == PyList_GET_SIZE(keys)) { +@@ -881,8 +950,7 @@ dedup_environment_keys(PyObject *keys) + continue; + } + +- PyObject *next_key = PyList_GET_ITEM(keys, i + 1); +- int compare_result; ++ next_key = PyList_GET_ITEM(keys, i + 1); + if (compare_string_ordinal(key, next_key, &compare_result) < 0) { + Py_DECREF(result); + return NULL; +@@ -902,7 +970,15 @@ dedup_environment_keys(PyObject *keys) + static PyObject * + normalize_environment(PyObject *environment) + { +- PyObject *keys = PyMapping_Keys(environment); ++ PyObject *keys; ++ PyObject *normalized_keys; ++ PyObject *result; ++ PyObject *key; ++ PyObject *value; ++ int i; ++ int ret; ++ ++ keys = PyMapping_Keys(environment); + if (keys == NULL) { + return NULL; + } +@@ -912,28 +988,28 @@ normalize_environment(PyObject *environment) + return NULL; + } + +- PyObject *normalized_keys = dedup_environment_keys(keys); ++ normalized_keys = dedup_environment_keys(keys); + Py_DECREF(keys); + if (normalized_keys == NULL) { + return NULL; + } + +- PyObject *result = PyDict_New(); ++ result = PyDict_New(); + if (result == NULL) { + Py_DECREF(normalized_keys); + return NULL; + } + +- for (int i = 0; i < PyList_GET_SIZE(normalized_keys); i++) { +- PyObject *key = PyList_GET_ITEM(normalized_keys, i); +- PyObject *value = PyObject_GetItem(environment, key); ++ for (i = 0; i < PyList_GET_SIZE(normalized_keys); i++) { ++ key = PyList_GET_ITEM(normalized_keys, i); ++ value = PyObject_GetItem(environment, key); + if (value == NULL) { + Py_DECREF(normalized_keys); + Py_DECREF(result); + return NULL; + } + +- int ret = PyObject_SetItem(result, key, value); ++ ret = PyObject_SetItem(result, key, value); + Py_DECREF(value); + if (ret < 0) { + Py_DECREF(normalized_keys); +@@ -1118,6 +1194,38 @@ gethandlelist(PyObject *mapping, const char *name, Py_ssize_t *size) + return ret; + } + ++#if defined(_MSC_VER) && _MSC_VER < 1300 ++/* Vista-only process/thread attribute APIs are unavailable in VC6. */ ++typedef struct { ++ int unused; ++} AttributeList; ++ ++static void ++freeattributelist(AttributeList *attribute_list) ++{ ++ (void)attribute_list; ++} ++ ++static int ++getattributelist(PyObject *obj, const char *name, AttributeList *attribute_list) ++{ ++ PyObject *value; ++ (void)attribute_list; ++ ++ value = PyObject_GetAttrString(obj, name); ++ if (!value) { ++ PyErr_Clear(); /* Preserve the existing optional-attribute behavior. */ ++ return 0; ++ } ++ if (value == Py_None) { ++ Py_DECREF(value); ++ return 0; ++ } ++ Py_DECREF(value); ++ PyErr_SetFromWindowsErr(ERROR_CALL_NOT_IMPLEMENTED); ++ return -1; ++} ++#else + typedef struct { + LPPROC_THREAD_ATTRIBUTE_LIST attribute_list; + LPHANDLE handle_list; +@@ -1228,6 +1336,7 @@ getattributelist(PyObject *obj, const char *name, AttributeList *attribute_list) + + return ret; + } ++#endif + + /*[clinic input] + _winapi.CreateProcess +@@ -1265,18 +1374,25 @@ _winapi_CreateProcess_impl(PyObject *module, + PyObject *ret = NULL; + BOOL result; + PROCESS_INFORMATION pi; ++ STARTUPINFOW *si_base; ++#if defined(_MSC_VER) && _MSC_VER < 1300 ++ STARTUPINFOW si; ++#else + STARTUPINFOEXW si; ++#endif + wchar_t *wenvironment = NULL; + wchar_t *command_line_copy = NULL; + AttributeList attribute_list = {0}; ++ PyInterpreterState *interp; ++ const PyConfig *config; + + if (PySys_Audit("_winapi.CreateProcess", "uuu", application_name, + command_line, current_directory) < 0) { + return NULL; + } + +- PyInterpreterState *interp = PyInterpreterState_Get(); +- const PyConfig *config = _PyInterpreterState_GetConfig(interp); ++ interp = PyInterpreterState_Get(); ++ config = _PyInterpreterState_GetConfig(interp); + if (config->_isolated_interpreter) { + PyErr_SetString(PyExc_RuntimeError, + "subprocess not supported for isolated subinterpreters"); +@@ -1284,14 +1400,19 @@ _winapi_CreateProcess_impl(PyObject *module, + } + + ZeroMemory(&si, sizeof(si)); +- si.StartupInfo.cb = sizeof(si); ++#if defined(_MSC_VER) && _MSC_VER < 1300 ++ si_base = &si; ++#else ++ si_base = &si.StartupInfo; ++#endif ++ si_base->cb = sizeof(si); + + /* note: we only support a small subset of all SI attributes */ +- si.StartupInfo.dwFlags = getulong(startup_info, "dwFlags"); +- si.StartupInfo.wShowWindow = (WORD)getulong(startup_info, "wShowWindow"); +- si.StartupInfo.hStdInput = gethandle(startup_info, "hStdInput"); +- si.StartupInfo.hStdOutput = gethandle(startup_info, "hStdOutput"); +- si.StartupInfo.hStdError = gethandle(startup_info, "hStdError"); ++ si_base->dwFlags = getulong(startup_info, "dwFlags"); ++ si_base->wShowWindow = (WORD)getulong(startup_info, "wShowWindow"); ++ si_base->hStdInput = gethandle(startup_info, "hStdInput"); ++ si_base->hStdOutput = gethandle(startup_info, "hStdOutput"); ++ si_base->hStdError = gethandle(startup_info, "hStdError"); + if (PyErr_Occurred()) + goto cleanup; + +@@ -1305,7 +1426,9 @@ _winapi_CreateProcess_impl(PyObject *module, + if (getattributelist(startup_info, "lpAttributeList", &attribute_list) < 0) + goto cleanup; + ++#if !defined(_MSC_VER) || _MSC_VER >= 1300 + si.lpAttributeList = attribute_list.attribute_list; ++#endif + if (PyUnicode_Check(command_line)) { + command_line_copy = PyUnicode_AsWideCharString(command_line, NULL); + if (command_line_copy == NULL) { +@@ -1326,8 +1449,12 @@ _winapi_CreateProcess_impl(PyObject *module, + NULL, + NULL, + inherit_handles, ++#if defined(_MSC_VER) && _MSC_VER < 1300 ++ creation_flags | CREATE_UNICODE_ENVIRONMENT, ++#else + creation_flags | EXTENDED_STARTUPINFO_PRESENT | + CREATE_UNICODE_ENVIRONMENT, ++#endif + wenvironment, + current_directory, + (LPSTARTUPINFOW)&si, +@@ -1736,17 +1863,24 @@ _winapi_LCMapStringEx_impl(PyObject *module, PyObject *locale, DWORD flags, + PyObject *src) + /*[clinic end generated code: output=8ea4c9d85a4a1f23 input=2fa6ebc92591731b]*/ + { ++ wchar_t *locale_; ++ Py_ssize_t src_size; ++ wchar_t *src_; ++ int dest_size; ++ wchar_t *dest; ++ int nmapped; ++ PyObject *ret; ++ + if (flags & (LCMAP_SORTHANDLE | LCMAP_HASH | LCMAP_BYTEREV | + LCMAP_SORTKEY)) { + return PyErr_Format(PyExc_ValueError, "unsupported flags"); + } + +- wchar_t *locale_ = PyUnicode_AsWideCharString(locale, NULL); ++ locale_ = PyUnicode_AsWideCharString(locale, NULL); + if (!locale_) { + return NULL; + } +- Py_ssize_t src_size; +- wchar_t *src_ = PyUnicode_AsWideCharString(src, &src_size); ++ src_ = PyUnicode_AsWideCharString(src, &src_size); + if (!src_) { + PyMem_Free(locale_); + return NULL; +@@ -1758,7 +1892,7 @@ _winapi_LCMapStringEx_impl(PyObject *module, PyObject *locale, DWORD flags, + return NULL; + } + +- int dest_size = LCMapStringEx(locale_, flags, src_, (int)src_size, NULL, 0, ++ dest_size = LCMapStringEx(locale_, flags, src_, (int)src_size, NULL, 0, + NULL, NULL, 0); + if (dest_size <= 0) { + DWORD error = GetLastError(); +@@ -1767,14 +1901,14 @@ _winapi_LCMapStringEx_impl(PyObject *module, PyObject *locale, DWORD flags, + return PyErr_SetFromWindowsErr(error); + } + +- wchar_t* dest = PyMem_NEW(wchar_t, dest_size); ++ dest = PyMem_NEW(wchar_t, dest_size); + if (dest == NULL) { + PyMem_Free(locale_); + PyMem_Free(src_); + return PyErr_NoMemory(); + } + +- int nmapped = LCMapStringEx(locale_, flags, src_, (int)src_size, dest, dest_size, ++ nmapped = LCMapStringEx(locale_, flags, src_, (int)src_size, dest, dest_size, + NULL, NULL, 0); + if (nmapped <= 0) { + DWORD error = GetLastError(); +@@ -1786,7 +1920,7 @@ _winapi_LCMapStringEx_impl(PyObject *module, PyObject *locale, DWORD flags, + + PyMem_Free(locale_); + PyMem_Free(src_); +- PyObject *ret = PyUnicode_FromWideChar(dest, nmapped); ++ ret = PyUnicode_FromWideChar(dest, nmapped); + PyMem_DEL(dest); + + return ret; +@@ -2199,10 +2333,12 @@ _winapi__mimetypes_read_windows_registry_impl(PyObject *module, + int entry = 0; + HKEY hkcr = NULL; + LRESULT err; ++ DWORD i; ++ int j; + + Py_BEGIN_ALLOW_THREADS + err = RegOpenKeyExW(HKEY_CLASSES_ROOT, NULL, 0, KEY_READ, &hkcr); +- for (DWORD i = 0; err == ERROR_SUCCESS || err == ERROR_MORE_DATA; ++i) { ++ for (i = 0; err == ERROR_SUCCESS || err == ERROR_MORE_DATA; ++i) { + LPWSTR ext = entries[entry].ext; + LPWSTR type = entries[entry].type; + DWORD cchExt = CCH_EXT; +@@ -2241,7 +2377,7 @@ _winapi__mimetypes_read_windows_registry_impl(PyObject *module, + /* Flush our cached entries if we are full */ + if (entry == sizeof(entries) / sizeof(entries[0])) { + Py_BLOCK_THREADS +- for (int j = 0; j < entry; ++j) { ++ for (j = 0; j < entry; ++j) { + PyObject *r = PyObject_CallFunction( + on_type_read, "uu", entries[j].type, entries[j].ext + ); +@@ -2266,7 +2402,7 @@ _winapi__mimetypes_read_windows_registry_impl(PyObject *module, + return NULL; + } + +- for (int j = 0; j < entry; ++j) { ++ for (j = 0; j < entry; ++j) { + PyObject *r = PyObject_CallFunction( + on_type_read, "uu", entries[j].type, entries[j].ext + ); +@@ -2334,7 +2470,10 @@ static PyMethodDef winapi_functions[] = { + + static int winapi_exec(PyObject *m) + { +- WinApiState *st = winapi_get_state(m); ++ WinApiState *st; ++ PyObject *d; ++ ++ st = winapi_get_state(m); + + st->overlapped_type = (PyTypeObject *)PyType_FromModuleAndSpec(m, &winapi_overlapped_type_spec, NULL); + if (st->overlapped_type == NULL) { +@@ -2345,7 +2484,7 @@ static int winapi_exec(PyObject *m) + return -1; + } + +- PyObject *d = PyModule_GetDict(m); ++ d = PyModule_GetDict(m); + + /* constants */ + WINAPI_CONSTANT(F_DWORD, CREATE_NEW_CONSOLE); +@@ -2492,13 +2631,14 @@ winapi_free(void *module) + + static struct PyModuleDef winapi_module = { + PyModuleDef_HEAD_INIT, +- .m_name = "_winapi", +- .m_size = sizeof(WinApiState), +- .m_methods = winapi_functions, +- .m_slots = winapi_slots, +- .m_traverse = winapi_traverse, +- .m_clear = winapi_clear, +- .m_free = winapi_free, ++ "_winapi", ++ NULL, ++ sizeof(WinApiState), ++ winapi_functions, ++ winapi_slots, ++ winapi_traverse, ++ winapi_clear, ++ winapi_free, + }; + + PyMODINIT_FUNC +diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c +index e5b96be..b60e38c 100644 +--- a/Modules/_xxsubinterpretersmodule.c ++++ b/Modules/_xxsubinterpretersmodule.c +@@ -15,10 +15,11 @@ static char * + _copy_raw_string(PyObject *strobj) + { + const char *str = PyUnicode_AsUTF8(strobj); ++ char *copied; + if (str == NULL) { + return NULL; + } +- char *copied = PyMem_Malloc(strlen(str)+1); ++ copied = PyMem_Malloc(strlen(str)+1); + if (copied == NULL) { + PyErr_NoMemory(); + return NULL; +@@ -72,16 +73,20 @@ _sharednsitem_clear(struct _sharednsitem *item) + static int + _sharednsitem_apply(struct _sharednsitem *item, PyObject *ns) + { +- PyObject *name = PyUnicode_FromString(item->name); ++ PyObject *name; ++ PyObject *value; ++ int res; ++ ++ name = PyUnicode_FromString(item->name); + if (name == NULL) { + return -1; + } +- PyObject *value = _PyCrossInterpreterData_NewObject(&item->data); ++ value = _PyCrossInterpreterData_NewObject(&item->data); + if (value == NULL) { + Py_DECREF(name); + return -1; + } +- int res = PyDict_SetItem(ns, name, value); ++ res = PyDict_SetItem(ns, name, value); + Py_DECREF(name); + Py_DECREF(value); + return res; +@@ -113,7 +118,8 @@ _sharedns_new(Py_ssize_t len) + static void + _sharedns_free(_sharedns *shared) + { +- for (Py_ssize_t i=0; i < shared->len; i++) { ++ Py_ssize_t i; ++ for (i=0; i < shared->len; i++) { + _sharednsitem_clear(&shared->items[i]); + } + PyMem_Free(shared->items); +@@ -123,20 +129,25 @@ _sharedns_free(_sharedns *shared) + static _sharedns * + _get_shared_ns(PyObject *shareable) + { ++ Py_ssize_t len; ++ _sharedns *shared; ++ Py_ssize_t pos; ++ Py_ssize_t i; ++ + if (shareable == NULL || shareable == Py_None) { + return NULL; + } +- Py_ssize_t len = PyDict_Size(shareable); ++ len = PyDict_Size(shareable); + if (len == 0) { + return NULL; + } + +- _sharedns *shared = _sharedns_new(len); ++ shared = _sharedns_new(len); + if (shared == NULL) { + return NULL; + } +- Py_ssize_t pos = 0; +- for (Py_ssize_t i=0; i < len; i++) { ++ pos = 0; ++ for (i=0; i < len; i++) { + PyObject *key, *value; + if (PyDict_Next(shareable, &pos, &key, &value) == 0) { + break; +@@ -155,7 +166,8 @@ _get_shared_ns(PyObject *shareable) + static int + _sharedns_apply(_sharedns *shared, PyObject *ns) + { +- for (Py_ssize_t i=0; i < shared->len; i++) { ++ Py_ssize_t i; ++ for (i=0; i < shared->len; i++) { + if (_sharednsitem_apply(&shared->items[i], ns) != 0) { + return -1; + } +@@ -207,15 +219,18 @@ _sharedexception_free(_sharedexception *exc) + static _sharedexception * + _sharedexception_bind(PyObject *exctype, PyObject *exc, PyObject *tb) + { ++ char *failure; ++ _sharedexception *err; ++ PyObject *name; + assert(exctype != NULL); +- char *failure = NULL; ++ failure = NULL; + +- _sharedexception *err = _sharedexception_new(); ++ err = _sharedexception_new(); + if (err == NULL) { + goto finally; + } + +- PyObject *name = PyUnicode_FromFormat("%S", exctype); ++ name = PyUnicode_FromFormat("%S", exctype); + if (name == NULL) { + failure = "unable to format exception type name"; + goto finally; +@@ -818,9 +833,10 @@ _channel_next(_PyChannelState *chan, int64_t interp) + static int + _channel_close_interpreter(_PyChannelState *chan, int64_t interp, int end) + { ++ int res; + PyThread_acquire_lock(chan->mutex, WAIT_LOCK); + +- int res = -1; ++ res = -1; + if (!chan->open) { + PyErr_SetString(ChannelClosedError, "channel already closed"); + goto done; +@@ -969,13 +985,15 @@ _channels_next_id(_channels *channels) // needs lock + static _PyChannelState * + _channels_lookup(_channels *channels, int64_t id, PyThread_type_lock *pmutex) + { +- _PyChannelState *chan = NULL; ++ _PyChannelState *chan; ++ _channelref *ref; ++ chan = NULL; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + if (pmutex != NULL) { + *pmutex = NULL; + } + +- _channelref *ref = _channelref_find(channels->head, id, NULL); ++ ref = _channelref_find(channels->head, id, NULL); + if (ref == NULL) { + PyErr_Format(ChannelNotFoundError, "channel %" PRId64 " not found", id); + goto done; +@@ -1001,15 +1019,18 @@ _channels_lookup(_channels *channels, int64_t id, PyThread_type_lock *pmutex) + static int64_t + _channels_add(_channels *channels, _PyChannelState *chan) + { +- int64_t cid = -1; ++ int64_t cid; ++ int64_t id; ++ _channelref *ref; ++ cid = -1; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + + // Create a new ref. +- int64_t id = _channels_next_id(channels); ++ id = _channels_next_id(channels); + if (id < 0) { + goto done; + } +- _channelref *ref = _channelref_new(id, chan); ++ ref = _channelref_new(id, chan); + if (ref == NULL) { + goto done; + } +@@ -1034,12 +1055,13 @@ _channels_close(_channels *channels, int64_t cid, _PyChannelState **pchan, + int end, int force) + { + int res = -1; ++ _channelref *ref; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + if (pchan != NULL) { + *pchan = NULL; + } + +- _channelref *ref = _channelref_find(channels->head, cid, NULL); ++ ref = _channelref_find(channels->head, cid, NULL); + if (ref == NULL) { + PyErr_Format(ChannelNotFoundError, "channel %" PRId64 " not found", cid); + goto done; +@@ -1112,14 +1134,16 @@ static int + _channels_remove(_channels *channels, int64_t id, _PyChannelState **pchan) + { + int res = -1; ++ _channelref *prev; ++ _channelref *ref; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + + if (pchan != NULL) { + *pchan = NULL; + } + +- _channelref *prev = NULL; +- _channelref *ref = _channelref_find(channels->head, id, &prev); ++ prev = NULL; ++ ref = _channelref_find(channels->head, id, &prev); + if (ref == NULL) { + PyErr_Format(ChannelNotFoundError, "channel %" PRId64 " not found", id); + goto done; +@@ -1137,9 +1161,10 @@ static int + _channels_add_id_object(_channels *channels, int64_t id) + { + int res = -1; ++ _channelref *ref; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + +- _channelref *ref = _channelref_find(channels->head, id, NULL); ++ ref = _channelref_find(channels->head, id, NULL); + if (ref == NULL) { + PyErr_Format(ChannelNotFoundError, "channel %" PRId64 " not found", id); + goto done; +@@ -1155,10 +1180,12 @@ _channels_add_id_object(_channels *channels, int64_t id) + static void + _channels_drop_id_object(_channels *channels, int64_t id) + { ++ _channelref *prev; ++ _channelref *ref; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + +- _channelref *prev = NULL; +- _channelref *ref = _channelref_find(channels->head, id, &prev); ++ prev = NULL; ++ ref = _channelref_find(channels->head, id, &prev); + if (ref == NULL) { + // Already destroyed. + goto done; +@@ -1182,13 +1209,16 @@ static int64_t * + _channels_list_all(_channels *channels, int64_t *count) + { + int64_t *cids = NULL; ++ int64_t *ids; ++ _channelref *ref; ++ int64_t i; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); +- int64_t *ids = PyMem_NEW(int64_t, (Py_ssize_t)(channels->numopen)); ++ ids = PyMem_NEW(int64_t, (Py_ssize_t)(channels->numopen)); + if (ids == NULL) { + goto done; + } +- _channelref *ref = channels->head; +- for (int64_t i=0; ref != NULL; ref = ref->next, i++) { ++ ref = channels->head; ++ for (i=0; ref != NULL; ref = ref->next, i++) { + ids[i] = ref->id; + } + *count = channels->numopen; +@@ -1207,12 +1237,14 @@ struct _channel_closing { + + static int + _channel_set_closing(struct _channelref *ref, PyThread_type_lock mutex) { +- struct _channel *chan = ref->chan; ++ struct _channel *chan; ++ int res; ++ chan = ref->chan; + if (chan == NULL) { + // already closed + return 0; + } +- int res = -1; ++ res = -1; + PyThread_acquire_lock(chan->mutex, WAIT_LOCK); + if (chan->closing != NULL) { + PyErr_SetString(ChannelClosedError, "channel closed"); +@@ -1242,11 +1274,13 @@ _channel_clear_closing(struct _channel *chan) { + + static void + _channel_finish_closing(struct _channel *chan) { +- struct _channel_closing *closing = chan->closing; ++ struct _channel_closing *closing; ++ _channelref *ref; ++ closing = chan->closing; + if (closing == NULL) { + return; + } +- _channelref *ref = closing->ref; ++ ref = closing->ref; + _channel_clear_closing(chan); + // Do the things that would have been done in _channels_close(). + ref->chan = NULL; +@@ -1258,11 +1292,13 @@ _channel_finish_closing(struct _channel *chan) { + static int64_t + _channel_create(_channels *channels) + { +- _PyChannelState *chan = _channel_new(); ++ _PyChannelState *chan; ++ int64_t id; ++ chan = _channel_new(); + if (chan == NULL) { + return -1; + } +- int64_t id = _channels_add(channels, chan); ++ id = _channels_add(channels, chan); + if (id < 0) { + _channel_free(chan); + return -1; +@@ -1286,14 +1322,19 @@ _channel_destroy(_channels *channels, int64_t id) + static int + _channel_send(_channels *channels, int64_t id, PyObject *obj) + { +- PyInterpreterState *interp = _get_current(); ++ PyInterpreterState *interp; ++ PyThread_type_lock mutex; ++ _PyChannelState *chan; ++ _PyCrossInterpreterData *data; ++ int res; ++ interp = _get_current(); + if (interp == NULL) { + return -1; + } + + // Look up the channel. +- PyThread_type_lock mutex = NULL; +- _PyChannelState *chan = _channels_lookup(channels, id, &mutex); ++ mutex = NULL; ++ chan = _channels_lookup(channels, id, &mutex); + if (chan == NULL) { + return -1; + } +@@ -1306,7 +1347,7 @@ _channel_send(_channels *channels, int64_t id, PyObject *obj) + } + + // Convert the object to cross-interpreter data. +- _PyCrossInterpreterData *data = PyMem_NEW(_PyCrossInterpreterData, 1); ++ data = PyMem_NEW(_PyCrossInterpreterData, 1); + if (data == NULL) { + PyThread_release_lock(mutex); + return -1; +@@ -1318,7 +1359,7 @@ _channel_send(_channels *channels, int64_t id, PyObject *obj) + } + + // Add the data to the channel. +- int res = _channel_add(chan, PyInterpreterState_GetID(interp), data); ++ res = _channel_add(chan, PyInterpreterState_GetID(interp), data); + PyThread_release_lock(mutex); + if (res != 0) { + _PyCrossInterpreterData_Release(data); +@@ -1332,28 +1373,33 @@ _channel_send(_channels *channels, int64_t id, PyObject *obj) + static PyObject * + _channel_recv(_channels *channels, int64_t id) + { +- PyInterpreterState *interp = _get_current(); ++ PyInterpreterState *interp; ++ PyThread_type_lock mutex; ++ _PyChannelState *chan; ++ _PyCrossInterpreterData *data; ++ PyObject *obj; ++ interp = _get_current(); + if (interp == NULL) { + return NULL; + } + + // Look up the channel. +- PyThread_type_lock mutex = NULL; +- _PyChannelState *chan = _channels_lookup(channels, id, &mutex); ++ mutex = NULL; ++ chan = _channels_lookup(channels, id, &mutex); + if (chan == NULL) { + return NULL; + } + // Past this point we are responsible for releasing the mutex. + + // Pop off the next item from the channel. +- _PyCrossInterpreterData *data = _channel_next(chan, PyInterpreterState_GetID(interp)); ++ data = _channel_next(chan, PyInterpreterState_GetID(interp)); + PyThread_release_lock(mutex); + if (data == NULL) { + return NULL; + } + + // Convert the data back to an object. +- PyObject *obj = _PyCrossInterpreterData_NewObject(data); ++ obj = _PyCrossInterpreterData_NewObject(data); + _PyCrossInterpreterData_Release(data); + PyMem_Free(data); + if (obj == NULL) { +@@ -1366,21 +1412,25 @@ _channel_recv(_channels *channels, int64_t id) + static int + _channel_drop(_channels *channels, int64_t id, int send, int recv) + { +- PyInterpreterState *interp = _get_current(); ++ PyInterpreterState *interp; ++ PyThread_type_lock mutex; ++ _PyChannelState *chan; ++ int res; ++ interp = _get_current(); + if (interp == NULL) { + return -1; + } + + // Look up the channel. +- PyThread_type_lock mutex = NULL; +- _PyChannelState *chan = _channels_lookup(channels, id, &mutex); ++ mutex = NULL; ++ chan = _channels_lookup(channels, id, &mutex); + if (chan == NULL) { + return -1; + } + // Past this point we are responsible for releasing the mutex. + + // Close one or both of the two ends. +- int res = _channel_close_interpreter(chan, PyInterpreterState_GetID(interp), send-recv); ++ res = _channel_close_interpreter(chan, PyInterpreterState_GetID(interp), send-recv); + PyThread_release_lock(mutex); + return res; + } +@@ -1395,7 +1445,9 @@ static int + _channel_is_associated(_channels *channels, int64_t cid, int64_t interp, + int send) + { +- _PyChannelState *chan = _channels_lookup(channels, cid, NULL); ++ _PyChannelState *chan; ++ _channelend *end; ++ chan = _channels_lookup(channels, cid, NULL); + if (chan == NULL) { + return -1; + } else if (send && chan->closing != NULL) { +@@ -1403,8 +1455,8 @@ _channel_is_associated(_channels *channels, int64_t cid, int64_t interp, + return -1; + } + +- _channelend *end = _channelend_find(send ? chan->ends->send : chan->ends->recv, +- interp, NULL); ++ end = _channelend_find(send ? chan->ends->send : chan->ends->recv, ++ interp, NULL); + + return (end != NULL && end->open); + } +@@ -1486,6 +1538,7 @@ channelid_new(PyTypeObject *cls, PyObject *args, PyObject *kwds) + int recv = -1; + int force = 0; + int resolve = 0; ++ int end; + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "O&|$pppp:ChannelID.__new__", kwlist, + channel_id_converter, &cid, &send, &recv, &force, &resolve)) +@@ -1498,7 +1551,7 @@ channelid_new(PyTypeObject *cls, PyObject *args, PyObject *kwds) + return NULL; + } + +- int end = 0; ++ end = 0; + if (send == 1) { + if (recv == 0 || recv == -1) { + end = CHANNEL_SEND; +@@ -1601,10 +1654,11 @@ channelid_hash(PyObject *self) + { + channelid *cid = (channelid *)self; + PyObject *id = PyLong_FromLongLong(cid->id); ++ Py_hash_t hash; + if (id == NULL) { + return -1; + } +- Py_hash_t hash = PyObject_Hash(id); ++ hash = PyObject_Hash(id); + Py_DECREF(id); + return hash; + } +@@ -1612,6 +1666,8 @@ channelid_hash(PyObject *self) + static PyObject * + channelid_richcompare(PyObject *self, PyObject *other, int op) + { ++ channelid *cid; ++ int equal; + if (op != Py_EQ && op != Py_NE) { + Py_RETURN_NOTIMPLEMENTED; + } +@@ -1620,27 +1676,29 @@ channelid_richcompare(PyObject *self, PyObject *other, int op) + Py_RETURN_NOTIMPLEMENTED; + } + +- channelid *cid = (channelid *)self; +- int equal; ++ cid = (channelid *)self; + if (PyObject_TypeCheck(other, &ChannelIDtype)) { + channelid *othercid = (channelid *)other; + equal = (cid->end == othercid->end) && (cid->id == othercid->id); + } + else if (PyLong_Check(other)) { +- /* Fast path */ + int overflow; +- long long othercid = PyLong_AsLongLongAndOverflow(other, &overflow); ++ PY_LONG_LONG othercid; ++ /* Fast path */ ++ othercid = PyLong_AsLongLongAndOverflow(other, &overflow); + if (othercid == -1 && PyErr_Occurred()) { + return NULL; + } + equal = !overflow && (othercid >= 0) && (cid->id == othercid); + } + else if (PyNumber_Check(other)) { +- PyObject *pyid = PyLong_FromLongLong(cid->id); ++ PyObject *pyid; ++ PyObject *res; ++ pyid = PyLong_FromLongLong(cid->id); + if (pyid == NULL) { + return NULL; + } +- PyObject *res = PyObject_RichCompare(pyid, other, op); ++ res = PyObject_RichCompare(pyid, other, op); + Py_DECREF(pyid); + return res; + } +@@ -1658,6 +1716,9 @@ static PyObject * + _channel_from_cid(PyObject *cid, int end) + { + PyObject *highlevel = PyImport_ImportModule("interpreters"); ++ const char *clsname; ++ PyObject *cls; ++ PyObject *chan; + if (highlevel == NULL) { + PyErr_Clear(); + highlevel = PyImport_ImportModule("test.support.interpreters"); +@@ -1665,14 +1726,14 @@ _channel_from_cid(PyObject *cid, int end) + return NULL; + } + } +- const char *clsname = (end == CHANNEL_RECV) ? "RecvChannel" : +- "SendChannel"; +- PyObject *cls = PyObject_GetAttrString(highlevel, clsname); ++ clsname = (end == CHANNEL_RECV) ? "RecvChannel" : ++ "SendChannel"; ++ cls = PyObject_GetAttrString(highlevel, clsname); + Py_DECREF(highlevel); + if (cls == NULL) { + return NULL; + } +- PyObject *chan = PyObject_CallFunctionObjArgs(cls, cid, NULL); ++ chan = PyObject_CallFunctionObjArgs(cls, cid, NULL); + Py_DECREF(cls); + if (chan == NULL) { + return NULL; +@@ -1690,9 +1751,11 @@ static PyObject * + _channelid_from_xid(_PyCrossInterpreterData *data) + { + struct _channelid_xid *xid = (struct _channelid_xid *)data->data; ++ PyObject *cid; ++ PyObject *chan; + // Note that we do not preserve the "resolve" flag. +- PyObject *cid = (PyObject *)newchannelid(&ChannelIDtype, xid->id, xid->end, +- _global_channels(), 0, 0); ++ cid = (PyObject *)newchannelid(&ChannelIDtype, xid->id, xid->end, ++ _global_channels(), 0, 0); + if (xid->end == 0) { + return cid; + } +@@ -1701,7 +1764,7 @@ _channelid_from_xid(_PyCrossInterpreterData *data) + } + + /* Try returning a high-level channel end but fall back to the ID. */ +- PyObject *chan = _channel_from_cid(cid, xid->end); ++ chan = _channel_from_cid(cid, xid->end); + if (chan == NULL) { + PyErr_Clear(); + return cid; +@@ -1831,6 +1894,7 @@ static int + _is_running(PyInterpreterState *interp) + { + PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); ++ _PyInterpreterFrame *frame; + if (PyThreadState_Next(tstate) != NULL) { + PyErr_SetString(PyExc_RuntimeError, + "interpreter has more than one thread"); +@@ -1838,7 +1902,7 @@ _is_running(PyInterpreterState *interp) + } + + assert(!PyErr_Occurred()); +- _PyInterpreterFrame *frame = tstate->cframe->current_frame; ++ frame = tstate->cframe->current_frame; + if (frame == NULL) { + return 0; + } +@@ -1866,12 +1930,15 @@ _run_script(PyInterpreterState *interp, const char *codestr, + PyObject *exctype = NULL; + PyObject *excval = NULL; + PyObject *tb = NULL; ++ PyObject *ns; ++ PyObject *result; ++ _sharedexception *sharedexc; + + PyObject *main_mod = _PyInterpreterState_GetMainModule(interp); + if (main_mod == NULL) { + goto error; + } +- PyObject *ns = PyModule_GetDict(main_mod); // borrowed ++ ns = PyModule_GetDict(main_mod); // borrowed + Py_DECREF(main_mod); + if (ns == NULL) { + goto error; +@@ -1887,7 +1954,7 @@ _run_script(PyInterpreterState *interp, const char *codestr, + } + + // Run the string (see PyRun_SimpleStringFlags). +- PyObject *result = PyRun_StringFlags(codestr, Py_file_input, ns, ns, NULL); ++ result = PyRun_StringFlags(codestr, Py_file_input, ns, ns, NULL); + Py_DECREF(ns); + if (result == NULL) { + goto error; +@@ -1902,7 +1969,7 @@ _run_script(PyInterpreterState *interp, const char *codestr, + error: + PyErr_Fetch(&exctype, &excval, &tb); + +- _sharedexception *sharedexc = _sharedexception_bind(exctype, excval, tb); ++ sharedexc = _sharedexception_bind(exctype, excval, tb); + Py_XDECREF(exctype); + Py_XDECREF(excval); + Py_XDECREF(tb); +@@ -1922,27 +1989,32 @@ static int + _run_script_in_interpreter(PyInterpreterState *interp, const char *codestr, + PyObject *shareables) + { ++ _sharedns *shared; ++ PyThreadState *save_tstate; ++ PyThreadState *tstate; ++ _sharedexception *exc; ++ int result; + if (_ensure_not_running(interp) < 0) { + return -1; + } + +- _sharedns *shared = _get_shared_ns(shareables); ++ shared = _get_shared_ns(shareables); + if (shared == NULL && PyErr_Occurred()) { + return -1; + } + + // Switch to interpreter. +- PyThreadState *save_tstate = NULL; ++ save_tstate = NULL; + if (interp != PyInterpreterState_Get()) { + // XXX Using the "head" thread isn't strictly correct. +- PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); ++ tstate = PyInterpreterState_ThreadHead(interp); + // XXX Possible GILState issues? + save_tstate = PyThreadState_Swap(tstate); + } + + // Run the script. +- _sharedexception *exc = NULL; +- int result = _run_script(interp, codestr, shared, &exc); ++ exc = NULL; ++ result = _run_script(interp, codestr, shared, &exc); + + // Switch back. + if (save_tstate != NULL) { +@@ -1996,15 +2068,19 @@ interp_create(PyObject *self, PyObject *args, PyObject *kwds) + + static char *kwlist[] = {"isolated", NULL}; + int isolated = 1; ++ PyThreadState *save_tstate; ++ PyThreadState *tstate; ++ PyInterpreterState *interp; ++ PyObject *idobj; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|$i:create", kwlist, + &isolated)) { + return NULL; + } + + // Create and initialize the new interpreter. +- PyThreadState *save_tstate = _PyThreadState_GET(); ++ save_tstate = _PyThreadState_GET(); + // XXX Possible GILState issues? +- PyThreadState *tstate = _Py_NewInterpreter(isolated); ++ tstate = _Py_NewInterpreter(isolated); + PyThreadState_Swap(save_tstate); + if (tstate == NULL) { + /* Since no new thread state was created, there is no exception to +@@ -2013,8 +2089,8 @@ interp_create(PyObject *self, PyObject *args, PyObject *kwds) + PyErr_SetString(PyExc_RuntimeError, "interpreter creation failed"); + return NULL; + } +- PyInterpreterState *interp = PyThreadState_GetInterpreter(tstate); +- PyObject *idobj = _PyInterpreterState_GetIDObject(interp); ++ interp = PyThreadState_GetInterpreter(tstate); ++ idobj = _PyInterpreterState_GetIDObject(interp); + if (idobj == NULL) { + // XXX Possible GILState issues? + save_tstate = PyThreadState_Swap(tstate); +@@ -2037,6 +2113,10 @@ interp_destroy(PyObject *self, PyObject *args, PyObject *kwds) + { + static char *kwlist[] = {"id", NULL}; + PyObject *id; ++ PyInterpreterState *interp; ++ PyInterpreterState *current; ++ PyThreadState *tstate; ++ PyThreadState *save_tstate; + // XXX Use "L" for id? + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "O:destroy", kwlist, &id)) { +@@ -2044,13 +2124,13 @@ interp_destroy(PyObject *self, PyObject *args, PyObject *kwds) + } + + // Look up the interpreter. +- PyInterpreterState *interp = _PyInterpreterID_LookUp(id); ++ interp = _PyInterpreterID_LookUp(id); + if (interp == NULL) { + return NULL; + } + + // Ensure we don't try to destroy the current interpreter. +- PyInterpreterState *current = _get_current(); ++ current = _get_current(); + if (current == NULL) { + return NULL; + } +@@ -2068,9 +2148,9 @@ interp_destroy(PyObject *self, PyObject *args, PyObject *kwds) + } + + // Destroy the interpreter. +- PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); ++ tstate = PyInterpreterState_ThreadHead(interp); + // XXX Possible GILState issues? +- PyThreadState *save_tstate = PyThreadState_Swap(tstate); ++ save_tstate = PyThreadState_Swap(tstate); + Py_EndInterpreter(tstate); + PyThreadState_Swap(save_tstate); + +@@ -2091,6 +2171,7 @@ interp_list_all(PyObject *self, PyObject *Py_UNUSED(ignored)) + { + PyObject *ids, *id; + PyInterpreterState *interp; ++ int res; + + ids = PyList_New(0); + if (ids == NULL) { +@@ -2105,7 +2186,7 @@ interp_list_all(PyObject *self, PyObject *Py_UNUSED(ignored)) + return NULL; + } + // insert at front of list +- int res = PyList_Insert(ids, 0, id); ++ res = PyList_Insert(ids, 0, id); + Py_DECREF(id); + if (res < 0) { + Py_DECREF(ids); +@@ -2160,6 +2241,9 @@ interp_run_string(PyObject *self, PyObject *args, PyObject *kwds) + static char *kwlist[] = {"id", "script", "shared", NULL}; + PyObject *id, *code; + PyObject *shared = NULL; ++ PyInterpreterState *interp; ++ Py_ssize_t size; ++ const char *codestr; + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "OU|O:run_string", kwlist, + &id, &code, &shared)) { +@@ -2167,14 +2251,13 @@ interp_run_string(PyObject *self, PyObject *args, PyObject *kwds) + } + + // Look up the interpreter. +- PyInterpreterState *interp = _PyInterpreterID_LookUp(id); ++ interp = _PyInterpreterID_LookUp(id); + if (interp == NULL) { + return NULL; + } + + // Extract code. +- Py_ssize_t size; +- const char *codestr = PyUnicode_AsUTF8AndSize(code, &size); ++ codestr = PyUnicode_AsUTF8AndSize(code, &size); + if (codestr == NULL) { + return NULL; + } +@@ -2228,16 +2311,18 @@ interp_is_running(PyObject *self, PyObject *args, PyObject *kwds) + { + static char *kwlist[] = {"id", NULL}; + PyObject *id; ++ PyInterpreterState *interp; ++ int is_running; + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "O:is_running", kwlist, &id)) { + return NULL; + } + +- PyInterpreterState *interp = _PyInterpreterID_LookUp(id); ++ interp = _PyInterpreterID_LookUp(id); + if (interp == NULL) { + return NULL; + } +- int is_running = _is_running(interp); ++ is_running = _is_running(interp); + if (is_running < 0) { + return NULL; + } +@@ -2256,11 +2341,12 @@ static PyObject * + channel_create(PyObject *self, PyObject *Py_UNUSED(ignored)) + { + int64_t cid = _channel_create(&_globals.channels); ++ PyObject *id; + if (cid < 0) { + return NULL; + } +- PyObject *id = (PyObject *)newchannelid(&ChannelIDtype, cid, 0, +- &_globals.channels, 0, 0); ++ id = (PyObject *)newchannelid(&ChannelIDtype, cid, 0, ++ &_globals.channels, 0, 0); + if (id == NULL) { + if (_channel_destroy(&_globals.channels, cid) != 0) { + // XXX issue a warning? +@@ -2303,18 +2389,21 @@ channel_list_all(PyObject *self, PyObject *Py_UNUSED(ignored)) + { + int64_t count = 0; + int64_t *cids = _channels_list_all(&_globals.channels, &count); ++ PyObject *ids; ++ int64_t *cur; ++ int64_t i; + if (cids == NULL) { + if (count == 0) { + return PyList_New(0); + } + return NULL; + } +- PyObject *ids = PyList_New((Py_ssize_t)count); ++ ids = PyList_New((Py_ssize_t)count); + if (ids == NULL) { + goto finally; + } +- int64_t *cur = cids; +- for (int64_t i=0; i < count; cur++, i++) { ++ cur = cids; ++ for (i=0; i < count; cur++, i++) { + PyObject *id = (PyObject *)newchannelid(&ChannelIDtype, *cur, 0, + &_globals.channels, 0, 0); + if (id == NULL) { +@@ -2344,6 +2433,7 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) + int64_t id; + PyObject *ids, *id_obj; + PyInterpreterState *interp; ++ int res; + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "O&$p:channel_list_interpreters", +@@ -2360,7 +2450,7 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) + while (interp != NULL) { + id = PyInterpreterState_GetID(interp); + assert(id >= 0); +- int res = _channel_is_associated(&_globals.channels, cid, id, send); ++ res = _channel_is_associated(&_globals.channels, cid, id, send); + if (res < 0) { + goto except; + } +@@ -2425,13 +2515,14 @@ channel_recv(PyObject *self, PyObject *args, PyObject *kwds) + static char *kwlist[] = {"cid", "default", NULL}; + int64_t cid; + PyObject *dflt = NULL; ++ PyObject *obj; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O:channel_recv", kwlist, + channel_id_converter, &cid, &dflt)) { + return NULL; + } + Py_XINCREF(dflt); + +- PyObject *obj = _channel_recv(&_globals.channels, cid); ++ obj = _channel_recv(&_globals.channels, cid); + if (obj != NULL) { + Py_XDECREF(dflt); + return obj; +@@ -2606,6 +2697,8 @@ static struct PyModuleDef interpretersmodule = { + PyMODINIT_FUNC + PyInit__xxsubinterpreters(void) + { ++ PyObject *module; ++ PyObject *ns; + if (_init_globals() != 0) { + return NULL; + } +@@ -2616,13 +2709,13 @@ PyInit__xxsubinterpreters(void) + } + + /* Create the module */ +- PyObject *module = PyModule_Create(&interpretersmodule); ++ module = PyModule_Create(&interpretersmodule); + if (module == NULL) { + return NULL; + } + + /* Add exception types */ +- PyObject *ns = PyModule_GetDict(module); // borrowed ++ ns = PyModule_GetDict(module); // borrowed + if (interp_exceptions_init(ns) != 0) { + return NULL; + } diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 7699b19..f05985a 100644 --- a/Modules/arraymodule.c @@ -4537,6 +7621,319 @@ index de3c2d8..d1dfc0d 100644 /* Check for pad sequences and ignore ** the invalid ones. +diff --git a/Modules/cjkcodecs/cjkcodecs.h b/Modules/cjkcodecs/cjkcodecs.h +index ba8fad2..ee348b7 100644 +--- a/Modules/cjkcodecs/cjkcodecs.h ++++ b/Modules/cjkcodecs/cjkcodecs.h +@@ -245,12 +245,14 @@ static const struct dbcs_map *mapping_list; + static PyObject * + getmultibytecodec(void) + { +- PyObject *mod = PyImport_ImportModuleNoBlock("_multibytecodec"); ++ PyObject *mod; ++ PyObject *cofunc; ++ mod = PyImport_ImportModuleNoBlock("_multibytecodec"); + if (mod == NULL) { + return NULL; + } + +- PyObject *cofunc = PyObject_GetAttrString(mod, "__create_codec"); ++ cofunc = PyObject_GetAttrString(mod, "__create_codec"); + Py_DECREF(mod); + return cofunc; + } +@@ -301,12 +303,13 @@ static int + register_maps(PyObject *module) + { + const struct dbcs_map *h; ++ PyObject *capsule; + + for (h = mapping_list; h->charset[0] != '\0'; h++) { + char mhname[256] = "__map_"; + strcpy(mhname + sizeof("__map_") - 1, h->charset); + +- PyObject *capsule = PyCapsule_New((void *)h, ++ capsule = PyCapsule_New((void *)h, + PyMultibyteCodec_CAPSULE_NAME, NULL); + if (capsule == NULL) { + return -1; +@@ -415,10 +418,14 @@ static PyModuleDef_Slot _cjk_slots[] = { + #define I_AM_A_MODULE_FOR(loc) \ + static struct PyModuleDef _cjk_module = { \ + PyModuleDef_HEAD_INIT, \ +- .m_name = "_codecs_"#loc, \ +- .m_size = 0, \ +- .m_methods = _cjk_methods, \ +- .m_slots = _cjk_slots, \ ++ "_codecs_"#loc, \ ++ NULL, \ ++ 0, \ ++ _cjk_methods, \ ++ _cjk_slots, \ ++ NULL, \ ++ NULL, \ ++ NULL, \ + }; \ + \ + PyMODINIT_FUNC \ +diff --git a/Modules/cjkcodecs/multibytecodec.c b/Modules/cjkcodecs/multibytecodec.c +index 4769ab2..1393db7 100644 +--- a/Modules/cjkcodecs/multibytecodec.c ++++ b/Modules/cjkcodecs/multibytecodec.c +@@ -730,8 +730,9 @@ multibytecodec_traverse(PyObject *self, visitproc visit, void *arg) + static void + multibytecodec_dealloc(MultibyteCodecObject *self) + { ++ PyTypeObject *tp; + PyObject_GC_UnTrack(self); +- PyTypeObject *tp = Py_TYPE(self); ++ tp = Py_TYPE(self); + tp->tp_free(self); + Py_DECREF(tp); + } +@@ -745,11 +746,12 @@ static PyType_Slot multibytecodec_slots[] = { + }; + + static PyType_Spec multibytecodec_spec = { +- .name = MODULE_NAME ".MultibyteCodec", +- .basicsize = sizeof(MultibyteCodecObject), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | +- Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE), +- .slots = multibytecodec_slots, ++ MODULE_NAME ".MultibyteCodec", ++ sizeof(MultibyteCodecObject), ++ 0, ++ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | ++ Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE, ++ multibytecodec_slots, + }; + + +@@ -1033,6 +1035,7 @@ mbiencoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + MultibyteIncrementalEncoderObject *self; + PyObject *codec = NULL; + char *errors = NULL; ++ _multibytecodec_state *state; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s:IncrementalEncoder", + incnewkwarglist, &errors)) +@@ -1046,7 +1049,7 @@ mbiencoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + if (codec == NULL) + goto errorexit; + +- _multibytecodec_state *state = _multibyte_codec_find_state_by_type(type); ++ state = _multibyte_codec_find_state_by_type(type); + if (!MultibyteCodec_Check(state, codec)) { + PyErr_SetString(PyExc_TypeError, "codec is unexpected type"); + goto errorexit; +@@ -1108,11 +1111,12 @@ static PyType_Slot encoder_slots[] = { + }; + + static PyType_Spec encoder_spec = { +- .name = MODULE_NAME ".MultibyteIncrementalEncoder", +- .basicsize = sizeof(MultibyteIncrementalEncoderObject), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | +- Py_TPFLAGS_IMMUTABLETYPE), +- .slots = encoder_slots, ++ MODULE_NAME ".MultibyteIncrementalEncoder", ++ sizeof(MultibyteIncrementalEncoderObject), ++ 0, ++ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | ++ Py_TPFLAGS_IMMUTABLETYPE, ++ encoder_slots, + }; + + +@@ -1308,6 +1312,7 @@ mbidecoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + MultibyteIncrementalDecoderObject *self; + PyObject *codec = NULL; + char *errors = NULL; ++ _multibytecodec_state *state; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s:IncrementalDecoder", + incnewkwarglist, &errors)) +@@ -1321,7 +1326,7 @@ mbidecoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + if (codec == NULL) + goto errorexit; + +- _multibytecodec_state *state = _multibyte_codec_find_state_by_type(type); ++ state = _multibyte_codec_find_state_by_type(type); + if (!MultibyteCodec_Check(state, codec)) { + PyErr_SetString(PyExc_TypeError, "codec is unexpected type"); + goto errorexit; +@@ -1382,11 +1387,12 @@ static PyType_Slot decoder_slots[] = { + }; + + static PyType_Spec decoder_spec = { +- .name = MODULE_NAME ".MultibyteIncrementalDecoder", +- .basicsize = sizeof(MultibyteIncrementalDecoderObject), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | +- Py_TPFLAGS_IMMUTABLETYPE), +- .slots = decoder_slots, ++ MODULE_NAME ".MultibyteIncrementalDecoder", ++ sizeof(MultibyteIncrementalDecoderObject), ++ 0, ++ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | ++ Py_TPFLAGS_IMMUTABLETYPE, ++ decoder_slots, + }; + + static PyObject * +@@ -1625,6 +1631,7 @@ mbstreamreader_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + MultibyteStreamReaderObject *self; + PyObject *stream, *codec = NULL; + char *errors = NULL; ++ _multibytecodec_state *state; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s:StreamReader", + streamkwarglist, &stream, &errors)) +@@ -1638,7 +1645,7 @@ mbstreamreader_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + if (codec == NULL) + goto errorexit; + +- _multibytecodec_state *state = _multibyte_codec_find_state_by_type(type); ++ state = _multibyte_codec_find_state_by_type(type); + if (!MultibyteCodec_Check(state, codec)) { + PyErr_SetString(PyExc_TypeError, "codec is unexpected type"); + goto errorexit; +@@ -1704,11 +1711,12 @@ static PyType_Slot reader_slots[] = { + }; + + static PyType_Spec reader_spec = { +- .name = MODULE_NAME ".MultibyteStreamReader", +- .basicsize = sizeof(MultibyteStreamReaderObject), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | +- Py_TPFLAGS_IMMUTABLETYPE), +- .slots = reader_slots, ++ MODULE_NAME ".MultibyteStreamReader", ++ sizeof(MultibyteStreamReaderObject), ++ 0, ++ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | ++ Py_TPFLAGS_IMMUTABLETYPE, ++ reader_slots, + }; + + static int +@@ -1744,7 +1752,8 @@ _multibytecodec_MultibyteStreamWriter_write_impl(MultibyteStreamWriterObject *se + PyObject *strobj) + /*[clinic end generated code: output=68ade3aea26410ac input=199f26f68bd8425a]*/ + { +- _multibytecodec_state *state = PyType_GetModuleState(cls); ++ _multibytecodec_state *state; ++ state = PyType_GetModuleState(cls); + assert(state != NULL); + if (mbstreamwriter_iwrite(self, strobj, state->str_write)) { + return NULL; +@@ -1768,6 +1777,7 @@ _multibytecodec_MultibyteStreamWriter_writelines_impl(MultibyteStreamWriterObjec + { + PyObject *strobj; + int i, r; ++ _multibytecodec_state *state; + + if (!PySequence_Check(lines)) { + PyErr_SetString(PyExc_TypeError, +@@ -1775,7 +1785,7 @@ _multibytecodec_MultibyteStreamWriter_writelines_impl(MultibyteStreamWriterObjec + return NULL; + } + +- _multibytecodec_state *state = PyType_GetModuleState(cls); ++ state = PyType_GetModuleState(cls); + assert(state != NULL); + for (i = 0; i < PySequence_Length(lines); i++) { + /* length can be changed even within this loop */ +@@ -1809,6 +1819,7 @@ _multibytecodec_MultibyteStreamWriter_reset_impl(MultibyteStreamWriterObject *se + /*[clinic end generated code: output=32ef224c2a38aa3d input=28af6a9cd38d1979]*/ + { + PyObject *pwrt; ++ _multibytecodec_state *state; + + if (!self->pending) + Py_RETURN_NONE; +@@ -1826,7 +1837,7 @@ _multibytecodec_MultibyteStreamWriter_reset_impl(MultibyteStreamWriterObject *se + + assert(PyBytes_Check(pwrt)); + +- _multibytecodec_state *state = PyType_GetModuleState(cls); ++ state = PyType_GetModuleState(cls); + assert(state != NULL); + + if (PyBytes_Size(pwrt) > 0) { +@@ -1849,6 +1860,7 @@ mbstreamwriter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + MultibyteStreamWriterObject *self; + PyObject *stream, *codec = NULL; + char *errors = NULL; ++ _multibytecodec_state *state; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s:StreamWriter", + streamkwarglist, &stream, &errors)) +@@ -1862,7 +1874,7 @@ mbstreamwriter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + if (codec == NULL) + goto errorexit; + +- _multibytecodec_state *state = _multibyte_codec_find_state_by_type(type); ++ state = _multibyte_codec_find_state_by_type(type); + if (!MultibyteCodec_Check(state, codec)) { + PyErr_SetString(PyExc_TypeError, "codec is unexpected type"); + goto errorexit; +@@ -1942,11 +1954,12 @@ static PyType_Slot writer_slots[] = { + }; + + static PyType_Spec writer_spec = { +- .name = MODULE_NAME ".MultibyteStreamWriter", +- .basicsize = sizeof(MultibyteStreamWriterObject), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | +- Py_TPFLAGS_IMMUTABLETYPE), +- .slots = writer_slots, ++ MODULE_NAME ".MultibyteStreamWriter", ++ sizeof(MultibyteStreamWriterObject), ++ 0, ++ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | ++ Py_TPFLAGS_IMMUTABLETYPE, ++ writer_slots, + }; + + +@@ -1963,6 +1976,7 @@ _multibytecodec___create_codec(PyObject *module, PyObject *arg) + { + MultibyteCodecObject *self; + MultibyteCodec *codec; ++ _multibytecodec_state *state; + + if (!PyCapsule_IsValid(arg, PyMultibyteCodec_CAPSULE_NAME)) { + PyErr_SetString(PyExc_ValueError, "argument type invalid"); +@@ -1973,7 +1987,7 @@ _multibytecodec___create_codec(PyObject *module, PyObject *arg) + if (codec->codecinit != NULL && codec->codecinit(codec->config) != 0) + return NULL; + +- _multibytecodec_state *state = _multibytecodec_get_state(module); ++ state = _multibytecodec_get_state(module); + self = PyObject_GC_New(MultibyteCodecObject, state->multibytecodec_type); + if (self == NULL) + return NULL; +@@ -2064,14 +2078,15 @@ static PyModuleDef_Slot _multibytecodec_slots[] = { + }; + + static struct PyModuleDef _multibytecodecmodule = { +- .m_base = PyModuleDef_HEAD_INIT, +- .m_name = "_multibytecodec", +- .m_size = sizeof(_multibytecodec_state), +- .m_methods = _multibytecodec_methods, +- .m_slots = _multibytecodec_slots, +- .m_traverse = _multibytecodec_traverse, +- .m_clear = _multibytecodec_clear, +- .m_free = _multibytecodec_free, ++ PyModuleDef_HEAD_INIT, ++ "_multibytecodec", ++ NULL, ++ sizeof(_multibytecodec_state), ++ _multibytecodec_methods, ++ _multibytecodec_slots, ++ _multibytecodec_traverse, ++ _multibytecodec_clear, ++ _multibytecodec_free, + }; + + PyMODINIT_FUNC diff --git a/Modules/clinic/_codecsmodule.c.h b/Modules/clinic/_codecsmodule.c.h index 29e9d5e..7bc9c5c 100644 --- a/Modules/clinic/_codecsmodule.c.h @@ -4714,6 +8111,26 @@ index 5dc62fe..7f4e83f 100644 errors = PyUnicode_AsUTF8AndSize(args[3], &errors_length); if (errors == NULL) { goto exit; +diff --git a/Modules/clinic/symtablemodule.c.h b/Modules/clinic/symtablemodule.c.h +index 2cd08f8..16d05e1 100644 +--- a/Modules/clinic/symtablemodule.c.h ++++ b/Modules/clinic/symtablemodule.c.h +@@ -22,6 +22,7 @@ _symtable_symtable(PyObject *module, PyObject *const *args, Py_ssize_t nargs) + PyObject *source; + PyObject *filename; + const char *startstr; ++ Py_ssize_t startstr_length; + + if (!_PyArg_CheckPositional("symtable", nargs, 3, 3)) { + goto exit; +@@ -34,7 +35,6 @@ _symtable_symtable(PyObject *module, PyObject *const *args, Py_ssize_t nargs) + _PyArg_BadArgument("symtable", "argument 3", "str", args[2]); + goto exit; + } +- Py_ssize_t startstr_length; + startstr = PyUnicode_AsUTF8AndSize(args[2], &startstr_length); + if (startstr == NULL) { + goto exit; diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 7fffb31..51f5782 100644 --- a/Modules/cmathmodule.c @@ -6773,6 +10190,4665 @@ index 5f5b71c..999fa85 100644 }; PyMODINIT_FUNC +diff --git a/Modules/md5module.c b/Modules/md5module.c +index 48b11e0..eaae0de 100644 +--- a/Modules/md5module.c ++++ b/Modules/md5module.c +@@ -33,7 +33,7 @@ class MD5Type "MD5object *" "&PyType_Type" + + #if SIZEOF_INT == 4 + typedef unsigned int MD5_INT32; /* 32-bit integer */ +-typedef long long MD5_INT64; /* 64-bit integer */ ++typedef PY_LONG_LONG MD5_INT64; /* 64-bit integer */ + #else + /* not defined. compilation will die. */ + #endif +@@ -495,11 +495,12 @@ static PyType_Slot md5_type_slots[] = { + }; + + static PyType_Spec md5_type_spec = { +- .name = "_md5.md5", +- .basicsize = sizeof(MD5object), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | +- Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), +- .slots = md5_type_slots ++ "_md5.md5", ++ sizeof(MD5object), ++ 0, ++ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | ++ Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), ++ md5_type_slots + }; + + /* The single module-level function: new() */ +@@ -520,11 +521,12 @@ _md5_md5_impl(PyObject *module, PyObject *string, int usedforsecurity) + { + MD5object *new; + Py_buffer buf; ++ MD5State *st; + + if (string) + GET_BUFFER_VIEW_OR_ERROUT(string, &buf); + +- MD5State *st = md5_get_state(module); ++ st = md5_get_state(module); + if ((new = newMD5object(st)) == NULL) { + if (string) + PyBuffer_Release(&buf); +@@ -607,13 +609,14 @@ static PyModuleDef_Slot _md5_slots[] = { + + static struct PyModuleDef _md5module = { + PyModuleDef_HEAD_INIT, +- .m_name = "_md5", +- .m_size = sizeof(MD5State), +- .m_methods = MD5_functions, +- .m_slots = _md5_slots, +- .m_traverse = _md5_traverse, +- .m_clear = _md5_clear, +- .m_free = _md5_free, ++ "_md5", ++ NULL, ++ sizeof(MD5State), ++ MD5_functions, ++ _md5_slots, ++ _md5_traverse, ++ _md5_clear, ++ _md5_free, + }; + + PyMODINIT_FUNC +diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c +index b315bec..32d001b 100644 +--- a/Modules/mmapmodule.c ++++ b/Modules/mmapmodule.c +@@ -100,7 +100,7 @@ typedef struct { + Py_ssize_t size; + Py_ssize_t pos; /* relative to offset */ + #ifdef MS_WINDOWS +- long long offset; ++ PY_LONG_LONG offset; + #else + off_t offset; + #endif +@@ -178,6 +178,11 @@ mmap_object_dealloc(mmap_object *m_obj) + static PyObject * + mmap_close_method(mmap_object *self, PyObject *unused) + { ++#ifdef MS_WINDOWS ++ HANDLE map_handle; ++ HANDLE file_handle; ++ char *data; ++#endif + if (self->exports > 0) { + PyErr_SetString(PyExc_BufferError, "cannot close "\ + "exported pointers exist"); +@@ -191,9 +196,9 @@ mmap_close_method(mmap_object *self, PyObject *unused) + again. + TODO - should we check for errors in the close operations??? + */ +- HANDLE map_handle = self->map_handle; +- HANDLE file_handle = self->file_handle; +- char *data = self->data; ++ map_handle = self->map_handle; ++ file_handle = self->file_handle; ++ data = self->data; + self->map_handle = NULL; + self->file_handle = INVALID_HANDLE_VALUE; + self->data = NULL; +@@ -327,6 +332,7 @@ mmap_gfind(mmap_object *self, + { + Py_ssize_t start = self->pos; + Py_ssize_t end = self->size; ++ Py_ssize_t res; + Py_buffer view; + + CHECK_VALID(NULL); +@@ -349,7 +355,6 @@ mmap_gfind(mmap_object *self, + else if (end > self->size) + end = self->size; + +- Py_ssize_t res; + CHECK_VALID_OR_RELEASE(NULL, view); + if (end < start) { + res = -1; +@@ -472,7 +477,7 @@ mmap_size_method(mmap_object *self, + #ifdef MS_WINDOWS + if (self->file_handle != INVALID_HANDLE_VALUE) { + DWORD low,high; +- long long size; ++ PY_LONG_LONG size; + low = GetFileSize(self->file_handle, &high); + if (low == INVALID_FILE_SIZE) { + /* It might be that the function appears to have failed, +@@ -483,7 +488,7 @@ mmap_size_method(mmap_object *self, + } + if (!high && low < LONG_MAX) + return PyLong_FromLong((long)low); +- size = (((long long)high)<<32) + low; ++ size = (((PY_LONG_LONG)high)<<32) + low; + return PyLong_FromLongLong(size); + } else { + return PyLong_FromSsize_t(self->size); +@@ -1199,11 +1204,12 @@ static PyType_Slot mmap_object_slots[] = { + }; + + static PyType_Spec mmap_object_spec = { +- .name = "mmap.mmap", +- .basicsize = sizeof(mmap_object), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | +- Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), +- .slots = mmap_object_slots, ++ "mmap.mmap", ++ sizeof(mmap_object), ++ 0, ++ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | ++ Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), ++ mmap_object_slots, + }; + + +@@ -1394,7 +1400,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) + { + mmap_object *m_obj; + Py_ssize_t map_size; +- long long offset = 0, size; ++ PY_LONG_LONG offset = 0, size; + DWORD off_hi; /* upper 32 bits of offset */ + DWORD off_lo; /* lower 32 bits of offset */ + DWORD size_hi; /* upper 32 bits of size */ +@@ -1506,7 +1512,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) + return PyErr_SetFromWindowsErr(dwErr); + } + +- size = (((long long) high) << 32) + low; ++ size = (((PY_LONG_LONG) high) << 32) + low; + if (size == 0) { + PyErr_SetString(PyExc_ValueError, + "cannot mmap an empty file"); +@@ -1780,12 +1786,14 @@ static PyModuleDef_Slot mmap_slots[] = { + + static struct PyModuleDef mmapmodule = { + PyModuleDef_HEAD_INIT, +- .m_name = "mmap", +- .m_size = sizeof(mmap_state), +- .m_slots = mmap_slots, +- .m_traverse = mmap_traverse, +- .m_clear = mmap_clear, +- .m_free = mmap_free, ++ "mmap", ++ NULL, ++ sizeof(mmap_state), ++ NULL, ++ mmap_slots, ++ mmap_traverse, ++ mmap_clear, ++ mmap_free, + }; + + PyMODINIT_FUNC +diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c +index 6fb2fc8..3f9778c 100644 +--- a/Modules/posixmodule.c ++++ b/Modules/posixmodule.c +@@ -15,6 +15,7 @@ + // whereas pycore_condvar.h defines the WIN32_LEAN_AND_MEAN macro. + #ifdef MS_WINDOWS + # include ++# include "pycore_win9x_loadlibrary.h" // SDK fallbacks for VC6 + # include + # include // UNLEN + # include "osdefs.h" // SEP +@@ -36,7 +37,37 @@ + + #ifdef MS_WINDOWS + # include // SetEntriesInAcl +-# include // SDDL_REVISION_1 ++# if defined(_MSC_VER) && _MSC_VER < 1300 ++# ifndef SYMBOLIC_LINK_FLAG_DIRECTORY ++# define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1 ++# endif ++# endif ++# if defined(_MSC_VER) && _MSC_VER < 1300 ++# ifndef INVALID_FILE_ATTRIBUTES ++# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) ++# endif ++# endif ++# if defined(_MSC_VER) && _MSC_VER < 1300 ++# ifndef VOLUME_NAME_DOS ++# define VOLUME_NAME_DOS 0x0 ++# endif ++# endif ++# if defined(_MSC_VER) && _MSC_VER < 1300 ++# ifndef _MAX_ENV ++# define _MAX_ENV 32767 ++# endif ++# endif ++# if defined(_MSC_VER) && _MSC_VER < 1300 ++# ifndef IO_REPARSE_TAG_SYMLINK ++# define IO_REPARSE_TAG_SYMLINK 0xA000000C ++# endif ++# ifndef IO_REPARSE_TAG_MOUNT_POINT ++# define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 ++# endif ++# endif ++# if !defined(_MSC_VER) || _MSC_VER >= 1300 ++# include // SDDL_REVISION_1 ++# endif + #endif + + #include "structmember.h" // PyMemberDef +@@ -1477,7 +1508,7 @@ dir_fd_and_follow_symlinks_invalid(const char *function_name, int dir_fd, + } + + #ifdef MS_WINDOWS +- typedef long long Py_off_t; ++ typedef PY_LONG_LONG Py_off_t; + #else + typedef off_t Py_off_t; + #endif +@@ -1571,6 +1602,10 @@ _Py_Sigset_Converter(PyObject *obj, void *addr) + static int + win32_get_reparse_tag(HANDLE reparse_point_handle, ULONG *reparse_tag) + { ++#if defined(_MSC_VER) && _MSC_VER < 1300 ++ /* Win9x has no reparse points; the VC98 SDK lacks the query operation. */ ++ return FALSE; ++#else + char target_buffer[_Py_MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + _Py_REPARSE_DATA_BUFFER *rdb = (_Py_REPARSE_DATA_BUFFER *)target_buffer; + DWORD n_bytes_returned; +@@ -1588,6 +1623,7 @@ win32_get_reparse_tag(HANDLE reparse_point_handle, ULONG *reparse_tag) + *reparse_tag = rdb->ReparseTag; + + return TRUE; ++#endif + } + + #endif /* MS_WINDOWS */ +@@ -1798,6 +1834,7 @@ win32_wchdir(LPCWSTR path) + wchar_t path_buf[MAX_PATH], *new_path = path_buf; + int result; + wchar_t env[4] = L"=x:"; ++ int is_unc_like_path; + + if(!SetCurrentDirectoryW(path)) + return FALSE; +@@ -1816,7 +1853,7 @@ win32_wchdir(LPCWSTR path) + return FALSE; + } + } +- int is_unc_like_path = (wcsncmp(new_path, L"\\\\", 2) == 0 || ++ is_unc_like_path = (wcsncmp(new_path, L"\\\\", 2) == 0 || + wcsncmp(new_path, L"//", 2) == 0); + if (!is_unc_like_path) { + env[1] = new_path[0]; +@@ -1896,7 +1933,14 @@ win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result, + { + HANDLE hFile; + BY_HANDLE_FILE_INFORMATION fileInfo; ++#if defined(_MSC_VER) && _MSC_VER < 1300 ++ struct { ++ DWORD FileAttributes; ++ DWORD ReparseTag; ++ } tagInfo = { 0 }; ++#else + FILE_ATTRIBUTE_TAG_INFO tagInfo = { 0 }; ++#endif + DWORD fileType, error; + BOOL isUnhandledTag = FALSE; + int retval = 0; +@@ -1974,11 +2018,12 @@ win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result, + /* Handle types other than files on disk. */ + fileType = GetFileType(hFile); + if (fileType != FILE_TYPE_DISK) { ++ DWORD fileAttributes; + if (fileType == FILE_TYPE_UNKNOWN && GetLastError() != 0) { + retval = -1; + goto cleanup; + } +- DWORD fileAttributes = GetFileAttributesW(path); ++ fileAttributes = GetFileAttributesW(path); + memset(result, 0, sizeof(*result)); + if (fileAttributes != INVALID_FILE_ATTRIBUTES && + fileAttributes & FILE_ATTRIBUTE_DIRECTORY) { +@@ -1995,6 +2040,7 @@ win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result, + goto cleanup; + } + ++#if !defined(_MSC_VER) || _MSC_VER >= 1300 + /* Query the reparse tag, and traverse a non-link. */ + if (!traverse) { + if (!GetFileInformationByHandleEx(hFile, FileAttributeTagInfo, +@@ -2030,6 +2076,7 @@ win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result, + } + } + ++#endif + if (!GetFileInformationByHandle(hFile, &fileInfo)) { + switch (GetLastError()) { + case ERROR_INVALID_PARAMETER: +@@ -2372,15 +2419,17 @@ _posix_free(void *module) + static int + fill_time(PyObject *module, PyObject *v, int index, time_t sec, unsigned long nsec) + { +- assert(!PyErr_Occurred()); +- + int res = -1; + PyObject *s_in_ns = NULL; + PyObject *ns_total = NULL; + PyObject *float_s = NULL; ++ PyObject *s; ++ PyObject *ns_fractional; ++ ++ assert(!PyErr_Occurred()); + +- PyObject *s = _PyLong_FromTime_t(sec); +- PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec); ++ s = _PyLong_FromTime_t(sec); ++ ns_fractional = PyLong_FromUnsignedLong(nsec); + if (!(s && ns_fractional)) { + goto exit; + } +@@ -2423,10 +2472,14 @@ fill_time(PyObject *module, PyObject *v, int index, time_t sec, unsigned long ns + static PyObject* + _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st) + { ++ PyObject *StatResultType; ++ PyObject *v; ++ unsigned long ansec, mnsec, cnsec; ++ + assert(!PyErr_Occurred()); + +- PyObject *StatResultType = get_posix_state(module)->StatResultType; +- PyObject *v = PyStructSequence_New((PyTypeObject *)StatResultType); ++ StatResultType = get_posix_state(module)->StatResultType; ++ v = PyStructSequence_New((PyTypeObject *)StatResultType); + if (v == NULL) { + return NULL; + } +@@ -2441,8 +2494,8 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st) + } while (0) + + SET_ITEM(0, PyLong_FromLong((long)st->st_mode)); +- static_assert(sizeof(unsigned long long) >= sizeof(st->st_ino), +- "stat.st_ino is larger than unsigned long long"); ++ static_assert(sizeof(unsigned PY_LONG_LONG) >= sizeof(st->st_ino), ++ "stat.st_ino is larger than unsigned PY_LONG_LONG"); + SET_ITEM(1, PyLong_FromUnsignedLongLong(st->st_ino)); + #ifdef MS_WINDOWS + SET_ITEM(2, PyLong_FromUnsignedLong(st->st_dev)); +@@ -2457,12 +2510,11 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st) + SET_ITEM(4, _PyLong_FromUid(st->st_uid)); + SET_ITEM(5, _PyLong_FromGid(st->st_gid)); + #endif +- static_assert(sizeof(long long) >= sizeof(st->st_size), +- "stat.st_size is larger than long long"); ++ static_assert(sizeof(PY_LONG_LONG) >= sizeof(st->st_size), ++ "stat.st_size is larger than PY_LONG_LONG"); + SET_ITEM(6, PyLong_FromLongLong(st->st_size)); + + // Set st_atime, st_mtime and st_ctime +- unsigned long ansec, mnsec, cnsec; + #if defined(HAVE_STAT_TV_NSEC) + ansec = st->st_atim.tv_nsec; + mnsec = st->st_mtim.tv_nsec; +@@ -3813,6 +3865,7 @@ posix_getcwd(int use_bytes) + wchar_t wbuf[MAXPATHLEN]; + wchar_t *wbuf2 = wbuf; + DWORD len; ++ PyObject *resobj; + + Py_BEGIN_ALLOW_THREADS + len = GetCurrentDirectoryW(Py_ARRAY_LENGTH(wbuf), wbuf); +@@ -3843,7 +3896,7 @@ posix_getcwd(int use_bytes) + return NULL; + } + +- PyObject *resobj = PyUnicode_FromWideChar(wbuf2, len); ++ resobj = PyUnicode_FromWideChar(wbuf2, len); + if (wbuf2 != wbuf) { + PyMem_RawFree(wbuf2); + } +@@ -4362,6 +4415,7 @@ os__getfullpathname_impl(PyObject *module, path_t *path) + /*[clinic end generated code: output=bb8679d56845bc9b input=332ed537c29d0a3e]*/ + { + wchar_t *abspath; ++ PyObject *str; + + if (_PyOS_getfullpathname(path->wide, &abspath) < 0) { + return win32_error_object("GetFullPathNameW", path->object); +@@ -4370,7 +4424,7 @@ os__getfullpathname_impl(PyObject *module, path_t *path) + return PyErr_NoMemory(); + } + +- PyObject *str = PyUnicode_FromWideChar(abspath, wcslen(abspath)); ++ str = PyUnicode_FromWideChar(abspath, wcslen(abspath)); + PyMem_RawFree(abspath); + if (str == NULL) { + return NULL; +@@ -4396,6 +4450,7 @@ os__getfinalpathname_impl(PyObject *module, path_t *path) + /*[clinic end generated code: output=621a3c79bc29ebfa input=2b6b6c7cbad5fb84]*/ + { + HANDLE hFile; ++ wchar_t *tmp; + wchar_t buf[MAXPATHLEN], *target_path = buf; + int buf_size = Py_ARRAY_LENGTH(buf); + int result_length; +@@ -4435,7 +4490,6 @@ os__getfinalpathname_impl(PyObject *module, path_t *path) + break; + } + +- wchar_t *tmp; + tmp = PyMem_Realloc(target_path != buf ? target_path : NULL, + result_length * sizeof(*tmp)); + if (!tmp) { +@@ -4523,6 +4577,7 @@ os__path_splitroot_impl(PyObject *module, path_t *path) + { + wchar_t *buffer; + wchar_t *end; ++ wchar_t *p; + PyObject *result = NULL; + HRESULT ret; + +@@ -4531,7 +4586,7 @@ os__path_splitroot_impl(PyObject *module, path_t *path) + return NULL; + } + wcscpy(buffer, path->wide); +- for (wchar_t *p = wcschr(buffer, L'/'); p; p = wcschr(p, L'/')) { ++ for (p = wcschr(buffer, L'/'); p; p = wcschr(p, L'/')) { + *p = L'\\'; + } + +@@ -4570,19 +4625,23 @@ static PyObject * + os__path_normpath_impl(PyObject *module, PyObject *path) + /*[clinic end generated code: output=b94d696d828019da input=5e90c39e12549dc0]*/ + { ++ Py_ssize_t len; ++ wchar_t *buffer; ++ Py_ssize_t norm_len; ++ wchar_t *norm_path; ++ PyObject *result; ++ + if (!PyUnicode_Check(path)) { + PyErr_Format(PyExc_TypeError, "expected 'str', not '%.200s'", + Py_TYPE(path)->tp_name); + return NULL; + } +- Py_ssize_t len; +- wchar_t *buffer = PyUnicode_AsWideCharString(path, &len); ++ buffer = PyUnicode_AsWideCharString(path, &len); + if (!buffer) { + return NULL; + } +- Py_ssize_t norm_len; +- wchar_t *norm_path = _Py_normpath_and_size(buffer, len, &norm_len); +- PyObject *result = PyUnicode_FromWideChar(norm_path, norm_len); ++ norm_path = _Py_normpath_and_size(buffer, len, &norm_len); ++ result = PyUnicode_FromWideChar(norm_path, norm_len); + PyMem_Free(buffer); + return result; + } +@@ -4619,7 +4678,9 @@ os_mkdir_impl(PyObject *module, path_t *path, int mode, int dir_fd) + #ifdef MS_WINDOWS + int error = 0; + int pathError = 0; ++#if !defined(_MSC_VER) || _MSC_VER >= 1300 + SECURITY_ATTRIBUTES secAttr = { sizeof(secAttr) }; ++#endif + SECURITY_ATTRIBUTES *pSecAttr = NULL; + #endif + #ifdef HAVE_MKDIRAT +@@ -4633,6 +4694,7 @@ os_mkdir_impl(PyObject *module, path_t *path, int mode, int dir_fd) + + #ifdef MS_WINDOWS + Py_BEGIN_ALLOW_THREADS ++#if !defined(_MSC_VER) || _MSC_VER >= 1300 + if (mode == 0700 /* 0o700 */) { + ULONG sdSize; + pSecAttr = &secAttr; +@@ -4648,14 +4710,17 @@ os_mkdir_impl(PyObject *module, path_t *path, int mode, int dir_fd) + error = GetLastError(); + } + } ++#endif + if (!error) { + result = CreateDirectoryW(path->wide, pSecAttr); ++#if !defined(_MSC_VER) || _MSC_VER >= 1300 + if (secAttr.lpSecurityDescriptor && + // uncommonly, LocalFree returns non-zero on error, but still uses + // GetLastError() to see what the error code is + LocalFree(secAttr.lpSecurityDescriptor)) { + error = GetLastError(); + } ++#endif + } + Py_END_ALLOW_THREADS + +@@ -5755,6 +5820,7 @@ parse_envlist(PyObject* env, Py_ssize_t *envc_ptr) + Py_ssize_t i, pos, envc; + PyObject *keys=NULL, *vals=NULL; + PyObject *key2, *val2, *keyval; ++ PyObject *key, *val; + EXECV_CHAR **envlist; + + i = PyMapping_Size(env); +@@ -5779,11 +5845,11 @@ parse_envlist(PyObject* env, Py_ssize_t *envc_ptr) + } + + for (pos = 0; pos < i; pos++) { +- PyObject *key = PyList_GetItem(keys, pos); // Borrowed ref. ++ key = PyList_GetItem(keys, pos); // Borrowed ref. + if (key == NULL) { + goto error; + } +- PyObject *val = PyList_GetItem(vals, pos); // Borrowed ref. ++ val = PyList_GetItem(vals, pos); // Borrowed ref. + if (val == NULL) { + goto error; + } +@@ -6571,6 +6637,7 @@ os_spawnv_impl(PyObject *module, int mode, path_t *path, PyObject *argv) + int i; + Py_ssize_t argc; + intptr_t spawnval; ++ int saved_errno; + PyObject *(*getitem)(PyObject *, Py_ssize_t); + + /* spawnv has three arguments: (mode, path, argv), where +@@ -6641,7 +6708,7 @@ os_spawnv_impl(PyObject *module, int mode, path_t *path, PyObject *argv) + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + +- int saved_errno = errno; ++ saved_errno = errno; + free_string_array(argvlist, argc); + + if (spawnval == -1) { +@@ -8101,6 +8168,13 @@ static PyObject * + os_kill_impl(PyObject *module, pid_t pid, Py_ssize_t signal) + /*[clinic end generated code: output=8e346a6701c88568 input=61a36b86ca275ab9]*/ + { ++#ifdef MS_WINDOWS ++ PyObject *result; ++ DWORD sig; ++ DWORD err; ++ HANDLE handle; ++#endif ++ + if (PySys_Audit("os.kill", "in", pid, signal) < 0) { + return NULL; + } +@@ -8118,10 +8192,7 @@ os_kill_impl(PyObject *module, pid_t pid, Py_ssize_t signal) + + Py_RETURN_NONE; + #else /* !MS_WINDOWS */ +- PyObject *result; +- DWORD sig = (DWORD)signal; +- DWORD err; +- HANDLE handle; ++ sig = (DWORD)signal; + + /* Console processes which share a common console can be sent CTRL+C or + CTRL+BREAK events, provided they handle said events. */ +@@ -8667,6 +8738,7 @@ os_waitpid_impl(PyObject *module, intptr_t pid, int options) + int status; + intptr_t res; + int async_err = 0; ++ PY_LONG_LONG ustatus; + + do { + Py_BEGIN_ALLOW_THREADS +@@ -8678,7 +8750,7 @@ os_waitpid_impl(PyObject *module, intptr_t pid, int options) + if (res < 0) + return (!async_err) ? posix_error() : NULL; + +- unsigned long long ustatus = (unsigned int)status; ++ ustatus = (unsigned int)status; + + /* shift the status left a byte so this is more like the POSIX waitpid */ + return Py_BuildValue(_Py_PARSE_INTPTR "K", res, ustatus << 8); +@@ -8800,6 +8872,11 @@ os_readlink_impl(PyObject *module, path_t *path, int dir_fd) + else + return PyBytes_FromStringAndSize(buffer, length); + #elif defined(MS_WINDOWS) ++#if defined(_MSC_VER) && _MSC_VER < 1300 ++ PyErr_SetString(PyExc_NotImplementedError, ++ "os.readlink is unavailable on this Windows build"); ++ return NULL; ++#else + DWORD n_bytes_returned; + DWORD io_result = 0; + HANDLE reparse_point_handle; +@@ -8864,6 +8941,7 @@ os_readlink_impl(PyObject *module, path_t *path, int dir_fd) + } + } + return result; ++#endif /* _MSC_VER >= 1300 */ + #endif + } + #endif /* defined(HAVE_READLINK) || defined(MS_WINDOWS) */ +@@ -11134,6 +11212,11 @@ os_posix_fadvise_impl(PyObject *module, int fd, Py_off_t offset, + static PyObject* + win32_putenv(PyObject *name, PyObject *value) + { ++ PyObject *unicode; ++ Py_ssize_t size; ++ wchar_t *env; ++ int err; ++ + /* Search from index 1 because on Windows starting '=' is allowed for + defining hidden environment variables. */ + if (PyUnicode_GET_LENGTH(name) == 0 || +@@ -11142,7 +11225,6 @@ win32_putenv(PyObject *name, PyObject *value) + PyErr_SetString(PyExc_ValueError, "illegal environment variable name"); + return NULL; + } +- PyObject *unicode; + if (value != NULL) { + unicode = PyUnicode_FromFormat("%U=%U", name, value); + } +@@ -11153,8 +11235,7 @@ win32_putenv(PyObject *name, PyObject *value) + return NULL; + } + +- Py_ssize_t size; +- wchar_t *env = PyUnicode_AsWideCharString(unicode, &size); ++ env = PyUnicode_AsWideCharString(unicode, &size); + Py_DECREF(unicode); + + if (env == NULL) { +@@ -11180,7 +11261,7 @@ win32_putenv(PyObject *name, PyObject *value) + + Prefer _wputenv() to be compatible with C libraries using CRT + variables and CRT functions using these variables (ex: getenv()). */ +- int err = _wputenv(env); ++ err = _wputenv(env); + + if (err) { + posix_error(); +@@ -13437,6 +13518,8 @@ os_get_terminal_size_impl(PyObject *module, int fd) + { + int columns, lines; + PyObject *termsize; ++ PyObject *TerminalSizeType; ++ int pos; + + /* Under some conditions stdout may not be connected and + * fileno(stdout) may point to an invalid file descriptor. For example +@@ -13485,12 +13568,12 @@ os_get_terminal_size_impl(PyObject *module, int fd) + } + #endif /* TERMSIZE_USE_CONIO */ + +- PyObject *TerminalSizeType = get_posix_state(module)->TerminalSizeType; ++ TerminalSizeType = get_posix_state(module)->TerminalSizeType; + termsize = PyStructSequence_New((PyTypeObject *)TerminalSizeType); + if (termsize == NULL) + return NULL; + +- int pos = 0; ++ pos = 0; + + #define SET_TERMSIZE(CALL) \ + do { \ +@@ -13527,7 +13610,14 @@ os_cpu_count_impl(PyObject *module) + { + int ncpu = 0; + #ifdef MS_WINDOWS ++# if defined(_MSC_VER) && _MSC_VER < 1300 ++ /* Win9x has no processor groups or GetActiveProcessorCount. */ ++ SYSTEM_INFO sysinfo; ++ GetSystemInfo(&sysinfo); ++ ncpu = sysinfo.dwNumberOfProcessors; ++# else + ncpu = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); ++# endif + #elif defined(__hpux) + ncpu = mpctl(MPC_GETNUMSPUS, NULL, NULL); + #elif defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN) +@@ -13724,11 +13814,12 @@ static void + DirEntry_dealloc(DirEntry *entry) + { + PyTypeObject *tp = Py_TYPE(entry); ++ freefunc free_func; + Py_XDECREF(entry->name); + Py_XDECREF(entry->path); + Py_XDECREF(entry->stat); + Py_XDECREF(entry->lstat); +- freefunc free_func = PyType_GetSlot(tp, Py_tp_free); ++ free_func = PyType_GetSlot(tp, Py_tp_free); + free_func(entry); + Py_DECREF(tp); + } +@@ -13768,8 +13859,18 @@ static PyObject * + DirEntry_fetch_stat(PyObject *module, DirEntry *self, int follow_symlinks) + { + int result; ++ int saved_errno; + STRUCT_STAT st; + PyObject *ub; ++#ifdef MS_WINDOWS ++#if USE_UNICODE_WCHAR_CACHE ++ const wchar_t *path; ++#else /* USE_UNICODE_WCHAR_CACHE */ ++ wchar_t *path; ++#endif /* USE_UNICODE_WCHAR_CACHE */ ++#else /* POSIX */ ++ const char *path; ++#endif + + #ifdef MS_WINDOWS + if (!PyUnicode_FSDecoder(self->path, &ub)) +@@ -13777,16 +13878,16 @@ DirEntry_fetch_stat(PyObject *module, DirEntry *self, int follow_symlinks) + #if USE_UNICODE_WCHAR_CACHE + _Py_COMP_DIAG_PUSH + _Py_COMP_DIAG_IGNORE_DEPR_DECLS +- const wchar_t *path = PyUnicode_AsUnicode(ub); ++ path = PyUnicode_AsUnicode(ub); + _Py_COMP_DIAG_POP + #else /* USE_UNICODE_WCHAR_CACHE */ +- wchar_t *path = PyUnicode_AsWideCharString(ub, NULL); ++ path = PyUnicode_AsWideCharString(ub, NULL); + Py_DECREF(ub); + #endif /* USE_UNICODE_WCHAR_CACHE */ + #else /* POSIX */ + if (!PyUnicode_FSConverter(self->path, &ub)) + return NULL; +- const char *path = PyBytes_AS_STRING(ub); ++ path = PyBytes_AS_STRING(ub); + if (self->dir_fd != DEFAULT_DIR_FD) { + #ifdef HAVE_FSTATAT + if (HAVE_FSTATAT_RUNTIME) { +@@ -13815,7 +13916,7 @@ _Py_COMP_DIAG_POP + } + Py_END_ALLOW_THREADS + } +- int saved_errno = errno; ++ saved_errno = errno; + #if defined(MS_WINDOWS) && !USE_UNICODE_WCHAR_CACHE + PyMem_Free(path); + #else /* USE_UNICODE_WCHAR_CACHE */ +@@ -13892,6 +13993,7 @@ DirEntry_test_mode(PyTypeObject *defining_class, DirEntry *self, + PyObject *st_mode = NULL; + long mode; + int result; ++ _posixstate *state; + #if defined(MS_WINDOWS) || defined(HAVE_DIRENT_D_TYPE) + int is_symlink; + int need_stat; +@@ -13921,7 +14023,7 @@ DirEntry_test_mode(PyTypeObject *defining_class, DirEntry *self, + } + goto error; + } +- _posixstate* state = get_posix_state(PyType_GetModule(defining_class)); ++ state = get_posix_state(PyType_GetModule(defining_class)); + st_mode = PyObject_GetAttr(stat, state->st_mode); + if (!st_mode) + goto error; +@@ -14014,23 +14116,29 @@ os_DirEntry_inode_impl(DirEntry *self) + PyObject *unicode; + STRUCT_STAT stat; + int result; ++ int saved_errno; ++#if USE_UNICODE_WCHAR_CACHE ++ const wchar_t *path; ++#else /* USE_UNICODE_WCHAR_CACHE */ ++ wchar_t *path; ++#endif /* USE_UNICODE_WCHAR_CACHE */ + + if (!PyUnicode_FSDecoder(self->path, &unicode)) + return NULL; + #if USE_UNICODE_WCHAR_CACHE + _Py_COMP_DIAG_PUSH + _Py_COMP_DIAG_IGNORE_DEPR_DECLS +- const wchar_t *path = PyUnicode_AsUnicode(unicode); ++ path = PyUnicode_AsUnicode(unicode); + result = LSTAT(path, &stat); +- int saved_errno = errno; ++ saved_errno = errno; + Py_DECREF(unicode); + _Py_COMP_DIAG_POP + #else /* USE_UNICODE_WCHAR_CACHE */ +- wchar_t *path = PyUnicode_AsWideCharString(unicode, NULL); ++ path = PyUnicode_AsWideCharString(unicode, NULL); + Py_DECREF(unicode); + result = LSTAT(path, &stat); + +- int saved_errno = errno; ++ saved_errno = errno; + PyMem_Free(path); + #endif /* USE_UNICODE_WCHAR_CACHE */ + +@@ -14042,11 +14150,11 @@ _Py_COMP_DIAG_POP + self->win32_file_index = stat.st_ino; + self->got_file_index = 1; + } +- static_assert(sizeof(unsigned long long) >= sizeof(self->win32_file_index), ++ static_assert(sizeof(unsigned PY_LONG_LONG) >= sizeof(self->win32_file_index), + "DirEntry.win32_file_index is larger than unsigned long long"); + return PyLong_FromUnsignedLongLong(self->win32_file_index); + #else /* POSIX */ +- static_assert(sizeof(unsigned long long) >= sizeof(self->d_ino), ++ static_assert(sizeof(unsigned PY_LONG_LONG) >= sizeof(self->d_ino), + "DirEntry.d_ino is larger than unsigned long long"); + return PyLong_FromUnsignedLongLong(self->d_ino); + #endif +@@ -14510,10 +14618,11 @@ static void + ScandirIterator_dealloc(ScandirIterator *iterator) + { + PyTypeObject *tp = Py_TYPE(iterator); ++ freefunc free_func; + if (PyObject_CallFinalizerFromDealloc((PyObject *)iterator) < 0) + return; + +- freefunc free_func = PyType_GetSlot(tp, Py_tp_free); ++ free_func = PyType_GetSlot(tp, Py_tp_free); + free_func(iterator); + Py_DECREF(tp); + } +@@ -14564,6 +14673,7 @@ os_scandir_impl(PyObject *module, path_t *path) + /*[clinic end generated code: output=6eb2668b675ca89e input=6bdd312708fc3bb0]*/ + { + ScandirIterator *iterator; ++ PyObject *ScandirIteratorType; + #ifdef MS_WINDOWS + wchar_t *path_strW; + #else +@@ -14578,7 +14688,7 @@ os_scandir_impl(PyObject *module, path_t *path) + return NULL; + } + +- PyObject *ScandirIteratorType = get_posix_state(module)->ScandirIteratorType; ++ ScandirIteratorType = get_posix_state(module)->ScandirIteratorType; + iterator = PyObject_New(ScandirIterator, (PyTypeObject *)ScandirIteratorType); + if (!iterator) + return NULL; +@@ -14793,8 +14903,10 @@ os_getrandom_impl(PyObject *module, Py_ssize_t size, int flags) + * on win32 + */ + ++#if !defined(_MSC_VER) || _MSC_VER >= 1300 + typedef DLL_DIRECTORY_COOKIE (WINAPI *PAddDllDirectory)(PCWSTR newDirectory); + typedef BOOL (WINAPI *PRemoveDllDirectory)(DLL_DIRECTORY_COOKIE cookie); ++#endif + + /*[clinic input] + os._add_dll_directory +@@ -14815,6 +14927,11 @@ static PyObject * + os__add_dll_directory_impl(PyObject *module, path_t *path) + /*[clinic end generated code: output=80b025daebb5d683 input=1de3e6c13a5808c8]*/ + { ++#if defined(_MSC_VER) && _MSC_VER < 1300 ++ PyErr_SetString(PyExc_NotImplementedError, ++ "DLL directory management is unavailable on this Windows build"); ++ return NULL; ++#else + HMODULE hKernel32; + PAddDllDirectory AddDllDirectory; + DLL_DIRECTORY_COOKIE cookie = 0; +@@ -14842,6 +14959,7 @@ os__add_dll_directory_impl(PyObject *module, path_t *path) + } + + return PyCapsule_New(cookie, "DLL directory cookie", NULL); ++#endif + } + + /*[clinic input] +@@ -14860,6 +14978,11 @@ static PyObject * + os__remove_dll_directory_impl(PyObject *module, PyObject *cookie) + /*[clinic end generated code: output=594350433ae535bc input=c1d16a7e7d9dc5dc]*/ + { ++#if defined(_MSC_VER) && _MSC_VER < 1300 ++ PyErr_SetString(PyExc_NotImplementedError, ++ "DLL directory management is unavailable on this Windows build"); ++ return NULL; ++#else + HMODULE hKernel32; + PRemoveDllDirectory RemoveDllDirectory; + DLL_DIRECTORY_COOKIE cookieValue; +@@ -14896,6 +15019,7 @@ os__remove_dll_directory_impl(PyObject *module, PyObject *cookie) + } + + Py_RETURN_NONE; ++#endif + } + + #endif +@@ -14974,14 +15098,17 @@ os_waitstatus_to_exitcode_impl(PyObject *module, PyObject *status_obj) + } + return PyLong_FromLong(exitcode); + #else ++ unsigned PY_LONG_LONG status; ++ unsigned PY_LONG_LONG exitcode; ++ + /* Windows implementation: see os.waitpid() implementation + which uses _cwait(). */ +- unsigned long long status = PyLong_AsUnsignedLongLong(status_obj); +- if (status == (unsigned long long)-1 && PyErr_Occurred()) { ++ status = PyLong_AsUnsignedLongLong(status_obj); ++ if (status == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) { + return NULL; + } + +- unsigned long long exitcode = (status >> 8); ++ exitcode = (status >> 8); + /* ExitProcess() accepts an UINT type: + reject exit code which doesn't fit in an UINT */ + if (exitcode > UINT_MAX) { +@@ -15960,7 +16087,12 @@ static const struct have_function { + static int + posixmodule_exec(PyObject *m) + { +- _posixstate *state = get_posix_state(m); ++ _posixstate *state; ++ PyObject *ScandirIteratorType; ++ PyObject *list; ++ const struct have_function *trace; ++ ++ state = get_posix_state(m); + + #if defined(HAVE_PWRITEV) + if (HAVE_PWRITEV_RUNTIME) {} else { +@@ -16044,7 +16176,7 @@ posixmodule_exec(PyObject *m) + } + + /* initialize scandir types */ +- PyObject *ScandirIteratorType = PyType_FromModuleAndSpec(m, &ScandirIteratorType_spec, NULL); ++ ScandirIteratorType = PyType_FromModuleAndSpec(m, &ScandirIteratorType_spec, NULL); + if (ScandirIteratorType == NULL) { + return -1; + } +@@ -16091,11 +16223,11 @@ posixmodule_exec(PyObject *m) + * provide list of locally available functions + * so os.py can populate support_* lists + */ +- PyObject *list = PyList_New(0); ++ list = PyList_New(0); + if (!list) { + return -1; + } +- for (const struct have_function *trace = have_functions; trace->label; trace++) { ++ for (trace = have_functions; trace->label; trace++) { + PyObject *unicode; + if (trace->probe && !trace->probe()) continue; + unicode = PyUnicode_DecodeASCII(trace->label, strlen(trace->label), NULL); +@@ -16117,14 +16249,14 @@ static PyModuleDef_Slot posixmodile_slots[] = { + + static struct PyModuleDef posixmodule = { + PyModuleDef_HEAD_INIT, +- .m_name = MODNAME, +- .m_doc = posix__doc__, +- .m_size = sizeof(_posixstate), +- .m_methods = posix_methods, +- .m_slots = posixmodile_slots, +- .m_traverse = _posix_traverse, +- .m_clear = _posix_clear, +- .m_free = _posix_free, ++ MODNAME, ++ posix__doc__, ++ sizeof(_posixstate), ++ posix_methods, ++ posixmodile_slots, ++ _posix_traverse, ++ _posix_clear, ++ _posix_free, + }; + + PyMODINIT_FUNC +diff --git a/Modules/sha1module.c b/Modules/sha1module.c +index 9153557..a4c0735 100644 +--- a/Modules/sha1module.c ++++ b/Modules/sha1module.c +@@ -33,7 +33,7 @@ class SHA1Type "SHA1object *" "&PyType_Type" + + #if SIZEOF_INT == 4 + typedef unsigned int SHA1_INT32; /* 32-bit integer */ +-typedef long long SHA1_INT64; /* 64-bit integer */ ++typedef PY_LONG_LONG SHA1_INT64; /* 64-bit integer */ + #else + /* not defined. compilation will die. */ + #endif +@@ -473,11 +473,12 @@ static PyType_Slot sha1_type_slots[] = { + }; + + static PyType_Spec sha1_type_spec = { +- .name = "_sha1.sha1", +- .basicsize = sizeof(SHA1object), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | +- Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), +- .slots = sha1_type_slots ++ "_sha1.sha1", ++ sizeof(SHA1object), ++ 0, ++ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | ++ Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), ++ sha1_type_slots + }; + + /* The single module-level function: new() */ +@@ -498,11 +499,12 @@ _sha1_sha1_impl(PyObject *module, PyObject *string, int usedforsecurity) + { + SHA1object *new; + Py_buffer buf; ++ SHA1State *st; + + if (string) + GET_BUFFER_VIEW_OR_ERROUT(string, &buf); + +- SHA1State *st = sha1_get_state(module); ++ st = sha1_get_state(module); + if ((new = newSHA1object(st)) == NULL) { + if (string) + PyBuffer_Release(&buf); +@@ -588,13 +590,14 @@ static PyModuleDef_Slot _sha1_slots[] = { + + static struct PyModuleDef _sha1module = { + PyModuleDef_HEAD_INIT, +- .m_name = "_sha1", +- .m_size = sizeof(SHA1State), +- .m_methods = SHA1_functions, +- .m_slots = _sha1_slots, +- .m_traverse = _sha1_traverse, +- .m_clear = _sha1_clear, +- .m_free = _sha1_free ++ "_sha1", ++ NULL, ++ sizeof(SHA1State), ++ SHA1_functions, ++ _sha1_slots, ++ _sha1_traverse, ++ _sha1_clear, ++ _sha1_free + }; + + PyMODINIT_FUNC +diff --git a/Modules/sha256module.c b/Modules/sha256module.c +index 17ee866..4dceaae 100644 +--- a/Modules/sha256module.c ++++ b/Modules/sha256module.c +@@ -559,19 +559,21 @@ static PyType_Slot sha256_types_slots[] = { + }; + + static PyType_Spec sha224_type_spec = { +- .name = "_sha256.sha224", +- .basicsize = sizeof(SHAobject), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | +- Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), +- .slots = sha256_types_slots ++ "_sha256.sha224", ++ sizeof(SHAobject), ++ 0, ++ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | ++ Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), ++ sha256_types_slots + }; + + static PyType_Spec sha256_type_spec = { +- .name = "_sha256.sha256", +- .basicsize = sizeof(SHAobject), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | +- Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), +- .slots = sha256_types_slots ++ "_sha256.sha256", ++ sizeof(SHAobject), ++ 0, ++ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | ++ Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), ++ sha256_types_slots + }; + + /* The single module-level function: new() */ +@@ -591,14 +593,15 @@ _sha256_sha256_impl(PyObject *module, PyObject *string, int usedforsecurity) + /*[clinic end generated code: output=a1de327e8e1185cf input=9be86301aeb14ea5]*/ + { + Py_buffer buf; ++ _sha256_state *state; ++ SHAobject *new; + + if (string) { + GET_BUFFER_VIEW_OR_ERROUT(string, &buf); + } + +- _sha256_state *state = PyModule_GetState(module); ++ state = PyModule_GetState(module); + +- SHAobject *new; + if ((new = newSHA256object(state)) == NULL) { + if (string) { + PyBuffer_Release(&buf); +@@ -638,12 +641,14 @@ _sha256_sha224_impl(PyObject *module, PyObject *string, int usedforsecurity) + /*[clinic end generated code: output=08be6b36569bc69c input=9fcfb46e460860ac]*/ + { + Py_buffer buf; ++ _sha256_state *state; ++ SHAobject *new; ++ + if (string) { + GET_BUFFER_VIEW_OR_ERROUT(string, &buf); + } + +- _sha256_state *state = PyModule_GetState(module); +- SHAobject *new; ++ state = PyModule_GetState(module); + if ((new = newSHA224object(state)) == NULL) { + if (string) { + PyBuffer_Release(&buf); +@@ -739,13 +744,14 @@ static PyModuleDef_Slot _sha256_slots[] = { + + static struct PyModuleDef _sha256module = { + PyModuleDef_HEAD_INIT, +- .m_name = "_sha256", +- .m_size = sizeof(_sha256_state), +- .m_methods = SHA_functions, +- .m_slots = _sha256_slots, +- .m_traverse = _sha256_traverse, +- .m_clear = _sha256_clear, +- .m_free = _sha256_free ++ "_sha256", ++ NULL, ++ sizeof(_sha256_state), ++ SHA_functions, ++ _sha256_slots, ++ _sha256_traverse, ++ _sha256_clear, ++ _sha256_free + }; + + /* Initialize this module. */ +diff --git a/Modules/sha512module.c b/Modules/sha512module.c +index bf4408b..db47ad2 100644 +--- a/Modules/sha512module.c ++++ b/Modules/sha512module.c +@@ -105,12 +105,12 @@ static void SHAcopy(SHAobject *src, SHAobject *dest) + + /* Various logical functions */ + #define ROR64(x, y) \ +- ( ((((x) & 0xFFFFFFFFFFFFFFFFULL)>>((unsigned long long)(y) & 63)) | \ +- ((x)<<((unsigned long long)(64-((y) & 63))))) & 0xFFFFFFFFFFFFFFFFULL) ++ ( ((((x) & PY_ULL(0xFFFFFFFFFFFFFFFF))>>((unsigned PY_LONG_LONG)(y) & 63)) | \ ++ ((x)<<((unsigned PY_LONG_LONG)(64-((y) & 63))))) & PY_ULL(0xFFFFFFFFFFFFFFFF)) + #define Ch(x,y,z) (z ^ (x & (y ^ z))) + #define Maj(x,y,z) (((x | y) & z) | (x & y)) + #define S(x, n) ROR64((x),(n)) +-#define R(x, n) (((x) & 0xFFFFFFFFFFFFFFFFULL) >> ((unsigned long long)n)) ++#define R(x, n) (((x) & PY_ULL(0xFFFFFFFFFFFFFFFF)) >> ((unsigned PY_LONG_LONG)n)) + #define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) + #define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) + #define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) +@@ -142,86 +142,86 @@ sha512_transform(SHAobject *sha_info) + d += t0; \ + h = t0 + t1; + +- RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98d728ae22ULL); +- RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x7137449123ef65cdULL); +- RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcfec4d3b2fULL); +- RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba58189dbbcULL); +- RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25bf348b538ULL); +- RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1b605d019ULL); +- RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4af194f9bULL); +- RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5da6d8118ULL); +- RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98a3030242ULL); +- RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b0145706fbeULL); +- RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be4ee4b28cULL); +- RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3d5ffb4e2ULL); +- RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74f27b896fULL); +- RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe3b1696b1ULL); +- RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a725c71235ULL); +- RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174cf692694ULL); +- RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c19ef14ad2ULL); +- RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786384f25e3ULL); +- RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc68b8cd5b5ULL); +- RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc77ac9c65ULL); +- RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f592b0275ULL); +- RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa6ea6e483ULL); +- RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dcbd41fbd4ULL); +- RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da831153b5ULL); +- RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152ee66dfabULL); +- RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d2db43210ULL); +- RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c898fb213fULL); +- RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7beef0ee4ULL); +- RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf33da88fc2ULL); +- RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147930aa725ULL); +- RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351e003826fULL); +- RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x142929670a0e6e70ULL); +- RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a8546d22ffcULL); +- RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b21385c26c926ULL); +- RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc5ac42aedULL); +- RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d139d95b3dfULL); +- RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a73548baf63deULL); +- RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb3c77b2a8ULL); +- RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e47edaee6ULL); +- RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c851482353bULL); +- RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a14cf10364ULL); +- RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664bbc423001ULL); +- RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70d0f89791ULL); +- RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a30654be30ULL); +- RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819d6ef5218ULL); +- RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd69906245565a910ULL); +- RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e35855771202aULL); +- RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa07032bbd1b8ULL); +- RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116b8d2d0c8ULL); +- RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c085141ab53ULL); +- RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774cdf8eeb99ULL); +- RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5e19b48a8ULL); +- RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3c5c95a63ULL); +- RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4ae3418acbULL); +- RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f7763e373ULL); +- RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3d6b2b8a3ULL); +- RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee5defb2fcULL); +- RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f43172f60ULL); +- RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814a1f0ab72ULL); +- RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc702081a6439ecULL); +- RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa23631e28ULL); +- RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506cebde82bde9ULL); +- RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7b2c67915ULL); +- RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2e372532bULL); +- RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],64,0xca273eceea26619cULL); +- RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],65,0xd186b8c721c0c207ULL); +- RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],66,0xeada7dd6cde0eb1eULL); +- RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],67,0xf57d4f7fee6ed178ULL); +- RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],68,0x06f067aa72176fbaULL); +- RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],69,0x0a637dc5a2c898a6ULL); +- RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],70,0x113f9804bef90daeULL); +- RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],71,0x1b710b35131c471bULL); +- RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],72,0x28db77f523047d84ULL); +- RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],73,0x32caab7b40c72493ULL); +- RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],74,0x3c9ebe0a15c9bebcULL); +- RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],75,0x431d67c49c100d4cULL); +- RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],76,0x4cc5d4becb3e42b6ULL); +- RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],77,0x597f299cfc657e2aULL); +- RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],78,0x5fcb6fab3ad6faecULL); +- RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],79,0x6c44198c4a475817ULL); ++ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,PY_ULL(0x428a2f98d728ae22)); ++ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,PY_ULL(0x7137449123ef65cd)); ++ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,PY_ULL(0xb5c0fbcfec4d3b2f)); ++ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,PY_ULL(0xe9b5dba58189dbbc)); ++ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,PY_ULL(0x3956c25bf348b538)); ++ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,PY_ULL(0x59f111f1b605d019)); ++ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,PY_ULL(0x923f82a4af194f9b)); ++ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,PY_ULL(0xab1c5ed5da6d8118)); ++ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,PY_ULL(0xd807aa98a3030242)); ++ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,PY_ULL(0x12835b0145706fbe)); ++ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,PY_ULL(0x243185be4ee4b28c)); ++ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,PY_ULL(0x550c7dc3d5ffb4e2)); ++ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,PY_ULL(0x72be5d74f27b896f)); ++ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,PY_ULL(0x80deb1fe3b1696b1)); ++ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,PY_ULL(0x9bdc06a725c71235)); ++ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,PY_ULL(0xc19bf174cf692694)); ++ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,PY_ULL(0xe49b69c19ef14ad2)); ++ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,PY_ULL(0xefbe4786384f25e3)); ++ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,PY_ULL(0x0fc19dc68b8cd5b5)); ++ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,PY_ULL(0x240ca1cc77ac9c65)); ++ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,PY_ULL(0x2de92c6f592b0275)); ++ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,PY_ULL(0x4a7484aa6ea6e483)); ++ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,PY_ULL(0x5cb0a9dcbd41fbd4)); ++ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,PY_ULL(0x76f988da831153b5)); ++ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,PY_ULL(0x983e5152ee66dfab)); ++ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,PY_ULL(0xa831c66d2db43210)); ++ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,PY_ULL(0xb00327c898fb213f)); ++ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,PY_ULL(0xbf597fc7beef0ee4)); ++ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,PY_ULL(0xc6e00bf33da88fc2)); ++ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,PY_ULL(0xd5a79147930aa725)); ++ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,PY_ULL(0x06ca6351e003826f)); ++ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,PY_ULL(0x142929670a0e6e70)); ++ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,PY_ULL(0x27b70a8546d22ffc)); ++ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,PY_ULL(0x2e1b21385c26c926)); ++ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,PY_ULL(0x4d2c6dfc5ac42aed)); ++ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,PY_ULL(0x53380d139d95b3df)); ++ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,PY_ULL(0x650a73548baf63de)); ++ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,PY_ULL(0x766a0abb3c77b2a8)); ++ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,PY_ULL(0x81c2c92e47edaee6)); ++ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,PY_ULL(0x92722c851482353b)); ++ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,PY_ULL(0xa2bfe8a14cf10364)); ++ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,PY_ULL(0xa81a664bbc423001)); ++ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,PY_ULL(0xc24b8b70d0f89791)); ++ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,PY_ULL(0xc76c51a30654be30)); ++ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,PY_ULL(0xd192e819d6ef5218)); ++ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,PY_ULL(0xd69906245565a910)); ++ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,PY_ULL(0xf40e35855771202a)); ++ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,PY_ULL(0x106aa07032bbd1b8)); ++ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,PY_ULL(0x19a4c116b8d2d0c8)); ++ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,PY_ULL(0x1e376c085141ab53)); ++ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,PY_ULL(0x2748774cdf8eeb99)); ++ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,PY_ULL(0x34b0bcb5e19b48a8)); ++ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,PY_ULL(0x391c0cb3c5c95a63)); ++ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,PY_ULL(0x4ed8aa4ae3418acb)); ++ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,PY_ULL(0x5b9cca4f7763e373)); ++ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,PY_ULL(0x682e6ff3d6b2b8a3)); ++ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,PY_ULL(0x748f82ee5defb2fc)); ++ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,PY_ULL(0x78a5636f43172f60)); ++ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,PY_ULL(0x84c87814a1f0ab72)); ++ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,PY_ULL(0x8cc702081a6439ec)); ++ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,PY_ULL(0x90befffa23631e28)); ++ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,PY_ULL(0xa4506cebde82bde9)); ++ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,PY_ULL(0xbef9a3f7b2c67915)); ++ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,PY_ULL(0xc67178f2e372532b)); ++ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],64,PY_ULL(0xca273eceea26619c)); ++ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],65,PY_ULL(0xd186b8c721c0c207)); ++ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],66,PY_ULL(0xeada7dd6cde0eb1e)); ++ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],67,PY_ULL(0xf57d4f7fee6ed178)); ++ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],68,PY_ULL(0x06f067aa72176fba)); ++ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],69,PY_ULL(0x0a637dc5a2c898a6)); ++ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],70,PY_ULL(0x113f9804bef90dae)); ++ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],71,PY_ULL(0x1b710b35131c471b)); ++ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],72,PY_ULL(0x28db77f523047d84)); ++ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],73,PY_ULL(0x32caab7b40c72493)); ++ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],74,PY_ULL(0x3c9ebe0a15c9bebc)); ++ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],75,PY_ULL(0x431d67c49c100d4c)); ++ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],76,PY_ULL(0x4cc5d4becb3e42b6)); ++ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],77,PY_ULL(0x597f299cfc657e2a)); ++ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],78,PY_ULL(0x5fcb6fab3ad6faec)); ++ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],79,PY_ULL(0x6c44198c4a475817)); + + #undef RND + +@@ -615,11 +615,12 @@ static PyType_Slot sha512_sha384_type_slots[] = { + }; + + static PyType_Spec sha512_sha384_type_spec = { +- .name = "_sha512.sha384", +- .basicsize = sizeof(SHAobject), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | +- Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), +- .slots = sha512_sha384_type_slots ++ "_sha512.sha384", ++ sizeof(SHAobject), ++ 0, ++ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | ++ Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), ++ sha512_sha384_type_slots + }; + + static PyType_Slot sha512_sha512_type_slots[] = { +@@ -634,11 +635,12 @@ static PyType_Slot sha512_sha512_type_slots[] = { + // Using PyType_GetModuleState() on this type is safe since + // it cannot be subclassed: it does not have the Py_TPFLAGS_BASETYPE flag. + static PyType_Spec sha512_sha512_type_spec = { +- .name = "_sha512.sha512", +- .basicsize = sizeof(SHAobject), +- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | +- Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), +- .slots = sha512_sha512_type_slots ++ "_sha512.sha512", ++ sizeof(SHAobject), ++ 0, ++ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | ++ Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), ++ sha512_sha512_type_slots + }; + + /* The single module-level function: new() */ +@@ -803,13 +805,14 @@ static PyModuleDef_Slot _sha512_slots[] = { + + static struct PyModuleDef _sha512module = { + PyModuleDef_HEAD_INIT, +- .m_name = "_sha512", +- .m_size = sizeof(SHA512State), +- .m_methods = SHA_functions, +- .m_slots = _sha512_slots, +- .m_traverse = _sha512_traverse, +- .m_clear = _sha512_clear, +- .m_free = _sha512_free ++ "_sha512", ++ NULL, ++ sizeof(SHA512State), ++ SHA_functions, ++ _sha512_slots, ++ _sha512_traverse, ++ _sha512_clear, ++ _sha512_free + }; + + PyMODINIT_FUNC +diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c +index 8305f64..f7d9eb3 100644 +--- a/Modules/signalmodule.c ++++ b/Modules/signalmodule.c +@@ -27,6 +27,9 @@ + # ifdef HAVE_PROCESS_H + # include + # endif ++# ifndef EWOULDBLOCK ++# define EWOULDBLOCK EAGAIN ++# endif + #endif + + #ifdef HAVE_SIGNAL_H +@@ -115,7 +118,7 @@ static volatile struct { + SOCKET_T fd; + int warn_on_full_buffer; + int use_send; +-} wakeup = {.fd = INVALID_FD, .warn_on_full_buffer = 1, .use_send = 0}; ++} wakeup = {INVALID_FD, 1, 0}; + #else + #define INVALID_FD (-1) + static volatile struct { +@@ -305,6 +308,9 @@ report_wakeup_send_error(void* data) + static void + trip_signal(int sig_num) + { ++ int fd; ++ PyInterpreterState *interp; ++ + _Py_atomic_store_relaxed(&Handlers[sig_num].tripped, 1); + + /* Set is_tripped after setting .tripped, as it gets +@@ -312,7 +318,7 @@ trip_signal(int sig_num) + _Py_atomic_store(&is_tripped, 1); + + /* Signals are always handled by the main interpreter */ +- PyInterpreterState *interp = _PyInterpreterState_Main(); ++ interp = _PyInterpreterState_Main(); + + /* Notify ceval.c */ + _PyEval_SignalReceived(interp); +@@ -336,7 +342,6 @@ trip_signal(int sig_num) + See bpo-30038 for more details. + */ + +- int fd; + #ifdef MS_WINDOWS + fd = Py_SAFE_DOWNCAST(wakeup.fd, SOCKET_T, int); + #else +@@ -519,9 +524,12 @@ static PyObject * + signal_signal_impl(PyObject *module, int signalnum, PyObject *handler) + /*[clinic end generated code: output=b44cfda43780f3a1 input=deee84af5fa0432c]*/ + { +- _signal_module_state *modstate = get_signal_state(module); ++ _signal_module_state *modstate; + PyObject *old_handler; + void (*func)(int); ++ PyThreadState *tstate; ++ ++ modstate = get_signal_state(module); + #ifdef MS_WINDOWS + /* Validate that signalnum is one of the allowable signals */ + switch (signalnum) { +@@ -542,7 +550,7 @@ signal_signal_impl(PyObject *module, int signalnum, PyObject *handler) + } + #endif + +- PyThreadState *tstate = _PyThreadState_GET(); ++ tstate = _PyThreadState_GET(); + if (!_Py_ThreadCanHandleSignals(tstate->interp)) { + _PyErr_SetString(tstate, PyExc_ValueError, + "signal only works in main thread " +@@ -751,6 +759,7 @@ static PyObject* + signal_set_wakeup_fd(PyObject *self, PyObject *args, PyObject *kwds) + { + struct _Py_stat_struct status; ++ PyThreadState *tstate; + static char *kwlist[] = { + "", "warn_on_full_buffer", NULL, + }; +@@ -778,7 +787,7 @@ signal_set_wakeup_fd(PyObject *self, PyObject *args, PyObject *kwds) + return NULL; + #endif + +- PyThreadState *tstate = _PyThreadState_GET(); ++ tstate = _PyThreadState_GET(); + if (!_Py_ThreadCanHandleSignals(tstate->interp)) { + _PyErr_SetString(tstate, PyExc_ValueError, + "set_wakeup_fd only works in main thread " +@@ -874,14 +883,16 @@ The fd must be non-blocking."); + int + PySignal_SetWakeupFd(int fd) + { ++ int old_fd; ++ + if (fd < 0) { + fd = -1; + } + + #ifdef MS_WINDOWS +- int old_fd = Py_SAFE_DOWNCAST(wakeup.fd, SOCKET_T, int); ++ old_fd = Py_SAFE_DOWNCAST(wakeup.fd, SOCKET_T, int); + #else +- int old_fd = wakeup.fd; ++ old_fd = wakeup.fd; + #endif + wakeup.fd = fd; + wakeup.warn_on_full_buffer = 1; +@@ -1110,17 +1121,19 @@ signal_valid_signals_impl(PyObject *module) + /*[clinic end generated code: output=1609cffbcfcf1314 input=86a3717ff25288f2]*/ + { + #ifdef MS_WINDOWS ++ PyObject *tup; ++ PyObject *set; + #ifdef SIGBREAK +- PyObject *tup = Py_BuildValue("(iiiiiii)", SIGABRT, SIGBREAK, SIGFPE, +- SIGILL, SIGINT, SIGSEGV, SIGTERM); ++ tup = Py_BuildValue("(iiiiiii)", SIGABRT, SIGBREAK, SIGFPE, ++ SIGILL, SIGINT, SIGSEGV, SIGTERM); + #else +- PyObject *tup = Py_BuildValue("(iiiiii)", SIGABRT, SIGFPE, SIGILL, +- SIGINT, SIGSEGV, SIGTERM); ++ tup = Py_BuildValue("(iiiiii)", SIGABRT, SIGFPE, SIGILL, ++ SIGINT, SIGSEGV, SIGTERM); + #endif + if (tup == NULL) { + return NULL; + } +- PyObject *set = PySet_New(tup); ++ set = PySet_New(tup); + Py_DECREF(tup); + return set; + #else +@@ -1614,10 +1627,14 @@ signal_add_constants(PyObject *module) + static int + signal_get_set_handlers(signal_state_t *state, PyObject *mod_dict) + { ++ int signum; ++ PyObject *sigint_func; ++ + // Get signal handlers +- for (int signum = 1; signum < Py_NSIG; signum++) { ++ for (signum = 1; signum < Py_NSIG; signum++) { + void (*c_handler)(int) = PyOS_getsig(signum); + PyObject *func; ++ PyObject *old_func; + if (c_handler == SIG_DFL) { + func = state->default_handler; + } +@@ -1629,13 +1646,13 @@ signal_get_set_handlers(signal_state_t *state, PyObject *mod_dict) + } + // If signal_module_exec() is called more than one, we must + // clear the strong reference to the previous function. +- PyObject* old_func = get_handler(signum); ++ old_func = get_handler(signum); + set_handler(signum, Py_NewRef(func)); + Py_XDECREF(old_func); + } + + // Install Python SIGINT handler which raises KeyboardInterrupt +- PyObject* sigint_func = get_handler(SIGINT); ++ sigint_func = get_handler(SIGINT); + if (sigint_func == state->default_handler) { + PyObject *int_handler = PyMapping_GetItemString(mod_dict, + "default_int_handler"); +@@ -1654,10 +1671,15 @@ signal_get_set_handlers(signal_state_t *state, PyObject *mod_dict) + static int + signal_module_exec(PyObject *m) + { ++ signal_state_t *state; ++ _signal_module_state *modstate; ++ PyObject *d; ++ PyThreadState *tstate; ++ + assert(!PyErr_Occurred()); + +- signal_state_t *state = &signal_global_state; +- _signal_module_state *modstate = get_signal_state(m); ++ state = &signal_global_state; ++ modstate = get_signal_state(m); + + modstate->default_handler = state->default_handler; // borrowed ref + modstate->ignore_handler = state->ignore_handler; // borrowed ref +@@ -1675,7 +1697,7 @@ signal_module_exec(PyObject *m) + } + + /* Add some symbolic constants to the module */ +- PyObject *d = PyModule_GetDict(m); ++ d = PyModule_GetDict(m); + if (PyDict_SetItemString(d, "SIG_DFL", state->default_handler) < 0) { + return -1; + } +@@ -1700,7 +1722,7 @@ signal_module_exec(PyObject *m) + } + #endif + +- PyThreadState *tstate = _PyThreadState_GET(); ++ tstate = _PyThreadState_GET(); + if (_Py_IsMainInterpreter(tstate->interp)) { + if (signal_get_set_handlers(state, d) < 0) { + return -1; +@@ -1747,14 +1769,14 @@ static PyModuleDef_Slot signal_slots[] = { + static struct PyModuleDef signal_module = { + PyModuleDef_HEAD_INIT, + "_signal", +- .m_doc = module_doc, +- .m_size = sizeof(_signal_module_state), +- .m_methods = signal_methods, +- .m_slots = signal_slots, ++ module_doc, ++ sizeof(_signal_module_state), ++ signal_methods, ++ signal_slots, + #ifdef PYHAVE_ITIMER_ERROR +- .m_traverse = _signal_module_traverse, +- .m_clear = _signal_module_clear, +- .m_free = _signal_module_free, ++ _signal_module_traverse, ++ _signal_module_clear, ++ _signal_module_free, + #endif + }; + +@@ -1769,10 +1791,13 @@ PyInit__signal(void) + void + _PySignal_Fini(void) + { +- signal_state_t *state = &signal_global_state; ++ signal_state_t *state; ++ int signum; ++ ++ state = &signal_global_state; + + // Restore default signals and clear handlers +- for (int signum = 1; signum < Py_NSIG; signum++) { ++ for (signum = 1; signum < Py_NSIG; signum++) { + PyObject *func = get_handler(signum); + _Py_atomic_store_relaxed(&Handlers[signum].tripped, 0); + set_handler(signum, NULL); +@@ -1815,6 +1840,10 @@ PyErr_CheckSignals(void) + int + _PyErr_CheckSignalsTstate(PyThreadState *tstate) + { ++ _PyInterpreterFrame *frame; ++ signal_state_t *state; ++ int i; ++ + _Py_CHECK_EMSCRIPTEN_SIGNALS(); + if (!_Py_atomic_load(&is_tripped)) { + return 0; +@@ -1836,12 +1865,16 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate) + */ + _Py_atomic_store(&is_tripped, 0); + +- _PyInterpreterFrame *frame = tstate->cframe->current_frame; ++ frame = tstate->cframe->current_frame; + while (frame && _PyFrame_IsIncomplete(frame)) { + frame = frame->previous; + } +- signal_state_t *state = &signal_global_state; +- for (int i = 1; i < Py_NSIG; i++) { ++ state = &signal_global_state; ++ for (i = 1; i < Py_NSIG; i++) { ++ PyObject *func; ++ PyObject *arglist; ++ PyObject *result; ++ + if (!_Py_atomic_load_relaxed(&Handlers[i].tripped)) { + continue; + } +@@ -1853,7 +1886,7 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate) + * signal handler for it by the time PyErr_CheckSignals() is called + * (see bpo-43406). + */ +- PyObject *func = get_handler(i); ++ func = get_handler(i); + if (func == NULL || func == Py_None || + compare_handler(func, state->ignore_handler) || + compare_handler(func, state->default_handler)) { +@@ -1871,7 +1904,7 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate) + PyErr_WriteUnraisable(Py_None); + continue; + } +- PyObject *arglist = NULL; ++ arglist = NULL; + if (frame == NULL) { + arglist = Py_BuildValue("(iO)", i, Py_None); + } +@@ -1881,7 +1914,6 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate) + arglist = Py_BuildValue("(iO)", i, f); + } + } +- PyObject *result; + if (arglist) { + result = _PyObject_Call(tstate, func, arglist, NULL); + Py_DECREF(arglist); +@@ -1918,12 +1950,15 @@ _PyErr_CheckSignals(void) + int + PyErr_SetInterruptEx(int signum) + { ++ signal_state_t *state; ++ PyObject *func; ++ + if (signum < 1 || signum >= Py_NSIG) { + return -1; + } + +- signal_state_t *state = &signal_global_state; +- PyObject *func = get_handler(signum); ++ state = &signal_global_state; ++ func = get_handler(signum); + if (!compare_handler(func, state->ignore_handler) + && !compare_handler(func, state->default_handler)) { + trip_signal(signum); +@@ -1987,7 +2022,10 @@ _Py_RestoreSignals(void) + int + _PySignal_Init(int install_signal_handlers) + { +- signal_state_t *state = &signal_global_state; ++ signal_state_t *state; ++ int signum; ++ ++ state = &signal_global_state; + + state->default_handler = PyLong_FromVoidPtr((void *)SIG_DFL); + if (state->default_handler == NULL) { +@@ -2008,7 +2046,7 @@ _PySignal_Init(int install_signal_handlers) + } + #endif + +- for (int signum = 1; signum < Py_NSIG; signum++) { ++ for (signum = 1; signum < Py_NSIG; signum++) { + _Py_atomic_store_relaxed(&Handlers[signum].tripped, 0); + } + +@@ -2053,12 +2091,14 @@ PyOS_InterruptOccurred(void) + static void + _clear_pending_signals(void) + { ++ int i; ++ + if (!_Py_atomic_load(&is_tripped)) { + return; + } + + _Py_atomic_store(&is_tripped, 0); +- for (int i = 1; i < Py_NSIG; ++i) { ++ for (i = 1; i < Py_NSIG; ++i) { + _Py_atomic_store_relaxed(&Handlers[i].tripped, 0); + } + } +diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c +index f0c9b46..f5d0d6b 100644 +--- a/Modules/socketmodule.c ++++ b/Modules/socketmodule.c +@@ -490,7 +490,7 @@ remove_unusable_flags(PyObject *m) + #endif + #endif + +-#ifdef MS_WINDOWS ++#if defined(MS_WINDOWS) && defined(AF_BTH) + #define sockaddr_rc SOCKADDR_BTH_REDEF + + #define USE_BLUETOOTH 1 +diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h +index 1b35b11..4550562 100644 +--- a/Modules/socketmodule.h ++++ b/Modules/socketmodule.h +@@ -264,7 +264,7 @@ typedef union sock_addr { + struct sockaddr_rc bt_rc; + struct sockaddr_sco bt_sco; + struct sockaddr_hci bt_hci; +-#elif defined(MS_WINDOWS) ++#elif defined(MS_WINDOWS) && defined(AF_BTH) + struct SOCKADDR_BTH_REDEF bt_rc; + #endif + #ifdef HAVE_NETPACKET_PACKET_H +diff --git a/Modules/symtablemodule.c b/Modules/symtablemodule.c +index c25ecc2..57cbc44 100644 +--- a/Modules/symtablemodule.c ++++ b/Modules/symtablemodule.c +@@ -27,12 +27,15 @@ _symtable_symtable_impl(PyObject *module, PyObject *source, + struct symtable *st; + PyObject *t; + int start; +- PyCompilerFlags cf = _PyCompilerFlags_INIT; ++ PyCompilerFlags cf; + PyObject *source_copy = NULL; ++ const char *str; + ++ cf.cf_flags = 0; ++ cf.cf_feature_version = PY_MINOR_VERSION; + cf.cf_flags = PyCF_SOURCE_IS_UTF8; + +- const char *str = _Py_SourceAsString(source, "symtable", "string or bytes", &cf, &source_copy); ++ str = _Py_SourceAsString(source, "symtable", "string or bytes", &cf, &source_copy); + if (str == NULL) { + return NULL; + } +@@ -113,10 +116,11 @@ static PyModuleDef_Slot symtable_slots[] = { + + static struct PyModuleDef symtablemodule = { + PyModuleDef_HEAD_INIT, +- .m_name = "_symtable", +- .m_size = 0, +- .m_methods = symtable_methods, +- .m_slots = symtable_slots, ++ "_symtable", ++ NULL, ++ 0, ++ symtable_methods, ++ symtable_slots, + }; + + PyMODINIT_FUNC +diff --git a/Modules/timemodule.c b/Modules/timemodule.c +index b8e0e48..65f0097 100644 +--- a/Modules/timemodule.c ++++ b/Modules/timemodule.c +@@ -141,6 +141,8 @@ static int + _PyTime_GetClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) + { + static int initialized = 0; ++ clock_t ticks; ++ _PyTime_t ns; + + if (!initialized) { + initialized = 1; +@@ -161,14 +163,14 @@ _PyTime_GetClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) + info->adjustable = 0; + } + +- clock_t ticks = clock(); ++ ticks = clock(); + if (ticks == (clock_t)-1) { + PyErr_SetString(PyExc_RuntimeError, + "the processor time used is not available " + "or its value cannot be represented"); + return -1; + } +- _PyTime_t ns = _PyTime_MulDiv(ticks, SEC_TO_NS, (_PyTime_t)CLOCKS_PER_SEC); ++ ns = _PyTime_MulDiv(ticks, SEC_TO_NS, (_PyTime_t)CLOCKS_PER_SEC); + *tp = _PyTime_FromNanoseconds(ns); + return 0; + } +@@ -496,6 +498,7 @@ time_gmtime(PyObject *module, PyObject *args) + { + time_t when; + struct tm buf; ++ time_module_state *state; + + if (!parse_time_t_args(args, "|O:gmtime", &when)) + return NULL; +@@ -504,7 +507,7 @@ time_gmtime(PyObject *module, PyObject *args) + if (_PyTime_gmtime(when, &buf) != 0) + return NULL; + +- time_module_state *state = get_time_state(module); ++ state = get_time_state(module); + #ifdef HAVE_STRUCT_TM_TM_ZONE + return tmtotuple(state, &buf); + #else +@@ -540,13 +543,14 @@ time_localtime(PyObject *module, PyObject *args) + { + time_t when; + struct tm buf; ++ time_module_state *state; + + if (!parse_time_t_args(args, "|O:localtime", &when)) + return NULL; + if (_PyTime_localtime(when, &buf) != 0) + return NULL; + +- time_module_state *state = get_time_state(module); ++ state = get_time_state(module); + #ifdef HAVE_STRUCT_TM_TM_ZONE + return tmtotuple(state, &buf); + #else +@@ -761,6 +765,7 @@ time_strftime(PyObject *module, PyObject *args) + time_char *outbuf = NULL; + size_t i; + PyObject *ret = NULL; ++ time_module_state *state; + + memset((void *) &buf, '\0', sizeof(buf)); + +@@ -770,7 +775,7 @@ time_strftime(PyObject *module, PyObject *args) + if (!PyArg_ParseTuple(args, "U|O:strftime", &format_arg, &tup)) + return NULL; + +- time_module_state *state = get_time_state(module); ++ state = get_time_state(module); + if (tup == NULL) { + time_t tt = time(NULL); + if (_PyTime_localtime(tt, &buf) != 0) +@@ -962,11 +967,12 @@ time_asctime(PyObject *module, PyObject *args) + { + PyObject *tup = NULL; + struct tm buf; ++ time_module_state *state; + + if (!PyArg_UnpackTuple(args, "asctime", 0, 1, &tup)) + return NULL; + +- time_module_state *state = get_time_state(module); ++ state = get_time_state(module); + if (tup == NULL) { + time_t tt = time(NULL); + if (_PyTime_localtime(tt, &buf) != 0) +@@ -1724,6 +1730,13 @@ get_gmtoff(time_t t, struct tm *p) + static int + init_timezone(PyObject *m) + { ++#ifdef HAVE_DECL_TZNAME ++ PyObject *otz0, *otz1; ++#ifdef MS_WINDOWS ++ TIME_ZONE_INFORMATION tzinfo = {0}; ++#endif ++#endif ++ + assert(!PyErr_Occurred()); + + /* This code moved from PyInit_time wholesale to allow calling it from +@@ -1743,7 +1756,6 @@ init_timezone(PyObject *m) + And I'm lazy and hate C so nyer. + */ + #ifdef HAVE_DECL_TZNAME +- PyObject *otz0, *otz1; + tzset(); + PyModule_AddIntConstant(m, "timezone", _Py_timezone); + #ifdef HAVE_ALTZONE +@@ -1753,7 +1765,6 @@ init_timezone(PyObject *m) + #endif + PyModule_AddIntConstant(m, "daylight", _Py_daylight); + #ifdef MS_WINDOWS +- TIME_ZONE_INFORMATION tzinfo = {0}; + GetTimeZoneInformation(&tzinfo); + otz0 = PyUnicode_FromWideChar(tzinfo.StandardName, -1); + if (otz0 == NULL) { +@@ -2094,14 +2105,14 @@ static struct PyModuleDef_Slot time_slots[] = { + + static struct PyModuleDef timemodule = { + PyModuleDef_HEAD_INIT, +- .m_name = "time", +- .m_doc = module_doc, +- .m_size = sizeof(time_module_state), +- .m_methods = time_methods, +- .m_slots = time_slots, +- .m_traverse = time_module_traverse, +- .m_clear = time_module_clear, +- .m_free = time_module_free, ++ "time", ++ module_doc, ++ sizeof(time_module_state), ++ time_methods, ++ time_slots, ++ time_module_traverse, ++ time_module_clear, ++ time_module_free, + }; + + PyMODINIT_FUNC +@@ -2117,6 +2128,14 @@ PyInit_time(void) + static int + pysleep(_PyTime_t timeout) + { ++#ifdef MS_WINDOWS ++ _PyTime_t timeout_100ns; ++ LARGE_INTEGER relative_timeout; ++ HANDLE timer; ++ HANDLE sigint_event; ++ HANDLE events[2]; ++ DWORD rc; ++#endif + assert(timeout >= 0); + + #ifndef MS_WINDOWS +@@ -2196,7 +2215,7 @@ pysleep(_PyTime_t timeout) + + return 0; + #else // MS_WINDOWS +- _PyTime_t timeout_100ns = _PyTime_As100Nanoseconds(timeout, ++ timeout_100ns = _PyTime_As100Nanoseconds(timeout, + _PyTime_ROUND_CEILING); + + // Maintain Windows Sleep() semantics for time.sleep(0) +@@ -2211,13 +2230,12 @@ pysleep(_PyTime_t timeout) + return 0; + } + +- LARGE_INTEGER relative_timeout; + // No need to check for integer overflow, both types are signed + assert(sizeof(relative_timeout) == sizeof(timeout_100ns)); + // SetWaitableTimer(): a negative due time indicates relative time + relative_timeout.QuadPart = -timeout_100ns; + +- HANDLE timer = CreateWaitableTimerExW(NULL, NULL, timer_flags, ++ timer = CreateWaitableTimerExW(NULL, NULL, timer_flags, + TIMER_ALL_ACCESS); + if (timer == NULL) { + PyErr_SetFromWindowsErr(0); +@@ -2237,7 +2255,7 @@ pysleep(_PyTime_t timeout) + // Only the main thread can be interrupted by SIGINT. + // Signal handlers are only executed in the main thread. + if (_PyOS_IsMainThread()) { +- HANDLE sigint_event = _PyOS_SigintEvent(); ++ sigint_event = _PyOS_SigintEvent(); + + while (1) { + // Check for pending SIGINT signal before resetting the event +@@ -2246,8 +2264,8 @@ pysleep(_PyTime_t timeout) + } + ResetEvent(sigint_event); + +- HANDLE events[] = {timer, sigint_event}; +- DWORD rc; ++ events[0] = timer; ++ events[1] = sigint_event; + + Py_BEGIN_ALLOW_THREADS + rc = WaitForMultipleObjects(Py_ARRAY_LENGTH(events), events, +@@ -2272,8 +2290,6 @@ pysleep(_PyTime_t timeout) + } + } + else { +- DWORD rc; +- + Py_BEGIN_ALLOW_THREADS + rc = WaitForSingleObject(timer, INFINITE); + Py_END_ALLOW_THREADS +diff --git a/Objects/abstract.c b/Objects/abstract.c +index 93987c2..c321b2f 100644 +--- a/Objects/abstract.c ++++ b/Objects/abstract.c +@@ -52,12 +52,14 @@ PyObject_Type(PyObject *o) + Py_ssize_t + PyObject_Size(PyObject *o) + { ++ PySequenceMethods *m; ++ + if (o == NULL) { + null_error(); + return -1; + } + +- PySequenceMethods *m = Py_TYPE(o)->tp_as_sequence; ++ m = Py_TYPE(o)->tp_as_sequence; + if (m && m->sq_length) { + Py_ssize_t len = m->sq_length(o); + assert(_Py_CheckSlotResult(o, "__len__", len >= 0)); +@@ -148,18 +150,21 @@ PyObject_LengthHint(PyObject *o, Py_ssize_t defaultvalue) + PyObject * + PyObject_GetItem(PyObject *o, PyObject *key) + { ++ PyMappingMethods *m; ++ PySequenceMethods *ms; ++ + if (o == NULL || key == NULL) { + return null_error(); + } + +- PyMappingMethods *m = Py_TYPE(o)->tp_as_mapping; ++ m = Py_TYPE(o)->tp_as_mapping; + if (m && m->mp_subscript) { + PyObject *item = m->mp_subscript(o, key); + assert(_Py_CheckSlotResult(o, "__getitem__", item != NULL)); + return item; + } + +- PySequenceMethods *ms = Py_TYPE(o)->tp_as_sequence; ++ ms = Py_TYPE(o)->tp_as_sequence; + if (ms && ms->sq_item) { + if (_PyIndex_Check(key)) { + Py_ssize_t key_value; +@@ -202,12 +207,14 @@ PyObject_GetItem(PyObject *o, PyObject *key) + int + PyObject_SetItem(PyObject *o, PyObject *key, PyObject *value) + { ++ PyMappingMethods *m; ++ + if (o == NULL || key == NULL || value == NULL) { + null_error(); + return -1; + } + +- PyMappingMethods *m = Py_TYPE(o)->tp_as_mapping; ++ m = Py_TYPE(o)->tp_as_mapping; + if (m && m->mp_ass_subscript) { + int res = m->mp_ass_subscript(o, key, value); + assert(_Py_CheckSlotResult(o, "__setitem__", res >= 0)); +@@ -236,12 +243,14 @@ PyObject_SetItem(PyObject *o, PyObject *key, PyObject *value) + int + PyObject_DelItem(PyObject *o, PyObject *key) + { ++ PyMappingMethods *m; ++ + if (o == NULL || key == NULL) { + null_error(); + return -1; + } + +- PyMappingMethods *m = Py_TYPE(o)->tp_as_mapping; ++ m = Py_TYPE(o)->tp_as_mapping; + if (m && m->mp_ass_subscript) { + int res = m->mp_ass_subscript(o, key, (PyObject*)NULL); + assert(_Py_CheckSlotResult(o, "__delitem__", res >= 0)); +@@ -379,7 +388,10 @@ int PyObject_AsWriteBuffer(PyObject *obj, + int + PyObject_GetBuffer(PyObject *obj, Py_buffer *view, int flags) + { +- PyBufferProcs *pb = Py_TYPE(obj)->tp_as_buffer; ++ PyBufferProcs *pb; ++ int res; ++ ++ pb = Py_TYPE(obj)->tp_as_buffer; + + if (pb == NULL || pb->bf_getbuffer == NULL) { + PyErr_Format(PyExc_TypeError, +@@ -387,7 +399,7 @@ PyObject_GetBuffer(PyObject *obj, Py_buffer *view, int flags) + Py_TYPE(obj)->tp_name); + return -1; + } +- int res = (*pb->bf_getbuffer)(obj, view, flags); ++ res = (*pb->bf_getbuffer)(obj, view, flags); + assert(_Py_CheckSlotResult(obj, "getbuffer", res >= 0)); + return res; + } +@@ -831,9 +843,11 @@ PyObject_Format(PyObject *obj, PyObject *format_spec) + int + PyNumber_Check(PyObject *o) + { ++ PyNumberMethods *nb; ++ + if (o == NULL) + return 0; +- PyNumberMethods *nb = Py_TYPE(o)->tp_as_number; ++ nb = Py_TYPE(o)->tp_as_number; + return nb && (nb->nb_index || nb->nb_int || nb->nb_float || PyComplex_Check(o)); + } + +@@ -863,6 +877,8 @@ binary_op1(PyObject *v, PyObject *w, const int op_slot + ) + { + binaryfunc slotv; ++ binaryfunc slotw; ++ + if (Py_TYPE(v)->tp_as_number != NULL) { + slotv = NB_BINOP(Py_TYPE(v)->tp_as_number, op_slot); + } +@@ -870,7 +886,6 @@ binary_op1(PyObject *v, PyObject *w, const int op_slot + slotv = NULL; + } + +- binaryfunc slotw; + if (!Py_IS_TYPE(w, Py_TYPE(v)) && Py_TYPE(w)->tp_as_number != NULL) { + slotw = NB_BINOP(Py_TYPE(w)->tp_as_number, op_slot); + if (slotw == slotv) { +@@ -967,10 +982,15 @@ ternary_op(PyObject *v, + const char *op_name + ) + { +- PyNumberMethods *mv = Py_TYPE(v)->tp_as_number; +- PyNumberMethods *mw = Py_TYPE(w)->tp_as_number; +- ++ PyNumberMethods *mv; ++ PyNumberMethods *mw; ++ PyNumberMethods *mz; + ternaryfunc slotv; ++ ternaryfunc slotw; ++ ++ mv = Py_TYPE(v)->tp_as_number; ++ mw = Py_TYPE(w)->tp_as_number; ++ + if (mv != NULL) { + slotv = NB_TERNOP(mv, op_slot); + } +@@ -978,7 +998,6 @@ ternary_op(PyObject *v, + slotv = NULL; + } + +- ternaryfunc slotw; + if (!Py_IS_TYPE(w, Py_TYPE(v)) && mw != NULL) { + slotw = NB_TERNOP(mw, op_slot); + if (slotw == slotv) { +@@ -1015,7 +1034,7 @@ ternary_op(PyObject *v, + Py_DECREF(x); /* can't do it */ + } + +- PyNumberMethods *mz = Py_TYPE(z)->tp_as_number; ++ mz = Py_TYPE(z)->tp_as_number; + if (mz != NULL) { + ternaryfunc slotz = NB_TERNOP(mz, op_slot); + if (slotz == slotv || slotz == slotw) { +@@ -1070,13 +1089,16 @@ BINARY_FUNC(PyNumber_Divmod, nb_divmod, "divmod()") + PyObject * + PyNumber_Add(PyObject *v, PyObject *w) + { +- PyObject *result = BINARY_OP1(v, w, NB_SLOT(nb_add), "+"); ++ PySequenceMethods *m; ++ PyObject *result; ++ ++ result = BINARY_OP1(v, w, NB_SLOT(nb_add), "+"); + if (result != Py_NotImplemented) { + return result; + } + Py_DECREF(result); + +- PySequenceMethods *m = Py_TYPE(v)->tp_as_sequence; ++ m = Py_TYPE(v)->tp_as_sequence; + if (m && m->sq_concat) { + result = (*m->sq_concat)(v, w); + assert(_Py_CheckSlotResult(v, "+", result != NULL)); +@@ -1090,6 +1112,8 @@ static PyObject * + sequence_repeat(ssizeargfunc repeatfunc, PyObject *seq, PyObject *n) + { + Py_ssize_t count; ++ PyObject *res; ++ + if (_PyIndex_Check(n)) { + count = PyNumber_AsSsize_t(n, PyExc_OverflowError); + if (count == -1 && PyErr_Occurred()) { +@@ -1100,7 +1124,7 @@ sequence_repeat(ssizeargfunc repeatfunc, PyObject *seq, PyObject *n) + return type_error("can't multiply sequence by " + "non-int of type '%.200s'", n); + } +- PyObject *res = (*repeatfunc)(seq, count); ++ res = (*repeatfunc)(seq, count); + assert(_Py_CheckSlotResult(seq, "*", res != NULL)); + return res; + } +@@ -1326,11 +1350,13 @@ _PyNumber_InPlacePowerNoMod(PyObject *lhs, PyObject *rhs) + PyObject * + PyNumber_Negative(PyObject *o) + { ++ PyNumberMethods *m; ++ + if (o == NULL) { + return null_error(); + } + +- PyNumberMethods *m = Py_TYPE(o)->tp_as_number; ++ m = Py_TYPE(o)->tp_as_number; + if (m && m->nb_negative) { + PyObject *res = (*m->nb_negative)(o); + assert(_Py_CheckSlotResult(o, "__neg__", res != NULL)); +@@ -1343,11 +1369,13 @@ PyNumber_Negative(PyObject *o) + PyObject * + PyNumber_Positive(PyObject *o) + { ++ PyNumberMethods *m; ++ + if (o == NULL) { + return null_error(); + } + +- PyNumberMethods *m = Py_TYPE(o)->tp_as_number; ++ m = Py_TYPE(o)->tp_as_number; + if (m && m->nb_positive) { + PyObject *res = (*m->nb_positive)(o); + assert(_Py_CheckSlotResult(o, "__pos__", res != NULL)); +@@ -1360,11 +1388,13 @@ PyNumber_Positive(PyObject *o) + PyObject * + PyNumber_Invert(PyObject *o) + { ++ PyNumberMethods *m; ++ + if (o == NULL) { + return null_error(); + } + +- PyNumberMethods *m = Py_TYPE(o)->tp_as_number; ++ m = Py_TYPE(o)->tp_as_number; + if (m && m->nb_invert) { + PyObject *res = (*m->nb_invert)(o); + assert(_Py_CheckSlotResult(o, "__invert__", res != NULL)); +@@ -1377,11 +1407,13 @@ PyNumber_Invert(PyObject *o) + PyObject * + PyNumber_Absolute(PyObject *o) + { ++ PyNumberMethods *m; ++ + if (o == NULL) { + return null_error(); + } + +- PyNumberMethods *m = Py_TYPE(o)->tp_as_number; ++ m = Py_TYPE(o)->tp_as_number; + if (m && m->nb_absolute) { + PyObject *res = m->nb_absolute(o); + assert(_Py_CheckSlotResult(o, "__abs__", res != NULL)); +@@ -1407,6 +1439,8 @@ PyIndex_Check(PyObject *obj) + PyObject * + _PyNumber_Index(PyObject *item) + { ++ PyObject *result; ++ + if (item == NULL) { + return null_error(); + } +@@ -1422,7 +1456,7 @@ _PyNumber_Index(PyObject *item) + return NULL; + } + +- PyObject *result = Py_TYPE(item)->tp_as_number->nb_index(item); ++ result = Py_TYPE(item)->tp_as_number->nb_index(item); + assert(_Py_CheckSlotResult(item, "__index__", result != NULL)); + if (!result || PyLong_CheckExact(result)) { + return result; +@@ -1466,6 +1500,7 @@ PyNumber_Index(PyObject *item) + Py_ssize_t + PyNumber_AsSsize_t(PyObject *item, PyObject *err) + { ++ PyThreadState *tstate; + Py_ssize_t result; + PyObject *runerr; + PyObject *value = _PyNumber_Index(item); +@@ -1477,7 +1512,7 @@ PyNumber_AsSsize_t(PyObject *item, PyObject *err) + if (result != -1) + goto finish; + +- PyThreadState *tstate = _PyThreadState_GET(); ++ tstate = _PyThreadState_GET(); + runerr = _PyErr_Occurred(tstate); + if (!runerr) { + goto finish; +@@ -1633,6 +1668,9 @@ PyNumber_Long(PyObject *o) + PyObject * + PyNumber_Float(PyObject *o) + { ++ PyNumberMethods *m; ++ double val; ++ + if (o == NULL) { + return null_error(); + } +@@ -1641,7 +1679,7 @@ PyNumber_Float(PyObject *o) + return Py_NewRef(o); + } + +- PyNumberMethods *m = Py_TYPE(o)->tp_as_number; ++ m = Py_TYPE(o)->tp_as_number; + if (m && m->nb_float) { /* This should include subclasses of float */ + PyObject *res = m->nb_float(o); + assert(_Py_CheckSlotResult(o, "__float__", res != NULL)); +@@ -1665,7 +1703,7 @@ PyNumber_Float(PyObject *o) + Py_DECREF(res); + return NULL; + } +- double val = PyFloat_AS_DOUBLE(res); ++ val = PyFloat_AS_DOUBLE(res); + Py_DECREF(res); + return PyFloat_FromDouble(val); + } +@@ -1675,7 +1713,7 @@ PyNumber_Float(PyObject *o) + if (!res) { + return NULL; + } +- double val = PyLong_AsDouble(res); ++ val = PyLong_AsDouble(res); + Py_DECREF(res); + if (val == -1.0 && PyErr_Occurred()) { + return NULL; +@@ -1694,15 +1732,18 @@ PyNumber_Float(PyObject *o) + PyObject * + PyNumber_ToBase(PyObject *n, int base) + { ++ PyObject *index; ++ PyObject *res; ++ + if (!(base == 2 || base == 8 || base == 10 || base == 16)) { + PyErr_SetString(PyExc_SystemError, + "PyNumber_ToBase: base must be 2, 8, 10 or 16"); + return NULL; + } +- PyObject *index = _PyNumber_Index(n); ++ index = _PyNumber_Index(n); + if (!index) + return NULL; +- PyObject *res = _PyLong_Format(index, base); ++ res = _PyLong_Format(index, base); + Py_DECREF(index); + return res; + } +@@ -1722,12 +1763,14 @@ PySequence_Check(PyObject *s) + Py_ssize_t + PySequence_Size(PyObject *s) + { ++ PySequenceMethods *m; ++ + if (s == NULL) { + null_error(); + return -1; + } + +- PySequenceMethods *m = Py_TYPE(s)->tp_as_sequence; ++ m = Py_TYPE(s)->tp_as_sequence; + if (m && m->sq_length) { + Py_ssize_t len = m->sq_length(s); + assert(_Py_CheckSlotResult(s, "__len__", len >= 0)); +@@ -1753,11 +1796,13 @@ PySequence_Length(PyObject *s) + PyObject * + PySequence_Concat(PyObject *s, PyObject *o) + { ++ PySequenceMethods *m; ++ + if (s == NULL || o == NULL) { + return null_error(); + } + +- PySequenceMethods *m = Py_TYPE(s)->tp_as_sequence; ++ m = Py_TYPE(s)->tp_as_sequence; + if (m && m->sq_concat) { + PyObject *res = m->sq_concat(s, o); + assert(_Py_CheckSlotResult(s, "+", res != NULL)); +@@ -1779,11 +1824,13 @@ PySequence_Concat(PyObject *s, PyObject *o) + PyObject * + PySequence_Repeat(PyObject *o, Py_ssize_t count) + { ++ PySequenceMethods *m; ++ + if (o == NULL) { + return null_error(); + } + +- PySequenceMethods *m = Py_TYPE(o)->tp_as_sequence; ++ m = Py_TYPE(o)->tp_as_sequence; + if (m && m->sq_repeat) { + PyObject *res = m->sq_repeat(o, count); + assert(_Py_CheckSlotResult(o, "*", res != NULL)); +@@ -1810,11 +1857,13 @@ PySequence_Repeat(PyObject *o, Py_ssize_t count) + PyObject * + PySequence_InPlaceConcat(PyObject *s, PyObject *o) + { ++ PySequenceMethods *m; ++ + if (s == NULL || o == NULL) { + return null_error(); + } + +- PySequenceMethods *m = Py_TYPE(s)->tp_as_sequence; ++ m = Py_TYPE(s)->tp_as_sequence; + if (m && m->sq_inplace_concat) { + PyObject *res = m->sq_inplace_concat(s, o); + assert(_Py_CheckSlotResult(s, "+=", res != NULL)); +@@ -1839,11 +1888,13 @@ PySequence_InPlaceConcat(PyObject *s, PyObject *o) + PyObject * + PySequence_InPlaceRepeat(PyObject *o, Py_ssize_t count) + { ++ PySequenceMethods *m; ++ + if (o == NULL) { + return null_error(); + } + +- PySequenceMethods *m = Py_TYPE(o)->tp_as_sequence; ++ m = Py_TYPE(o)->tp_as_sequence; + if (m && m->sq_inplace_repeat) { + PyObject *res = m->sq_inplace_repeat(o, count); + assert(_Py_CheckSlotResult(o, "*=", res != NULL)); +@@ -1873,11 +1924,14 @@ PySequence_InPlaceRepeat(PyObject *o, Py_ssize_t count) + PyObject * + PySequence_GetItem(PyObject *s, Py_ssize_t i) + { ++ PySequenceMethods *m; ++ PyObject *res; ++ + if (s == NULL) { + return null_error(); + } + +- PySequenceMethods *m = Py_TYPE(s)->tp_as_sequence; ++ m = Py_TYPE(s)->tp_as_sequence; + if (m && m->sq_item) { + if (i < 0) { + if (m->sq_length) { +@@ -1889,7 +1943,7 @@ PySequence_GetItem(PyObject *s, Py_ssize_t i) + i += l; + } + } +- PyObject *res = m->sq_item(s, i); ++ res = m->sq_item(s, i); + assert(_Py_CheckSlotResult(s, "__getitem__", res != NULL)); + return res; + } +@@ -1903,17 +1957,21 @@ PySequence_GetItem(PyObject *s, Py_ssize_t i) + PyObject * + PySequence_GetSlice(PyObject *s, Py_ssize_t i1, Py_ssize_t i2) + { ++ PyMappingMethods *mp; ++ PyObject *slice; ++ PyObject *res; ++ + if (!s) { + return null_error(); + } + +- PyMappingMethods *mp = Py_TYPE(s)->tp_as_mapping; ++ mp = Py_TYPE(s)->tp_as_mapping; + if (mp && mp->mp_subscript) { +- PyObject *slice = _PySlice_FromIndices(i1, i2); ++ slice = _PySlice_FromIndices(i1, i2); + if (!slice) { + return NULL; + } +- PyObject *res = mp->mp_subscript(s, slice); ++ res = mp->mp_subscript(s, slice); + assert(_Py_CheckSlotResult(s, "__getitem__", res != NULL)); + Py_DECREF(slice); + return res; +@@ -1925,12 +1983,15 @@ PySequence_GetSlice(PyObject *s, Py_ssize_t i1, Py_ssize_t i2) + int + PySequence_SetItem(PyObject *s, Py_ssize_t i, PyObject *o) + { ++ PySequenceMethods *m; ++ int res; ++ + if (s == NULL) { + null_error(); + return -1; + } + +- PySequenceMethods *m = Py_TYPE(s)->tp_as_sequence; ++ m = Py_TYPE(s)->tp_as_sequence; + if (m && m->sq_ass_item) { + if (i < 0) { + if (m->sq_length) { +@@ -1942,7 +2003,7 @@ PySequence_SetItem(PyObject *s, Py_ssize_t i, PyObject *o) + i += l; + } + } +- int res = m->sq_ass_item(s, i, o); ++ res = m->sq_ass_item(s, i, o); + assert(_Py_CheckSlotResult(s, "__setitem__", res >= 0)); + return res; + } +@@ -1958,12 +2019,15 @@ PySequence_SetItem(PyObject *s, Py_ssize_t i, PyObject *o) + int + PySequence_DelItem(PyObject *s, Py_ssize_t i) + { ++ PySequenceMethods *m; ++ int res; ++ + if (s == NULL) { + null_error(); + return -1; + } + +- PySequenceMethods *m = Py_TYPE(s)->tp_as_sequence; ++ m = Py_TYPE(s)->tp_as_sequence; + if (m && m->sq_ass_item) { + if (i < 0) { + if (m->sq_length) { +@@ -1975,7 +2039,7 @@ PySequence_DelItem(PyObject *s, Py_ssize_t i) + i += l; + } + } +- int res = m->sq_ass_item(s, i, (PyObject *)NULL); ++ res = m->sq_ass_item(s, i, (PyObject *)NULL); + assert(_Py_CheckSlotResult(s, "__delitem__", res >= 0)); + return res; + } +@@ -1991,17 +2055,21 @@ PySequence_DelItem(PyObject *s, Py_ssize_t i) + int + PySequence_SetSlice(PyObject *s, Py_ssize_t i1, Py_ssize_t i2, PyObject *o) + { ++ PyMappingMethods *mp; ++ PyObject *slice; ++ int res; ++ + if (s == NULL) { + null_error(); + return -1; + } + +- PyMappingMethods *mp = Py_TYPE(s)->tp_as_mapping; ++ mp = Py_TYPE(s)->tp_as_mapping; + if (mp && mp->mp_ass_subscript) { +- PyObject *slice = _PySlice_FromIndices(i1, i2); ++ slice = _PySlice_FromIndices(i1, i2); + if (!slice) + return -1; +- int res = mp->mp_ass_subscript(s, slice, o); ++ res = mp->mp_ass_subscript(s, slice, o); + assert(_Py_CheckSlotResult(s, "__setitem__", res >= 0)); + Py_DECREF(slice); + return res; +@@ -2014,18 +2082,22 @@ PySequence_SetSlice(PyObject *s, Py_ssize_t i1, Py_ssize_t i2, PyObject *o) + int + PySequence_DelSlice(PyObject *s, Py_ssize_t i1, Py_ssize_t i2) + { ++ PyMappingMethods *mp; ++ PyObject *slice; ++ int res; ++ + if (s == NULL) { + null_error(); + return -1; + } + +- PyMappingMethods *mp = Py_TYPE(s)->tp_as_mapping; ++ mp = Py_TYPE(s)->tp_as_mapping; + if (mp && mp->mp_ass_subscript) { +- PyObject *slice = _PySlice_FromIndices(i1, i2); ++ slice = _PySlice_FromIndices(i1, i2); + if (!slice) { + return -1; + } +- int res = mp->mp_ass_subscript(s, slice, NULL); ++ res = mp->mp_ass_subscript(s, slice, NULL); + assert(_Py_CheckSlotResult(s, "__delitem__", res >= 0)); + Py_DECREF(slice); + return res; +@@ -2273,13 +2345,14 @@ PySequence_Count(PyObject *s, PyObject *o) + int + PySequence_Contains(PyObject *seq, PyObject *ob) + { ++ Py_ssize_t result; + PySequenceMethods *sqm = Py_TYPE(seq)->tp_as_sequence; + if (sqm != NULL && sqm->sq_contains != NULL) { + int res = (*sqm->sq_contains)(seq, ob); + assert(_Py_CheckSlotResult(seq, "__contains__", res >= 0)); + return res; + } +- Py_ssize_t result = _PySequence_IterSearch(seq, ob, PY_ITERSEARCH_CONTAINS); ++ result = _PySequence_IterSearch(seq, ob, PY_ITERSEARCH_CONTAINS); + return Py_SAFE_DOWNCAST(result, Py_ssize_t, int); + } + +@@ -2309,12 +2382,14 @@ PyMapping_Check(PyObject *o) + Py_ssize_t + PyMapping_Size(PyObject *o) + { ++ PyMappingMethods *m; ++ + if (o == NULL) { + null_error(); + return -1; + } + +- PyMappingMethods *m = Py_TYPE(o)->tp_as_mapping; ++ m = Py_TYPE(o)->tp_as_mapping; + if (m && m->mp_length) { + Py_ssize_t len = m->mp_length(o); + assert(_Py_CheckSlotResult(o, "__len__", len >= 0)); +@@ -2616,6 +2691,14 @@ object_isinstance(PyObject *inst, PyObject *cls) + static int + object_recursive_isinstance(PyThreadState *tstate, PyObject *inst, PyObject *cls) + { ++ Py_ssize_t n; ++ Py_ssize_t i; ++ int r; ++ PyObject *item; ++ PyObject *checker; ++ PyObject *res; ++ int ok; ++ + /* Quick test for an exact match */ + if (Py_IS_TYPE(inst, (PyTypeObject *)cls)) { + return 1; +@@ -2636,10 +2719,10 @@ object_recursive_isinstance(PyThreadState *tstate, PyObject *inst, PyObject *cls + if (_Py_EnterRecursiveCallTstate(tstate, " in __instancecheck__")) { + return -1; + } +- Py_ssize_t n = PyTuple_GET_SIZE(cls); +- int r = 0; +- for (Py_ssize_t i = 0; i < n; ++i) { +- PyObject *item = PyTuple_GET_ITEM(cls, i); ++ n = PyTuple_GET_SIZE(cls); ++ r = 0; ++ for (i = 0; i < n; ++i) { ++ item = PyTuple_GET_ITEM(cls, i); + r = object_recursive_isinstance(tstate, inst, item); + if (r != 0) { + /* either found it, or got an error */ +@@ -2650,21 +2733,21 @@ object_recursive_isinstance(PyThreadState *tstate, PyObject *inst, PyObject *cls + return r; + } + +- PyObject *checker = _PyObject_LookupSpecial(cls, &_Py_ID(__instancecheck__)); ++ checker = _PyObject_LookupSpecial(cls, &_Py_ID(__instancecheck__)); + if (checker != NULL) { + if (_Py_EnterRecursiveCallTstate(tstate, " in __instancecheck__")) { + Py_DECREF(checker); + return -1; + } + +- PyObject *res = PyObject_CallOneArg(checker, inst); ++ res = PyObject_CallOneArg(checker, inst); + _Py_LeaveRecursiveCallTstate(tstate); + Py_DECREF(checker); + + if (res == NULL) { + return -1; + } +- int ok = PyObject_IsTrue(res); ++ ok = PyObject_IsTrue(res); + Py_DECREF(res); + + return ok; +@@ -2710,6 +2793,12 @@ static int + object_issubclass(PyThreadState *tstate, PyObject *derived, PyObject *cls) + { + PyObject *checker; ++ Py_ssize_t n; ++ Py_ssize_t i; ++ int r; ++ PyObject *item; ++ PyObject *res; ++ int ok; + + /* We know what type's __subclasscheck__ does. */ + if (PyType_CheckExact(cls)) { +@@ -2728,10 +2817,10 @@ object_issubclass(PyThreadState *tstate, PyObject *derived, PyObject *cls) + if (_Py_EnterRecursiveCallTstate(tstate, " in __subclasscheck__")) { + return -1; + } +- Py_ssize_t n = PyTuple_GET_SIZE(cls); +- int r = 0; +- for (Py_ssize_t i = 0; i < n; ++i) { +- PyObject *item = PyTuple_GET_ITEM(cls, i); ++ n = PyTuple_GET_SIZE(cls); ++ r = 0; ++ for (i = 0; i < n; ++i) { ++ item = PyTuple_GET_ITEM(cls, i); + r = object_issubclass(tstate, derived, item); + if (r != 0) + /* either found it, or got an error */ +@@ -2743,12 +2832,12 @@ object_issubclass(PyThreadState *tstate, PyObject *derived, PyObject *cls) + + checker = _PyObject_LookupSpecial(cls, &_Py_ID(__subclasscheck__)); + if (checker != NULL) { +- int ok = -1; ++ ok = -1; + if (_Py_EnterRecursiveCallTstate(tstate, " in __subclasscheck__")) { + Py_DECREF(checker); + return ok; + } +- PyObject *res = PyObject_CallOneArg(checker, derived); ++ res = PyObject_CallOneArg(checker, derived); + _Py_LeaveRecursiveCallTstate(tstate); + Py_DECREF(checker); + if (res != NULL) { +@@ -2817,12 +2906,13 @@ PyObject * + PyObject_GetAIter(PyObject *o) { + PyTypeObject *t = Py_TYPE(o); + unaryfunc f; ++ PyObject *it; + + if (t->tp_as_async == NULL || t->tp_as_async->am_aiter == NULL) { + return type_error("'%.200s' object is not an async iterable", o); + } + f = t->tp_as_async->am_aiter; +- PyObject *it = (*f)(o); ++ it = (*f)(o); + if (it != NULL && !PyAIter_Check(it)) { + PyErr_Format(PyExc_TypeError, + "aiter() returned not an async iterator of type '%.100s'", +diff --git a/Objects/boolobject.c b/Objects/boolobject.c +index 8a20e36..23abfcc 100644 +--- a/Objects/boolobject.c ++++ b/Objects/boolobject.c +@@ -50,11 +50,13 @@ bool_vectorcall(PyObject *type, PyObject * const*args, + size_t nargsf, PyObject *kwnames) + { + long ok = 0; ++ Py_ssize_t nargs; ++ + if (!_PyArg_NoKwnames("bool", kwnames)) { + return NULL; + } + +- Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); ++ nargs = PyVectorcall_NARGS(nargsf); + if (!_PyArg_CheckPositional("bool", nargs, 0, 1)) { + return NULL; + } +@@ -190,7 +192,17 @@ PyTypeObject PyBool_Type = { + 0, /* tp_init */ + 0, /* tp_alloc */ + bool_new, /* tp_new */ +- .tp_vectorcall = bool_vectorcall, ++ 0, /* tp_free */ ++ 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 */ ++ bool_vectorcall, /* tp_vectorcall */ + }; + + /* The objects representing bool values False and True */ +diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c +index 1c50203..bd1c174 100644 +--- a/Objects/bytearrayobject.c ++++ b/Objects/bytearrayobject.c +@@ -320,14 +320,19 @@ bytearray_iconcat(PyByteArrayObject *self, PyObject *other) + static PyObject * + bytearray_repeat(PyByteArrayObject *self, Py_ssize_t count) + { ++ Py_ssize_t mysize; ++ Py_ssize_t size; ++ PyByteArrayObject *result; ++ const char *buf; ++ + if (count < 0) + count = 0; +- const Py_ssize_t mysize = Py_SIZE(self); ++ mysize = Py_SIZE(self); + if (count > 0 && mysize > PY_SSIZE_T_MAX / count) + return PyErr_NoMemory(); +- Py_ssize_t size = mysize * count; +- PyByteArrayObject* result = (PyByteArrayObject *)PyByteArray_FromStringAndSize(NULL, size); +- const char* buf = PyByteArray_AS_STRING(self); ++ size = mysize * count; ++ result = (PyByteArrayObject *)PyByteArray_FromStringAndSize(NULL, size); ++ buf = PyByteArray_AS_STRING(self); + if (result != NULL && size != 0) { + _PyBytes_Repeat(result->ob_bytes, size, buf, mysize); + } +@@ -337,6 +342,10 @@ bytearray_repeat(PyByteArrayObject *self, Py_ssize_t count) + static PyObject * + bytearray_irepeat(PyByteArrayObject *self, Py_ssize_t count) + { ++ Py_ssize_t mysize; ++ Py_ssize_t size; ++ char *buf; ++ + if (count < 0) + count = 0; + else if (count == 1) { +@@ -344,14 +353,14 @@ bytearray_irepeat(PyByteArrayObject *self, Py_ssize_t count) + return (PyObject*)self; + } + +- const Py_ssize_t mysize = Py_SIZE(self); ++ mysize = Py_SIZE(self); + if (count > 0 && mysize > PY_SSIZE_T_MAX / count) + return PyErr_NoMemory(); +- const Py_ssize_t size = mysize * count; ++ size = mysize * count; + if (PyByteArray_Resize((PyObject *)self, size) < 0) + return NULL; + +- char* buf = PyByteArray_AS_STRING(self); ++ buf = PyByteArray_AS_STRING(self); + _PyBytes_Repeat(buf, size, buf, mysize); + + Py_INCREF(self); +@@ -594,6 +603,7 @@ bytearray_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *valu + { + Py_ssize_t start, stop, step, slicelen, needed; + char *buf, *bytes; ++ int ival; + buf = PyByteArray_AS_STRING(self); + + if (_PyIndex_Check(index)) { +@@ -603,7 +613,7 @@ bytearray_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *valu + return -1; + } + +- int ival = -1; ++ ival = -1; + + // GH-91153: We need to do this *before* the size check, in case values + // has a nasty __index__ method that changes the size of the bytearray: +@@ -845,14 +855,20 @@ bytearray___init___impl(PyByteArrayObject *self, PyObject *arg, + } + + if (PyList_CheckExact(arg) || PyTuple_CheckExact(arg)) { +- Py_ssize_t size = PySequence_Fast_GET_SIZE(arg); ++ Py_ssize_t size; ++ PyObject **items; ++ char *s; ++ Py_ssize_t i; ++ ++ size = PySequence_Fast_GET_SIZE(arg); + if (PyByteArray_Resize((PyObject *)self, size) < 0) { + return -1; + } +- PyObject **items = PySequence_Fast_ITEMS(arg); +- char *s = PyByteArray_AS_STRING(self); +- for (Py_ssize_t i = 0; i < size; i++) { ++ items = PySequence_Fast_ITEMS(arg); ++ s = PyByteArray_AS_STRING(self); ++ for (i = 0; i < size; i++) { + int value; ++ int rc; + if (!PyLong_CheckExact(items[i])) { + /* Resize to 0 and go through slowpath */ + if (Py_SIZE(self) != 0) { +@@ -862,7 +878,7 @@ bytearray___init___impl(PyByteArrayObject *self, PyObject *arg, + } + goto slowpath; + } +- int rc = _getbytevalue(items[i], &value); ++ rc = _getbytevalue(items[i], &value); + if (!rc) { + return -1; + } +@@ -1867,6 +1883,8 @@ static PyObject* + bytearray_strip_impl_helper(PyByteArrayObject* self, PyObject* bytes, int striptype) + { + Py_ssize_t mysize, byteslen; ++ Py_ssize_t left; ++ Py_ssize_t right; + const char* myptr; + const char* bytesptr; + Py_buffer vbytes; +@@ -1884,12 +1902,12 @@ bytearray_strip_impl_helper(PyByteArrayObject* self, PyObject* bytes, int stript + myptr = PyByteArray_AS_STRING(self); + mysize = Py_SIZE(self); + +- Py_ssize_t left = 0; ++ left = 0; + if (striptype != RIGHTSTRIP) { + while (left < mysize && memchr(bytesptr, (unsigned char)myptr[left], byteslen)) + left++; + } +- Py_ssize_t right = mysize; ++ right = mysize; + if (striptype != LEFTSTRIP) { + do { + right--; +@@ -2008,8 +2026,10 @@ static PyObject * + bytearray_join(PyByteArrayObject *self, PyObject *iterable_of_bytes) + /*[clinic end generated code: output=a8516370bf68ae08 input=aba6b1f9b30fcb8e]*/ + { ++ PyObject *ret; ++ + self->ob_exports++; // this protects `self` from being cleared/resized if `iterable_of_bytes` is a custom iterator +- PyObject* ret = stringlib_bytes_join((PyObject*)self, iterable_of_bytes); ++ ret = stringlib_bytes_join((PyObject*)self, iterable_of_bytes); + self->ob_exports--; // unexport `self` + return ret; + } +diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c +index bc53089..9628807 100644 +--- a/Objects/bytesobject.c ++++ b/Objects/bytesobject.c +@@ -649,6 +649,9 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, + Py_ssize_t len = 0; + char onechar; /* For byte_converter() */ + Py_ssize_t alloc; ++#ifndef NDEBUG ++ char *before; ++#endif + + fmt++; + if (*fmt == '%') { +@@ -961,7 +964,7 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, + goto error; + } + #ifndef NDEBUG +- char *before = res; ++ before = res; + #endif + + /* Write the sign if needed */ +@@ -1303,6 +1306,8 @@ _PyBytes_Find(const char *haystack, Py_ssize_t len_haystack, + const char *needle, Py_ssize_t len_needle, + Py_ssize_t offset) + { ++ Py_ssize_t res; ++ + assert(len_haystack >= 0); + assert(len_needle >= 0); + // Extra checks because stringlib_find accesses haystack[len_haystack]. +@@ -1313,10 +1318,11 @@ _PyBytes_Find(const char *haystack, Py_ssize_t len_haystack, + return -1; + } + assert(len_haystack >= 1); +- Py_ssize_t res = stringlib_find(haystack, len_haystack - 1, +- needle, len_needle, offset); ++ res = stringlib_find(haystack, len_haystack - 1, ++ needle, len_needle, offset); + if (res == -1) { +- Py_ssize_t last_align = len_haystack - len_needle; ++ Py_ssize_t last_align; ++ last_align = len_haystack - len_needle; + if (memcmp(haystack + last_align, needle, len_needle) == 0) { + return offset + last_align; + } +@@ -3613,10 +3619,11 @@ _PyBytes_Repeat(char* dest, Py_ssize_t len_dest, + memset(dest, src[0], len_dest); + } + else { ++ Py_ssize_t copied; + if (src != dest) { + memcpy(dest, src, len_src); + } +- Py_ssize_t copied = len_src; ++ copied = len_src; + while (copied < len_dest) { + Py_ssize_t bytes_to_copy = Py_MIN(copied, len_dest - copied); + memcpy(dest + copied, dest, bytes_to_copy); +diff --git a/Objects/call.c b/Objects/call.c +index 678d162..8afba25 100644 +--- a/Objects/call.c ++++ b/Objects/call.c +@@ -118,6 +118,10 @@ _PyObject_FastCallDictTstate(PyThreadState *tstate, PyObject *callable, + PyObject *const *args, size_t nargsf, + PyObject *kwargs) + { ++ Py_ssize_t nargs; ++ vectorcallfunc func; ++ PyObject *res; ++ + assert(callable != NULL); + + /* PyObject_VectorcallDict() must not be called with an exception set, +@@ -125,18 +129,17 @@ _PyObject_FastCallDictTstate(PyThreadState *tstate, PyObject *callable, + caller loses its exception */ + assert(!_PyErr_Occurred(tstate)); + +- Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); ++ nargs = PyVectorcall_NARGS(nargsf); + assert(nargs >= 0); + assert(nargs == 0 || args != NULL); + assert(kwargs == NULL || PyDict_Check(kwargs)); + +- vectorcallfunc func = _PyVectorcall_Function(callable); ++ func = _PyVectorcall_Function(callable); + if (func == NULL) { + /* Use tp_call instead */ + return _PyObject_MakeTpCall(tstate, callable, args, nargs, kwargs); + } + +- PyObject *res; + if (kwargs == NULL || PyDict_GET_SIZE(kwargs) == 0) { + res = func(callable, args, nargsf, NULL); + } +@@ -171,13 +174,18 @@ _PyObject_MakeTpCall(PyThreadState *tstate, PyObject *callable, + PyObject *const *args, Py_ssize_t nargs, + PyObject *keywords) + { ++ ternaryfunc call; ++ PyObject *argstuple; ++ PyObject *kwdict; ++ PyObject *result; ++ + assert(nargs >= 0); + assert(nargs == 0 || args != NULL); + assert(keywords == NULL || PyTuple_Check(keywords) || PyDict_Check(keywords)); + + /* Slow path: build a temporary tuple for positional arguments and a + * temporary dictionary for keyword arguments (if any) */ +- ternaryfunc call = Py_TYPE(callable)->tp_call; ++ call = Py_TYPE(callable)->tp_call; + if (call == NULL) { + _PyErr_Format(tstate, PyExc_TypeError, + "'%.200s' object is not callable", +@@ -185,12 +193,11 @@ _PyObject_MakeTpCall(PyThreadState *tstate, PyObject *callable, + return NULL; + } + +- PyObject *argstuple = _PyTuple_FromArray(args, nargs); ++ argstuple = _PyTuple_FromArray(args, nargs); + if (argstuple == NULL) { + return NULL; + } + +- PyObject *kwdict; + if (keywords == NULL || PyDict_Check(keywords)) { + kwdict = keywords; + } +@@ -208,7 +215,7 @@ _PyObject_MakeTpCall(PyThreadState *tstate, PyObject *callable, + } + } + +- PyObject *result = NULL; ++ result = NULL; + if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object") == 0) + { + result = _PyCFunctionWithKeywords_TrampolineCall( +@@ -236,9 +243,14 @@ static PyObject * + _PyVectorcall_Call(PyThreadState *tstate, vectorcallfunc func, + PyObject *callable, PyObject *tuple, PyObject *kwargs) + { ++ Py_ssize_t nargs; ++ PyObject *const *args; ++ PyObject *kwnames; ++ PyObject *result; ++ + assert(func != NULL); + +- Py_ssize_t nargs = PyTuple_GET_SIZE(tuple); ++ nargs = PyTuple_GET_SIZE(tuple); + + /* Fast path for no keywords */ + if (kwargs == NULL || PyDict_GET_SIZE(kwargs) == 0) { +@@ -246,15 +258,13 @@ _PyVectorcall_Call(PyThreadState *tstate, vectorcallfunc func, + } + + /* Convert arguments & call */ +- PyObject *const *args; +- PyObject *kwnames; + args = _PyStack_UnpackDict(tstate, + _PyTuple_ITEMS(tuple), nargs, + kwargs, &kwnames); + if (args == NULL) { + return NULL; + } +- PyObject *result = func(callable, args, ++ result = func(callable, args, + nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames); + _PyStack_UnpackDict_Free(args, nargs, kwnames); + +@@ -265,11 +275,15 @@ _PyVectorcall_Call(PyThreadState *tstate, vectorcallfunc func, + PyObject * + PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs) + { +- PyThreadState *tstate = _PyThreadState_GET(); ++ PyThreadState *tstate; ++ Py_ssize_t offset; ++ vectorcallfunc func; ++ ++ tstate = _PyThreadState_GET(); + + /* get vectorcallfunc as in _PyVectorcall_Function, but without + * the Py_TPFLAGS_HAVE_VECTORCALL check */ +- Py_ssize_t offset = Py_TYPE(callable)->tp_vectorcall_offset; ++ offset = Py_TYPE(callable)->tp_vectorcall_offset; + if (offset <= 0) { + _PyErr_Format(tstate, PyExc_TypeError, + "'%.200s' object does not support vectorcall", +@@ -278,7 +292,6 @@ PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs) + } + assert(PyCallable_Check(callable)); + +- vectorcallfunc func; + memcpy(&func, (char *) callable + offset, sizeof(func)); + if (func == NULL) { + _PyErr_Format(tstate, PyExc_TypeError, +@@ -315,6 +328,7 @@ _PyObject_Call(PyThreadState *tstate, PyObject *callable, + { + ternaryfunc call; + PyObject *result; ++ vectorcallfunc vector_func; + + /* PyObject_Call() must not be called with an exception set, + because it can clear it (directly or indirectly) and so the +@@ -323,7 +337,7 @@ _PyObject_Call(PyThreadState *tstate, PyObject *callable, + assert(PyTuple_Check(args)); + assert(kwargs == NULL || PyDict_Check(kwargs)); + +- vectorcallfunc vector_func = _PyVectorcall_Function(callable); ++ vector_func = _PyVectorcall_Function(callable); + if (vector_func != NULL) { + return _PyVectorcall_Call(tstate, vector_func, callable, args, kwargs); + } +@@ -367,12 +381,16 @@ PyCFunction_Call(PyObject *callable, PyObject *args, PyObject *kwargs) + PyObject * + PyObject_CallOneArg(PyObject *func, PyObject *arg) + { +- assert(arg != NULL); + PyObject *_args[2]; +- PyObject **args = _args + 1; // For PY_VECTORCALL_ARGUMENTS_OFFSET ++ PyObject **args; ++ PyThreadState *tstate; ++ size_t nargsf; ++ ++ assert(arg != NULL); ++ args = _args + 1; // For PY_VECTORCALL_ARGUMENTS_OFFSET + args[0] = arg; +- PyThreadState *tstate = _PyThreadState_GET(); +- size_t nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET; ++ tstate = _PyThreadState_GET(); ++ nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET; + return _PyObject_VectorcallTstate(tstate, func, args, nargsf, NULL); + } + +@@ -383,11 +401,15 @@ PyObject * + _PyFunction_Vectorcall(PyObject *func, PyObject* const* stack, + size_t nargsf, PyObject *kwnames) + { ++ PyFunctionObject *f; ++ Py_ssize_t nargs; ++ PyThreadState *tstate; ++ + assert(PyFunction_Check(func)); +- PyFunctionObject *f = (PyFunctionObject *)func; +- Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); ++ f = (PyFunctionObject *)func; ++ nargs = PyVectorcall_NARGS(nargsf); + assert(nargs >= 0); +- PyThreadState *tstate = _PyThreadState_GET(); ++ tstate = _PyThreadState_GET(); + assert(nargs == 0 || stack != NULL); + if (((PyCodeObject *)f->func_code)->co_flags & CO_OPTIMIZED) { + return _PyEval_Vector(tstate, f, NULL, stack, nargs, kwnames); +@@ -456,12 +478,14 @@ PyObject * + _PyObject_Call_Prepend(PyThreadState *tstate, PyObject *callable, + PyObject *obj, PyObject *args, PyObject *kwargs) + { +- assert(PyTuple_Check(args)); +- + PyObject *small_stack[_PY_FASTCALL_SMALL_STACK]; + PyObject **stack; ++ Py_ssize_t argcount; ++ PyObject *result; + +- Py_ssize_t argcount = PyTuple_GET_SIZE(args); ++ assert(PyTuple_Check(args)); ++ ++ argcount = PyTuple_GET_SIZE(args); + if (argcount + 1 <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { + stack = small_stack; + } +@@ -479,7 +503,7 @@ _PyObject_Call_Prepend(PyThreadState *tstate, PyObject *callable, + _PyTuple_ITEMS(args), + argcount * sizeof(PyObject *)); + +- PyObject *result = _PyObject_FastCallDictTstate(tstate, callable, ++ result = _PyObject_FastCallDictTstate(tstate, callable, + stack, argcount + 1, + kwargs); + if (stack != small_stack) { +@@ -584,10 +608,11 @@ PyObject * + _PyObject_CallFunction_SizeT(PyObject *callable, const char *format, ...) + { + PyThreadState *tstate = _PyThreadState_GET(); +- + va_list va; ++ PyObject *result; ++ + va_start(va, format); +- PyObject *result = _PyObject_CallFunctionVa(tstate, callable, format, va, 1); ++ result = _PyObject_CallFunctionVa(tstate, callable, format, va, 1); + va_end(va); + + return result; +@@ -612,19 +637,21 @@ PyObject * + PyObject_CallMethod(PyObject *obj, const char *name, const char *format, ...) + { + PyThreadState *tstate = _PyThreadState_GET(); ++ PyObject *callable; ++ va_list va; ++ PyObject *retval; + + if (obj == NULL || name == NULL) { + return null_error(tstate); + } + +- PyObject *callable = PyObject_GetAttrString(obj, name); ++ callable = PyObject_GetAttrString(obj, name); + if (callable == NULL) { + return NULL; + } + +- va_list va; + va_start(va, format); +- PyObject *retval = callmethod(tstate, callable, format, va, 0); ++ retval = callmethod(tstate, callable, format, va, 0); + va_end(va); + + Py_DECREF(callable); +@@ -639,18 +666,21 @@ PyObject * + PyEval_CallMethod(PyObject *obj, const char *name, const char *format, ...) + { + PyThreadState *tstate = _PyThreadState_GET(); ++ PyObject *callable; ++ va_list va; ++ PyObject *retval; ++ + if (obj == NULL || name == NULL) { + return null_error(tstate); + } + +- PyObject *callable = PyObject_GetAttrString(obj, name); ++ callable = PyObject_GetAttrString(obj, name); + if (callable == NULL) { + return NULL; + } + +- va_list va; + va_start(va, format); +- PyObject *retval = callmethod(tstate, callable, format, va, 0); ++ retval = callmethod(tstate, callable, format, va, 0); + va_end(va); + + Py_DECREF(callable); +@@ -663,18 +693,21 @@ _PyObject_CallMethod(PyObject *obj, PyObject *name, + const char *format, ...) + { + PyThreadState *tstate = _PyThreadState_GET(); ++ PyObject *callable; ++ va_list va; ++ PyObject *retval; ++ + if (obj == NULL || name == NULL) { + return null_error(tstate); + } + +- PyObject *callable = PyObject_GetAttr(obj, name); ++ callable = PyObject_GetAttr(obj, name); + if (callable == NULL) { + return NULL; + } + +- va_list va; + va_start(va, format); +- PyObject *retval = callmethod(tstate, callable, format, va, 1); ++ retval = callmethod(tstate, callable, format, va, 1); + va_end(va); + + Py_DECREF(callable); +@@ -687,18 +720,21 @@ _PyObject_CallMethodId(PyObject *obj, _Py_Identifier *name, + const char *format, ...) + { + PyThreadState *tstate = _PyThreadState_GET(); ++ PyObject *callable; ++ va_list va; ++ PyObject *retval; ++ + if (obj == NULL || name == NULL) { + return null_error(tstate); + } + +- PyObject *callable = _PyObject_GetAttrId(obj, name); ++ callable = _PyObject_GetAttrId(obj, name); + if (callable == NULL) { + return NULL; + } + +- va_list va; + va_start(va, format); +- PyObject *retval = callmethod(tstate, callable, format, va, 0); ++ retval = callmethod(tstate, callable, format, va, 0); + va_end(va); + + Py_DECREF(callable); +@@ -710,8 +746,10 @@ PyObject * _PyObject_CallMethodFormat(PyThreadState *tstate, PyObject *callable, + const char *format, ...) + { + va_list va; ++ PyObject *retval; ++ + va_start(va, format); +- PyObject *retval = callmethod(tstate, callable, format, va, 0); ++ retval = callmethod(tstate, callable, format, va, 0); + va_end(va); + return retval; + } +@@ -722,18 +760,21 @@ _PyObject_CallMethod_SizeT(PyObject *obj, const char *name, + const char *format, ...) + { + PyThreadState *tstate = _PyThreadState_GET(); ++ PyObject *callable; ++ va_list va; ++ PyObject *retval; ++ + if (obj == NULL || name == NULL) { + return null_error(tstate); + } + +- PyObject *callable = PyObject_GetAttrString(obj, name); ++ callable = PyObject_GetAttrString(obj, name); + if (callable == NULL) { + return NULL; + } + +- va_list va; + va_start(va, format); +- PyObject *retval = callmethod(tstate, callable, format, va, 1); ++ retval = callmethod(tstate, callable, format, va, 1); + va_end(va); + + Py_DECREF(callable); +@@ -746,18 +787,21 @@ _PyObject_CallMethodId_SizeT(PyObject *obj, _Py_Identifier *name, + const char *format, ...) + { + PyThreadState *tstate = _PyThreadState_GET(); ++ PyObject *callable; ++ va_list va; ++ PyObject *retval; ++ + if (obj == NULL || name == NULL) { + return null_error(tstate); + } + +- PyObject *callable = _PyObject_GetAttrId(obj, name); ++ callable = _PyObject_GetAttrId(obj, name); + if (callable == NULL) { + return NULL; + } + +- va_list va; + va_start(va, format); +- PyObject *retval = callmethod(tstate, callable, format, va, 1); ++ retval = callmethod(tstate, callable, format, va, 1); + va_end(va); + + Py_DECREF(callable); +@@ -829,14 +873,19 @@ PyObject * + PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, + size_t nargsf, PyObject *kwnames) + { ++ PyThreadState *tstate; ++ PyObject *callable; ++ int unbound; ++ PyObject *result; ++ + assert(name != NULL); + assert(args != NULL); + assert(PyVectorcall_NARGS(nargsf) >= 1); + +- PyThreadState *tstate = _PyThreadState_GET(); +- PyObject *callable = NULL; ++ tstate = _PyThreadState_GET(); ++ callable = NULL; + /* Use args[0] as "self" argument */ +- int unbound = _PyObject_GetMethod(args[0], name, &callable); ++ unbound = _PyObject_GetMethod(args[0], name, &callable); + if (callable == NULL) { + return NULL; + } +@@ -852,7 +901,7 @@ PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, + args++; + nargsf--; + } +- PyObject *result = _PyObject_VectorcallTstate(tstate, callable, ++ result = _PyObject_VectorcallTstate(tstate, callable, + args, nargsf, kwnames); + Py_DECREF(callable); + return result; +@@ -863,20 +912,24 @@ PyObject * + PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...) + { + PyThreadState *tstate = _PyThreadState_GET(); ++ PyObject *callable; ++ int is_method; ++ va_list vargs; ++ PyObject *result; ++ + if (obj == NULL || name == NULL) { + return null_error(tstate); + } + +- PyObject *callable = NULL; +- int is_method = _PyObject_GetMethod(obj, name, &callable); ++ callable = NULL; ++ is_method = _PyObject_GetMethod(obj, name, &callable); + if (callable == NULL) { + return NULL; + } + obj = is_method ? obj : NULL; + +- va_list vargs; + va_start(vargs, name); +- PyObject *result = object_vacall(tstate, obj, callable, vargs); ++ result = object_vacall(tstate, obj, callable, vargs); + va_end(vargs); + + Py_DECREF(callable); +@@ -888,25 +941,30 @@ PyObject * + _PyObject_CallMethodIdObjArgs(PyObject *obj, _Py_Identifier *name, ...) + { + PyThreadState *tstate = _PyThreadState_GET(); ++ PyObject *oname; ++ PyObject *callable; ++ int is_method; ++ va_list vargs; ++ PyObject *result; ++ + if (obj == NULL || name == NULL) { + return null_error(tstate); + } + +- PyObject *oname = _PyUnicode_FromId(name); /* borrowed */ ++ oname = _PyUnicode_FromId(name); /* borrowed */ + if (!oname) { + return NULL; + } + +- PyObject *callable = NULL; +- int is_method = _PyObject_GetMethod(obj, oname, &callable); ++ callable = NULL; ++ is_method = _PyObject_GetMethod(obj, oname, &callable); + if (callable == NULL) { + return NULL; + } + obj = is_method ? obj : NULL; + +- va_list vargs; + va_start(vargs, name); +- PyObject *result = object_vacall(tstate, obj, callable, vargs); ++ result = object_vacall(tstate, obj, callable, vargs); + va_end(vargs); + + Py_DECREF(callable); +@@ -960,28 +1018,39 @@ _PyStack_UnpackDict(PyThreadState *tstate, + PyObject *const *args, Py_ssize_t nargs, + PyObject *kwargs, PyObject **p_kwnames) + { ++ Py_ssize_t nkwargs; ++ Py_ssize_t maxnargs; ++ PyObject **stack; ++ PyObject *kwnames; ++ PyObject **kwstack; ++ Py_ssize_t pos; ++ Py_ssize_t i; ++ PyObject *key; ++ PyObject *value; ++ unsigned long keys_are_strings; ++ + assert(nargs >= 0); + assert(kwargs != NULL); + assert(PyDict_Check(kwargs)); + +- Py_ssize_t nkwargs = PyDict_GET_SIZE(kwargs); ++ nkwargs = PyDict_GET_SIZE(kwargs); + /* Check for overflow in the PyMem_Malloc() call below. The subtraction + * in this check cannot overflow: both maxnargs and nkwargs are + * non-negative signed integers, so their difference fits in the type. */ +- Py_ssize_t maxnargs = PY_SSIZE_T_MAX / sizeof(args[0]) - 1; ++ maxnargs = PY_SSIZE_T_MAX / sizeof(args[0]) - 1; + if (nargs > maxnargs - nkwargs) { + _PyErr_NoMemory(tstate); + return NULL; + } + + /* Add 1 to support PY_VECTORCALL_ARGUMENTS_OFFSET */ +- PyObject **stack = PyMem_Malloc((1 + nargs + nkwargs) * sizeof(args[0])); ++ stack = PyMem_Malloc((1 + nargs + nkwargs) * sizeof(args[0])); + if (stack == NULL) { + _PyErr_NoMemory(tstate); + return NULL; + } + +- PyObject *kwnames = PyTuple_New(nkwargs); ++ kwnames = PyTuple_New(nkwargs); + if (kwnames == NULL) { + PyMem_Free(stack); + return NULL; +@@ -990,18 +1059,18 @@ _PyStack_UnpackDict(PyThreadState *tstate, + stack++; /* For PY_VECTORCALL_ARGUMENTS_OFFSET */ + + /* Copy positional arguments */ +- for (Py_ssize_t i = 0; i < nargs; i++) { ++ for (i = 0; i < nargs; i++) { + Py_INCREF(args[i]); + stack[i] = args[i]; + } + +- PyObject **kwstack = stack + nargs; ++ kwstack = stack + nargs; + /* This loop doesn't support lookup function mutating the dictionary + to change its size. It's a deliberate choice for speed, this function is + called in the performance critical hot code. */ +- Py_ssize_t pos = 0, i = 0; +- PyObject *key, *value; +- unsigned long keys_are_strings = Py_TPFLAGS_UNICODE_SUBCLASS; ++ pos = 0; ++ i = 0; ++ keys_are_strings = Py_TPFLAGS_UNICODE_SUBCLASS; + while (PyDict_Next(kwargs, &pos, &key, &value)) { + keys_are_strings &= Py_TYPE(key)->tp_flags; + Py_INCREF(key); +@@ -1032,7 +1101,8 @@ _PyStack_UnpackDict_Free(PyObject *const *stack, Py_ssize_t nargs, + PyObject *kwnames) + { + Py_ssize_t n = PyTuple_GET_SIZE(kwnames) + nargs; +- for (Py_ssize_t i = 0; i < n; i++) { ++ Py_ssize_t i; ++ for (i = 0; i < n; i++) { + Py_DECREF(stack[i]); + } + PyMem_Free((PyObject **)stack - 1); +diff --git a/Objects/classobject.c b/Objects/classobject.c +index b9708ba..86bab3d 100644 +--- a/Objects/classobject.c ++++ b/Objects/classobject.c +@@ -42,33 +42,43 @@ static PyObject * + method_vectorcall(PyObject *method, PyObject *const *args, + size_t nargsf, PyObject *kwnames) + { +- assert(Py_IS_TYPE(method, &PyMethod_Type)); ++ PyThreadState *tstate; ++ PyObject *self; ++ PyObject *func; ++ Py_ssize_t nargs; ++ PyObject *result; + +- PyThreadState *tstate = _PyThreadState_GET(); +- PyObject *self = PyMethod_GET_SELF(method); +- PyObject *func = PyMethod_GET_FUNCTION(method); +- Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); ++ assert(Py_IS_TYPE(method, &PyMethod_Type)); + +- PyObject *result; ++ tstate = _PyThreadState_GET(); ++ self = PyMethod_GET_SELF(method); ++ func = PyMethod_GET_FUNCTION(method); ++ nargs = PyVectorcall_NARGS(nargsf); + if (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET) { ++ PyObject **newargs; ++ PyObject *tmp; ++ + /* PY_VECTORCALL_ARGUMENTS_OFFSET is set, so we are allowed to mutate the vector */ +- PyObject **newargs = (PyObject**)args - 1; ++ newargs = (PyObject**)args - 1; + nargs += 1; +- PyObject *tmp = newargs[0]; ++ tmp = newargs[0]; + newargs[0] = self; + result = _PyObject_VectorcallTstate(tstate, func, newargs, + nargs, kwnames); + newargs[0] = tmp; + } + else { +- Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); +- Py_ssize_t totalargs = nargs + nkwargs; ++ Py_ssize_t nkwargs; ++ Py_ssize_t totalargs; ++ PyObject *newargs_stack[_PY_FASTCALL_SMALL_STACK]; ++ PyObject **newargs; ++ ++ nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); ++ totalargs = nargs + nkwargs; + if (totalargs == 0) { + return _PyObject_VectorcallTstate(tstate, func, &self, 1, NULL); + } + +- PyObject *newargs_stack[_PY_FASTCALL_SMALL_STACK]; +- PyObject **newargs; + if (totalargs <= (Py_ssize_t)Py_ARRAY_LENGTH(newargs_stack) - 1) { + newargs = newargs_stack; + } +@@ -104,11 +114,13 @@ method_vectorcall(PyObject *method, PyObject *const *args, + PyObject * + PyMethod_New(PyObject *func, PyObject *self) + { ++ PyMethodObject *im; ++ + if (self == NULL) { + PyErr_BadInternalCall(); + return NULL; + } +- PyMethodObject *im = PyObject_GC_New(PyMethodObject, &PyMethod_Type); ++ im = PyObject_GC_New(PyMethodObject, &PyMethod_Type); + if (im == NULL) { + return NULL; + } +@@ -323,6 +335,7 @@ method_traverse(PyMethodObject *im, visitproc visit, void *arg) + + PyTypeObject PyMethod_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) ++#if !defined(MS_WINDOWS) || !defined(_MSC_VER) || _MSC_VER >= 1300 + .tp_name = "method", + .tp_basicsize = sizeof(PyMethodObject), + .tp_dealloc = (destructor)method_dealloc, +@@ -342,6 +355,7 @@ PyTypeObject PyMethod_Type = { + .tp_members = method_memberlist, + .tp_getset = method_getset, + .tp_new = method_new, ++#endif + }; + + /* ------------------------------------------------------------------------ +@@ -527,6 +541,7 @@ instancemethod_new_impl(PyTypeObject *type, PyObject *function) + + PyTypeObject PyInstanceMethod_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) ++#if !defined(MS_WINDOWS) || !defined(_MSC_VER) || _MSC_VER >= 1300 + .tp_name = "instancemethod", + .tp_basicsize = sizeof(PyInstanceMethodObject), + .tp_dealloc = instancemethod_dealloc, +@@ -542,4 +557,47 @@ PyTypeObject PyInstanceMethod_Type = { + .tp_getset = instancemethod_getset, + .tp_descr_get = instancemethod_descr_get, + .tp_new = instancemethod_new, ++#endif + }; ++ ++#if defined(MS_WINDOWS) && defined(_MSC_VER) && _MSC_VER < 1300 ++void ++_PyClassObject_InitTypes(void) ++{ ++ PyMethod_Type.tp_name = "method"; ++ PyMethod_Type.tp_basicsize = sizeof(PyMethodObject); ++ PyMethod_Type.tp_dealloc = (destructor)method_dealloc; ++ PyMethod_Type.tp_vectorcall_offset = offsetof(PyMethodObject, vectorcall); ++ PyMethod_Type.tp_repr = (reprfunc)method_repr; ++ PyMethod_Type.tp_hash = (hashfunc)method_hash; ++ PyMethod_Type.tp_call = PyVectorcall_Call; ++ PyMethod_Type.tp_getattro = method_getattro; ++ PyMethod_Type.tp_setattro = PyObject_GenericSetAttr; ++ PyMethod_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | ++ Py_TPFLAGS_HAVE_VECTORCALL; ++ PyMethod_Type.tp_doc = method_new__doc__; ++ PyMethod_Type.tp_traverse = (traverseproc)method_traverse; ++ PyMethod_Type.tp_richcompare = method_richcompare; ++ PyMethod_Type.tp_weaklistoffset = offsetof(PyMethodObject, im_weakreflist); ++ PyMethod_Type.tp_methods = method_methods; ++ PyMethod_Type.tp_members = method_memberlist; ++ PyMethod_Type.tp_getset = method_getset; ++ PyMethod_Type.tp_new = method_new; ++ ++ PyInstanceMethod_Type.tp_name = "instancemethod"; ++ PyInstanceMethod_Type.tp_basicsize = sizeof(PyInstanceMethodObject); ++ PyInstanceMethod_Type.tp_dealloc = instancemethod_dealloc; ++ PyInstanceMethod_Type.tp_repr = (reprfunc)instancemethod_repr; ++ PyInstanceMethod_Type.tp_call = instancemethod_call; ++ PyInstanceMethod_Type.tp_getattro = instancemethod_getattro; ++ PyInstanceMethod_Type.tp_setattro = PyObject_GenericSetAttr; ++ PyInstanceMethod_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC; ++ PyInstanceMethod_Type.tp_doc = instancemethod_new__doc__; ++ PyInstanceMethod_Type.tp_traverse = instancemethod_traverse; ++ PyInstanceMethod_Type.tp_richcompare = instancemethod_richcompare; ++ PyInstanceMethod_Type.tp_members = instancemethod_memberlist; ++ PyInstanceMethod_Type.tp_getset = instancemethod_getset; ++ PyInstanceMethod_Type.tp_descr_get = instancemethod_descr_get; ++ PyInstanceMethod_Type.tp_new = instancemethod_new; ++} ++#endif +diff --git a/Objects/clinic/bytearrayobject.c.h b/Objects/clinic/bytearrayobject.c.h +index 0b5c01a..ab2bea8 100644 +--- a/Objects/clinic/bytearrayobject.c.h ++++ b/Objects/clinic/bytearrayobject.c.h +@@ -14,6 +14,8 @@ bytearray___init__(PyObject *self, PyObject *args, PyObject *kwargs) + static _PyArg_Parser _parser = {NULL, _keywords, "bytearray", 0}; + PyObject *argsbuf[3]; + PyObject * const *fastargs; ++ Py_ssize_t encoding_length; ++ Py_ssize_t errors_length; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; + PyObject *arg = NULL; +@@ -38,7 +40,6 @@ bytearray___init__(PyObject *self, PyObject *args, PyObject *kwargs) + _PyArg_BadArgument("bytearray", "argument 'encoding'", "str", fastargs[1]); + goto exit; + } +- Py_ssize_t encoding_length; + encoding = PyUnicode_AsUTF8AndSize(fastargs[1], &encoding_length); + if (encoding == NULL) { + goto exit; +@@ -55,7 +56,6 @@ bytearray___init__(PyObject *self, PyObject *args, PyObject *kwargs) + _PyArg_BadArgument("bytearray", "argument 'errors'", "str", fastargs[2]); + goto exit; + } +- Py_ssize_t errors_length; + errors = PyUnicode_AsUTF8AndSize(fastargs[2], &errors_length); + if (errors == NULL) { + goto exit; +@@ -846,6 +846,8 @@ bytearray_decode(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t narg + static const char * const _keywords[] = {"encoding", "errors", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "decode", 0}; + PyObject *argsbuf[2]; ++ Py_ssize_t encoding_length; ++ Py_ssize_t errors_length; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + const char *encoding = NULL; + const char *errors = NULL; +@@ -862,7 +864,6 @@ bytearray_decode(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t narg + _PyArg_BadArgument("decode", "argument 'encoding'", "str", args[0]); + goto exit; + } +- Py_ssize_t encoding_length; + encoding = PyUnicode_AsUTF8AndSize(args[0], &encoding_length); + if (encoding == NULL) { + goto exit; +@@ -879,7 +880,6 @@ bytearray_decode(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t narg + _PyArg_BadArgument("decode", "argument 'errors'", "str", args[1]); + goto exit; + } +- Py_ssize_t errors_length; + errors = PyUnicode_AsUTF8AndSize(args[1], &errors_length); + if (errors == NULL) { + goto exit; +diff --git a/Objects/clinic/bytesobject.c.h b/Objects/clinic/bytesobject.c.h +index 7e85720..64c63bc 100644 +--- a/Objects/clinic/bytesobject.c.h ++++ b/Objects/clinic/bytesobject.c.h +@@ -640,6 +640,8 @@ bytes_decode(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs, PyObj + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + const char *encoding = NULL; + const char *errors = NULL; ++ Py_ssize_t encoding_length; ++ Py_ssize_t errors_length; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + if (!args) { +@@ -653,7 +655,6 @@ bytes_decode(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs, PyObj + _PyArg_BadArgument("decode", "argument 'encoding'", "str", args[0]); + goto exit; + } +- Py_ssize_t encoding_length; + encoding = PyUnicode_AsUTF8AndSize(args[0], &encoding_length); + if (encoding == NULL) { + goto exit; +@@ -670,7 +671,6 @@ bytes_decode(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs, PyObj + _PyArg_BadArgument("decode", "argument 'errors'", "str", args[1]); + goto exit; + } +- Py_ssize_t errors_length; + errors = PyUnicode_AsUTF8AndSize(args[1], &errors_length); + if (errors == NULL) { + goto exit; +@@ -845,6 +845,8 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) + PyObject *x = NULL; + const char *encoding = NULL; + const char *errors = NULL; ++ Py_ssize_t encoding_length; ++ Py_ssize_t errors_length; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 3, 0, argsbuf); + if (!fastargs) { +@@ -864,7 +866,6 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) + _PyArg_BadArgument("bytes", "argument 'encoding'", "str", fastargs[1]); + goto exit; + } +- Py_ssize_t encoding_length; + encoding = PyUnicode_AsUTF8AndSize(fastargs[1], &encoding_length); + if (encoding == NULL) { + goto exit; +@@ -881,7 +882,6 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) + _PyArg_BadArgument("bytes", "argument 'errors'", "str", fastargs[2]); + goto exit; + } +- Py_ssize_t errors_length; + errors = PyUnicode_AsUTF8AndSize(fastargs[2], &errors_length); + if (errors == NULL) { + goto exit; +diff --git a/Objects/codeobject.c b/Objects/codeobject.c +index c4a0d9a..4d62228 100644 +--- a/Objects/codeobject.c ++++ b/Objects/codeobject.c +@@ -54,7 +54,9 @@ intern_strings(PyObject *tuple) + static int + intern_string_constants(PyObject *tuple, int *modified) + { +- for (Py_ssize_t i = PyTuple_GET_SIZE(tuple); --i >= 0; ) { ++ Py_ssize_t i; ++ ++ for (i = PyTuple_GET_SIZE(tuple); --i >= 0; ) { + PyObject *v = PyTuple_GET_ITEM(tuple, i); + if (PyUnicode_CheckExact(v)) { + if (PyUnicode_READY(v) == -1) { +@@ -80,10 +82,12 @@ intern_string_constants(PyObject *tuple, int *modified) + else if (PyFrozenSet_CheckExact(v)) { + PyObject *w = v; + PyObject *tmp = PySequence_Tuple(v); ++ int tmp_modified; ++ + if (tmp == NULL) { + return -1; + } +- int tmp_modified = 0; ++ tmp_modified = 0; + if (intern_string_constants(tmp, &tmp_modified) < 0) { + Py_DECREF(tmp); + return -1; +@@ -174,7 +178,9 @@ get_localsplus_counts(PyObject *names, PyObject *kinds, + int ncellvars = 0; + int nfreevars = 0; + Py_ssize_t nlocalsplus = PyTuple_GET_SIZE(names); +- for (int i = 0; i < nlocalsplus; i++) { ++ int i; ++ ++ for (i = 0; i < nlocalsplus; i++) { + _PyLocals_Kind kind = _PyLocals_GetKind(kinds, i); + if (kind & CO_FAST_LOCAL) { + nlocals += 1; +@@ -208,17 +214,22 @@ static PyObject * + get_localsplus_names(PyCodeObject *co, _PyLocals_Kind kind, int num) + { + PyObject *names = PyTuple_New(num); ++ int index; ++ int offset; + if (names == NULL) { + return NULL; + } +- int index = 0; +- for (int offset = 0; offset < co->co_nlocalsplus; offset++) { +- _PyLocals_Kind k = _PyLocals_GetKind(co->co_localspluskinds, offset); ++ index = 0; ++ for (offset = 0; offset < co->co_nlocalsplus; offset++) { ++ _PyLocals_Kind k; ++ PyObject *name; ++ ++ k = _PyLocals_GetKind(co->co_localspluskinds, offset); + if ((k & kind) == 0) { + continue; + } + assert(index < num); +- PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, offset); ++ name = PyTuple_GET_ITEM(co->co_localsplusnames, offset); + Py_INCREF(name); + PyTuple_SET_ITEM(names, index, name); + index += 1; +@@ -230,6 +241,9 @@ get_localsplus_names(PyCodeObject *co, _PyLocals_Kind kind, int num) + int + _PyCode_Validate(struct _PyCodeConstructor *con) + { ++ int nlocals; ++ int nplainlocals; ++ + /* Check argument types */ + if (con->argcount < con->posonlyargcount || con->posonlyargcount < 0 || + con->kwonlyargcount < 0 || +@@ -269,10 +283,9 @@ _PyCode_Validate(struct _PyCodeConstructor *con) + /* Ensure that the co_varnames has enough names to cover the arg counts. + * Note that totalargs = nlocals - nplainlocals. We check nplainlocals + * here to avoid the possibility of overflow (however remote). */ +- int nlocals; + get_localsplus_counts(con->localsplusnames, con->localspluskinds, + &nlocals, NULL, NULL, NULL); +- int nplainlocals = nlocals - ++ nplainlocals = nlocals - + con->argcount - + con->kwonlyargcount - + ((con->flags & CO_VARARGS) != 0) - +@@ -290,6 +303,7 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) + { + int nlocalsplus = (int)PyTuple_GET_SIZE(con->localsplusnames); + int nlocals, nplaincellvars, ncellvars, nfreevars; ++ int entry_point; + get_localsplus_counts(con->localsplusnames, con->localspluskinds, + &nlocals, &nplaincellvars, &ncellvars, &nfreevars); + +@@ -341,7 +355,7 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) + co->_co_linearray = NULL; + memcpy(_PyCode_CODE(co), PyBytes_AS_STRING(con->code), + PyBytes_GET_SIZE(con->code)); +- int entry_point = 0; ++ entry_point = 0; + while (entry_point < Py_SIZE(co) && + _Py_OPCODE(_PyCode_CODE(co)[entry_point]) != RESUME) { + entry_point++; +@@ -403,28 +417,33 @@ remove_column_info(PyObject *locations) + int offset = 0; + const uint8_t *data = (const uint8_t *)PyBytes_AS_STRING(locations); + PyObject *res = PyBytes_FromStringAndSize(NULL, 32); ++ uint8_t *output; ++ Py_ssize_t write_offset; ++ + if (res == NULL) { + PyErr_NoMemory(); + return NULL; + } +- uint8_t *output = (uint8_t *)PyBytes_AS_STRING(res); ++ output = (uint8_t *)PyBytes_AS_STRING(res); + while (offset < PyBytes_GET_SIZE(locations)) { +- Py_ssize_t write_offset = output - (uint8_t *)PyBytes_AS_STRING(res); ++ int code; ++ write_offset = output - (uint8_t *)PyBytes_AS_STRING(res); + if (write_offset + 16 >= PyBytes_GET_SIZE(res)) { + if (_PyBytes_Resize(&res, PyBytes_GET_SIZE(res) * 2) < 0) { + return NULL; + } + output = (uint8_t *)PyBytes_AS_STRING(res) + write_offset; + } +- int code = (data[offset] >> 3) & 15; ++ code = (data[offset] >> 3) & 15; + if (code == PY_CODE_LOCATION_INFO_NONE) { + *output++ = data[offset]; + } + else { ++ int ldelta; + int blength = (data[offset] & 7)+1; + output += write_location_entry_start( + output, PY_CODE_LOCATION_INFO_NO_COLUMNS, blength); +- int ldelta = get_line_delta(&data[offset]); ++ ldelta = get_line_delta(&data[offset]); + output += write_signed_varint(output, ldelta); + } + offset++; +@@ -433,7 +452,7 @@ remove_column_info(PyObject *locations) + offset++; + } + } +- Py_ssize_t write_offset = output - (uint8_t *)PyBytes_AS_STRING(res); ++ write_offset = output - (uint8_t *)PyBytes_AS_STRING(res); + if (_PyBytes_Resize(&res, write_offset)) { + return NULL; + } +@@ -445,6 +464,10 @@ remove_column_info(PyObject *locations) + PyCodeObject * + _PyCode_New(struct _PyCodeConstructor *con) + { ++ PyObject *replacement_locations; ++ Py_ssize_t size; ++ PyCodeObject *co; ++ + /* Ensure that strings are ready Unicode string */ + if (PyUnicode_READY(con->name) < 0) { + return NULL; +@@ -466,7 +489,7 @@ _PyCode_New(struct _PyCodeConstructor *con) + return NULL; + } + +- PyObject *replacement_locations = NULL; ++ replacement_locations = NULL; + // Compact the linetable if we are opted out of debug + // ranges. + if (!_Py_GetConfig()->code_debug_ranges) { +@@ -477,8 +500,8 @@ _PyCode_New(struct _PyCodeConstructor *con) + con->linetable = replacement_locations; + } + +- Py_ssize_t size = PyBytes_GET_SIZE(con->code) / sizeof(_Py_CODEUNIT); +- PyCodeObject *co = PyObject_NewVar(PyCodeObject, &PyCode_Type, size); ++ size = PyBytes_GET_SIZE(con->code) / sizeof(_Py_CODEUNIT); ++ co = PyObject_NewVar(PyCodeObject, &PyCode_Type, size); + if (co == NULL) { + Py_XDECREF(replacement_locations); + PyErr_NoMemory(); +@@ -507,6 +530,18 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, + PyCodeObject *co = NULL; + PyObject *localsplusnames = NULL; + PyObject *localspluskinds = NULL; ++ int nvarnames; ++ int ncellvars; ++ int nfreevars; ++ int nlocalsplus; ++ int offset; ++ int i; ++ int j; ++ PyObject *local_name; ++ int argoffset; ++ int cmp; ++ _PyLocals_Kind kind; ++ struct _PyCodeConstructor con = {0}; + + if (varnames == NULL || !PyTuple_Check(varnames) || + cellvars == NULL || !PyTuple_Check(cellvars) || +@@ -517,10 +552,10 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, + } + + // Set the "fast locals plus" info. +- int nvarnames = (int)PyTuple_GET_SIZE(varnames); +- int ncellvars = (int)PyTuple_GET_SIZE(cellvars); +- int nfreevars = (int)PyTuple_GET_SIZE(freevars); +- int nlocalsplus = nvarnames + ncellvars + nfreevars; ++ nvarnames = (int)PyTuple_GET_SIZE(varnames); ++ ncellvars = (int)PyTuple_GET_SIZE(cellvars); ++ nfreevars = (int)PyTuple_GET_SIZE(freevars); ++ nlocalsplus = nvarnames + ncellvars + nfreevars; + localsplusnames = PyTuple_New(nlocalsplus); + if (localsplusnames == NULL) { + goto error; +@@ -529,18 +564,18 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, + if (localspluskinds == NULL) { + goto error; + } +- int offset = 0; +- for (int i = 0; i < nvarnames; i++, offset++) { +- PyObject *name = PyTuple_GET_ITEM(varnames, i); +- _Py_set_localsplus_info(offset, name, CO_FAST_LOCAL, ++ offset = 0; ++ for (i = 0; i < nvarnames; i++, offset++) { ++ local_name = PyTuple_GET_ITEM(varnames, i); ++ _Py_set_localsplus_info(offset, local_name, CO_FAST_LOCAL, + localsplusnames, localspluskinds); + } +- for (int i = 0; i < ncellvars; i++, offset++) { +- PyObject *name = PyTuple_GET_ITEM(cellvars, i); +- int argoffset = -1; +- for (int j = 0; j < nvarnames; j++) { +- int cmp = PyUnicode_Compare(PyTuple_GET_ITEM(varnames, j), +- name); ++ for (i = 0; i < ncellvars; i++, offset++) { ++ local_name = PyTuple_GET_ITEM(cellvars, i); ++ argoffset = -1; ++ for (j = 0; j < nvarnames; j++) { ++ cmp = PyUnicode_Compare(PyTuple_GET_ITEM(varnames, j), ++ local_name); + assert(!PyErr_Occurred()); + if (cmp == 0) { + argoffset = j; +@@ -551,16 +586,16 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, + // Merge the localsplus indices. + nlocalsplus -= 1; + offset -= 1; +- _PyLocals_Kind kind = _PyLocals_GetKind(localspluskinds, argoffset); ++ kind = _PyLocals_GetKind(localspluskinds, argoffset); + _PyLocals_SetKind(localspluskinds, argoffset, kind | CO_FAST_CELL); + continue; + } +- _Py_set_localsplus_info(offset, name, CO_FAST_CELL, ++ _Py_set_localsplus_info(offset, local_name, CO_FAST_CELL, + localsplusnames, localspluskinds); + } +- for (int i = 0; i < nfreevars; i++, offset++) { +- PyObject *name = PyTuple_GET_ITEM(freevars, i); +- _Py_set_localsplus_info(offset, name, CO_FAST_FREE, ++ for (i = 0; i < nfreevars; i++, offset++) { ++ local_name = PyTuple_GET_ITEM(freevars, i); ++ _Py_set_localsplus_info(offset, local_name, CO_FAST_FREE, + localsplusnames, localspluskinds); + } + // If any cells were args then nlocalsplus will have shrunk. +@@ -571,30 +606,22 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, + } + } + +- struct _PyCodeConstructor con = { +- .filename = filename, +- .name = name, +- .qualname = qualname, +- .flags = flags, +- +- .code = code, +- .firstlineno = firstlineno, +- .linetable = linetable, +- +- .consts = consts, +- .names = names, +- +- .localsplusnames = localsplusnames, +- .localspluskinds = localspluskinds, +- +- .argcount = argcount, +- .posonlyargcount = posonlyargcount, +- .kwonlyargcount = kwonlyargcount, +- +- .stacksize = stacksize, +- +- .exceptiontable = exceptiontable, +- }; ++ con.filename = filename; ++ con.name = name; ++ con.qualname = qualname; ++ con.flags = flags; ++ con.code = code; ++ con.firstlineno = firstlineno; ++ con.linetable = linetable; ++ con.consts = consts; ++ con.names = names; ++ con.localsplusnames = localsplusnames; ++ con.localspluskinds = localspluskinds; ++ con.argcount = argcount; ++ con.posonlyargcount = posonlyargcount; ++ con.kwonlyargcount = kwonlyargcount; ++ con.stacksize = stacksize; ++ con.exceptiontable = exceptiontable; + + if (_PyCode_Validate(&con) < 0) { + goto error; +@@ -661,6 +688,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) + PyObject *code_ob = NULL; + PyObject *linetable_ob = NULL; + PyCodeObject *result = NULL; ++ struct _PyCodeConstructor con = {0}; + + nulltuple = PyTuple_New(0); + if (nulltuple == NULL) { +@@ -684,20 +712,18 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) + } + + #define emptystring (PyObject *)&_Py_SINGLETON(bytes_empty) +- struct _PyCodeConstructor con = { +- .filename = filename_ob, +- .name = funcname_ob, +- .qualname = funcname_ob, +- .code = code_ob, +- .firstlineno = firstlineno, +- .linetable = linetable_ob, +- .consts = nulltuple, +- .names = nulltuple, +- .localsplusnames = nulltuple, +- .localspluskinds = emptystring, +- .exceptiontable = emptystring, +- .stacksize = 1, +- }; ++ con.filename = filename_ob; ++ con.name = funcname_ob; ++ con.qualname = funcname_ob; ++ con.code = code_ob; ++ con.firstlineno = firstlineno; ++ con.linetable = linetable_ob; ++ con.consts = nulltuple; ++ con.names = nulltuple; ++ con.localsplusnames = nulltuple; ++ con.localspluskinds = emptystring; ++ con.exceptiontable = emptystring; ++ con.stacksize = 1; + result = _PyCode_New(&con); + + failed: +@@ -721,10 +747,12 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) + int + _PyCode_CreateLineArray(PyCodeObject *co) + { +- assert(co->_co_linearray == NULL); + PyCodeAddressRange bounds; + int size; +- int max_line = 0; ++ int max_line; ++ ++ assert(co->_co_linearray == NULL); ++ max_line = 0; + _PyCode_InitAddressRange(co, &bounds); + while(_PyLineTable_NextAddressRange(&bounds)) { + if (bounds.ar_line > max_line) { +@@ -747,7 +775,8 @@ _PyCode_CreateLineArray(PyCodeObject *co) + while(_PyLineTable_NextAddressRange(&bounds)) { + int start = bounds.ar_start / sizeof(_Py_CODEUNIT); + int end = bounds.ar_end / sizeof(_Py_CODEUNIT); +- for (int index = start; index < end; index++) { ++ int index; ++ for (index = start; index < end; index++) { + assert(index < (int)Py_SIZE(co)); + if (size == 2) { + assert(((int16_t)bounds.ar_line) == bounds.ar_line); +@@ -765,6 +794,8 @@ _PyCode_CreateLineArray(PyCodeObject *co) + int + PyCode_Addr2Line(PyCodeObject *co, int addrq) + { ++ PyCodeAddressRange bounds; ++ + if (addrq < 0) { + return co->co_firstlineno; + } +@@ -772,7 +803,6 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq) + if (co->_co_linearray) { + return _PyCode_LineNumberFromArray(co, addrq / sizeof(_Py_CODEUNIT)); + } +- PyCodeAddressRange bounds; + _PyCode_InitAddressRange(co, &bounds); + return _PyCode_CheckLineNumber(addrq, &bounds); + } +@@ -791,9 +821,12 @@ _PyLineTable_InitAddressRange(const char *linetable, Py_ssize_t length, int firs + int + _PyCode_InitAddressRange(PyCodeObject* co, PyCodeAddressRange *bounds) + { ++ const char *linetable; ++ Py_ssize_t length; ++ + assert(co->co_linetable != NULL); +- const char *linetable = PyBytes_AS_STRING(co->co_linetable); +- Py_ssize_t length = PyBytes_GET_SIZE(co->co_linetable); ++ linetable = PyBytes_AS_STRING(co->co_linetable); ++ length = PyBytes_GET_SIZE(co->co_linetable); + _PyLineTable_InitAddressRange(linetable, length, co->co_firstlineno, bounds); + return bounds->ar_line; + } +@@ -839,12 +872,13 @@ next_code_delta(PyCodeAddressRange *bounds) + static int + previous_code_delta(PyCodeAddressRange *bounds) + { ++ const uint8_t *ptr; + if (bounds->ar_start == 0) { + // If we looking at the first entry, the + // "previous" entry has an implicit length of 1. + return 1; + } +- const uint8_t *ptr = bounds->opaque.lo_next-1; ++ ptr = bounds->opaque.lo_next-1; + while (((*ptr) & 128) == 0) { + ptr--; + } +@@ -926,9 +960,12 @@ advance(PyCodeAddressRange *bounds) + static void + advance_with_locations(PyCodeAddressRange *bounds, int *endline, int *column, int *endcolumn) + { ++ int first_byte; ++ int code; ++ + ASSERT_VALID_BOUNDS(bounds); +- int first_byte = read_byte(bounds); +- int code = (first_byte >> 3) & 15; ++ first_byte = read_byte(bounds); ++ code = (first_byte >> 3) & 15; + bounds->ar_start = bounds->ar_end; + bounds->ar_end = bounds->ar_start + ((first_byte & 7) + 1) * sizeof(_Py_CODEUNIT); + switch(code) { +@@ -982,13 +1019,13 @@ PyCode_Addr2Location(PyCodeObject *co, int addrq, + int *start_line, int *start_column, + int *end_line, int *end_column) + { ++ PyCodeAddressRange bounds; + if (addrq < 0) { + *start_line = *end_line = co->co_firstlineno; + *start_column = *end_column = 0; + return 1; + } + assert(addrq >= 0 && addrq < _PyCode_NBYTES(co)); +- PyCodeAddressRange bounds; + _PyCode_InitAddressRange(co, &bounds); + _PyCode_CheckLineNumber(addrq, &bounds); + retreat(&bounds); +diff --git a/Objects/object.c b/Objects/object.c +index c4f2786..054bbed 100644 +--- a/Objects/object.c ++++ b/Objects/object.c +@@ -30,6 +30,10 @@ extern "C" { + /* Defined in tracemalloc.c */ + extern void _PyMem_DumpTraceback(int fd, const void *ptr); + ++#if defined(MS_WINDOWS) && defined(_MSC_VER) && _MSC_VER < 1300 ++extern void _PyClassObject_InitTypes(void); ++#endif ++ + + int + _PyObject_CheckConsistency(PyObject *op, int check_content) +@@ -1980,6 +1984,10 @@ _PyTypes_InitTypes(PyInterpreterState *interp) + return _PyStatus_OK(); + } + ++#if defined(MS_WINDOWS) && defined(_MSC_VER) && _MSC_VER < 1300 ++ _PyClassObject_InitTypes(); ++#endif ++ + // All other static types (unless initialized elsewhere) + for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) { + PyTypeObject *type = static_types[i]; +diff --git a/Objects/stringlib/fastsearch.h b/Objects/stringlib/fastsearch.h +index 7403d8a..9912f15 100644 +--- a/Objects/stringlib/fastsearch.h ++++ b/Objects/stringlib/fastsearch.h +@@ -169,15 +169,15 @@ STRINGLIB(rfind_char)(const STRINGLIB_CHAR* s, Py_ssize_t n, STRINGLIB_CHAR ch) + + /* Change to a 1 to see logging comments walk through the algorithm. */ + #if 0 && STRINGLIB_SIZEOF_CHAR == 1 +-# define LOG(...) printf(__VA_ARGS__) ++# define LOG(args) printf args + # define LOG_STRING(s, n) printf("\"%.*s\"", (int)(n), s) + # define LOG_LINEUP() do { \ +- LOG("> "); LOG_STRING(haystack, len_haystack); LOG("\n> "); \ +- LOG("%*s",(int)(window_last - haystack + 1 - len_needle), ""); \ +- LOG_STRING(needle, len_needle); LOG("\n"); \ ++ LOG(("> ")); LOG_STRING(haystack, len_haystack); LOG(("\n> ")); \ ++ LOG(("%*s",(int)(window_last - haystack + 1 - len_needle), "")); \ ++ LOG_STRING(needle, len_needle); LOG(("\n")); \ + } while(0) + #else +-# define LOG(...) ++# define LOG(args) ((void)0) + # define LOG_STRING(s, n) + # define LOG_LINEUP() + #endif +@@ -285,9 +285,9 @@ STRINGLIB(_factorize)(const STRINGLIB_CHAR *needle, + cut = cut2; + } + +- LOG("split: "); LOG_STRING(needle, cut); +- LOG(" + "); LOG_STRING(needle + cut, len_needle - cut); +- LOG("\n"); ++ LOG(("split: ")); LOG_STRING(needle, cut); ++ LOG((" + ")); LOG_STRING(needle + cut, len_needle - cut); ++ LOG(("\n")); + + *return_period = period; + return cut; +@@ -316,6 +316,10 @@ static void + STRINGLIB(_preprocess)(const STRINGLIB_CHAR *needle, Py_ssize_t len_needle, + STRINGLIB(prework) *p) + { ++ Py_ssize_t i; ++ STRINGLIB_CHAR last; ++ Py_ssize_t not_found_shift; ++ + p->needle = needle; + p->len_needle = len_needle; + p->cut = STRINGLIB(_factorize)(needle, len_needle, &(p->period)); +@@ -334,8 +338,8 @@ STRINGLIB(_preprocess)(const STRINGLIB_CHAR *needle, Py_ssize_t len_needle, + // The gap between the last character and the previous + // occurrence of an equivalent character (modulo TABLE_SIZE) + p->gap = len_needle; +- STRINGLIB_CHAR last = needle[len_needle - 1] & TABLE_MASK; +- for (Py_ssize_t i = len_needle - 2; i >= 0; i--) { ++ last = needle[len_needle - 1] & TABLE_MASK; ++ for (i = len_needle - 2; i >= 0; i--) { + STRINGLIB_CHAR x = needle[i] & TABLE_MASK; + if (x == last) { + p->gap = len_needle - 1 - i; +@@ -344,12 +348,12 @@ STRINGLIB(_preprocess)(const STRINGLIB_CHAR *needle, Py_ssize_t len_needle, + } + } + // Fill up a compressed Boyer-Moore "Bad Character" table +- Py_ssize_t not_found_shift = Py_MIN(len_needle, MAX_SHIFT); +- for (Py_ssize_t i = 0; i < (Py_ssize_t)TABLE_SIZE; i++) { ++ not_found_shift = Py_MIN(len_needle, MAX_SHIFT); ++ for (i = 0; i < (Py_ssize_t)TABLE_SIZE; i++) { + p->table[i] = Py_SAFE_DOWNCAST(not_found_shift, + Py_ssize_t, SHIFT_TYPE); + } +- for (Py_ssize_t i = len_needle - not_found_shift; i < len_needle; i++) { ++ for (i = len_needle - not_found_shift; i < len_needle; i++) { + SHIFT_TYPE shift = Py_SAFE_DOWNCAST(len_needle - 1 - i, + Py_ssize_t, SHIFT_TYPE); + p->table[needle[i] & TABLE_MASK] = shift; +@@ -370,17 +374,22 @@ STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack, + const STRINGLIB_CHAR *const haystack_end = haystack + len_haystack; + SHIFT_TYPE *table = p->table; + const STRINGLIB_CHAR *window; +- LOG("===== Two-way: \"%s\" in \"%s\". =====\n", needle, haystack); ++ Py_ssize_t memory; ++ Py_ssize_t i; ++ Py_ssize_t shift; ++ Py_ssize_t gap_jump_end; ++ Py_ssize_t mem_jump; ++ LOG(("===== Two-way: \"%s\" in \"%s\". =====\n", needle, haystack)); + + if (p->is_periodic) { +- LOG("Needle is periodic.\n"); +- Py_ssize_t memory = 0; ++ LOG(("Needle is periodic.\n")); ++ memory = 0; + periodicwindowloop: + while (window_last < haystack_end) { + assert(memory == 0); + for (;;) { + LOG_LINEUP(); +- Py_ssize_t shift = table[(*window_last) & TABLE_MASK]; ++ shift = table[(*window_last) & TABLE_MASK]; + window_last += shift; + if (shift == 0) { + break; +@@ -388,16 +397,16 @@ STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack, + if (window_last >= haystack_end) { + return -1; + } +- LOG("Horspool skip"); ++ LOG(("Horspool skip")); + } + no_shift: + window = window_last - len_needle + 1; + assert((window[len_needle - 1] & TABLE_MASK) == + (needle[len_needle - 1] & TABLE_MASK)); +- Py_ssize_t i = Py_MAX(cut, memory); ++ i = Py_MAX(cut, memory); + for (; i < len_needle; i++) { + if (needle[i] != window[i]) { +- LOG("Right half does not match.\n"); ++ LOG(("Right half does not match.\n")); + window_last += i - cut + 1; + memory = 0; + goto periodicwindowloop; +@@ -405,20 +414,20 @@ STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack, + } + for (i = memory; i < cut; i++) { + if (needle[i] != window[i]) { +- LOG("Left half does not match.\n"); ++ LOG(("Left half does not match.\n")); + window_last += period; + memory = len_needle - period; + if (window_last >= haystack_end) { + return -1; + } +- Py_ssize_t shift = table[(*window_last) & TABLE_MASK]; ++ shift = table[(*window_last) & TABLE_MASK]; + if (shift) { + // A mismatch has been identified to the right + // of where i will next start, so we can jump + // at least as far as if the mismatch occurred + // on the first comparison. +- Py_ssize_t mem_jump = Py_MAX(cut, memory) - cut + 1; +- LOG("Skip with Memory.\n"); ++ mem_jump = Py_MAX(cut, memory) - cut + 1; ++ LOG(("Skip with Memory.\n")); + memory = 0; + window_last += Py_MAX(shift, mem_jump); + goto periodicwindowloop; +@@ -426,20 +435,20 @@ STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack, + goto no_shift; + } + } +- LOG("Found a match!\n"); ++ LOG(("Found a match!\n")); + return window - haystack; + } + } + else { + Py_ssize_t gap = p->gap; + period = Py_MAX(gap, period); +- LOG("Needle is not periodic.\n"); +- Py_ssize_t gap_jump_end = Py_MIN(len_needle, cut + gap); ++ LOG(("Needle is not periodic.\n")); ++ gap_jump_end = Py_MIN(len_needle, cut + gap); + windowloop: + while (window_last < haystack_end) { + for (;;) { + LOG_LINEUP(); +- Py_ssize_t shift = table[(*window_last) & TABLE_MASK]; ++ shift = table[(*window_last) & TABLE_MASK]; + window_last += shift; + if (shift == 0) { + break; +@@ -447,39 +456,39 @@ STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack, + if (window_last >= haystack_end) { + return -1; + } +- LOG("Horspool skip"); ++ LOG(("Horspool skip")); + } + window = window_last - len_needle + 1; + assert((window[len_needle - 1] & TABLE_MASK) == + (needle[len_needle - 1] & TABLE_MASK)); +- for (Py_ssize_t i = cut; i < gap_jump_end; i++) { ++ for (i = cut; i < gap_jump_end; i++) { + if (needle[i] != window[i]) { +- LOG("Early right half mismatch: jump by gap.\n"); ++ LOG(("Early right half mismatch: jump by gap.\n")); + assert(gap >= i - cut + 1); + window_last += gap; + goto windowloop; + } + } +- for (Py_ssize_t i = gap_jump_end; i < len_needle; i++) { ++ for (i = gap_jump_end; i < len_needle; i++) { + if (needle[i] != window[i]) { +- LOG("Late right half mismatch.\n"); ++ LOG(("Late right half mismatch.\n")); + assert(i - cut + 1 > gap); + window_last += i - cut + 1; + goto windowloop; + } + } +- for (Py_ssize_t i = 0; i < cut; i++) { ++ for (i = 0; i < cut; i++) { + if (needle[i] != window[i]) { +- LOG("Left half does not match.\n"); ++ LOG(("Left half does not match.\n")); + window_last += period; + goto windowloop; + } + } +- LOG("Found a match!\n"); ++ LOG(("Found a match!\n")); + return window - haystack; + } + } +- LOG("Not found. Returning -1.\n"); ++ LOG(("Not found. Returning -1.\n")); + return -1; + } + +@@ -490,8 +499,9 @@ STRINGLIB(_two_way_find)(const STRINGLIB_CHAR *haystack, + const STRINGLIB_CHAR *needle, + Py_ssize_t len_needle) + { +- LOG("###### Finding \"%s\" in \"%s\".\n", needle, haystack); + STRINGLIB(prework) p; ++ ++ LOG(("###### Finding \"%s\" in \"%s\".\n", needle, haystack)); + STRINGLIB(_preprocess)(needle, len_needle, &p); + return STRINGLIB(_two_way)(haystack, len_haystack, &p); + } +@@ -504,10 +514,14 @@ STRINGLIB(_two_way_count)(const STRINGLIB_CHAR *haystack, + Py_ssize_t len_needle, + Py_ssize_t maxcount) + { +- LOG("###### Counting \"%s\" in \"%s\".\n", needle, haystack); + STRINGLIB(prework) p; ++ Py_ssize_t index; ++ Py_ssize_t count; ++ ++ LOG(("###### Counting \"%s\" in \"%s\".\n", needle, haystack)); + STRINGLIB(_preprocess)(needle, len_needle, &p); +- Py_ssize_t index = 0, count = 0; ++ index = 0; ++ count = 0; + while (1) { + Py_ssize_t result; + result = STRINGLIB(_two_way)(haystack + index, +@@ -543,11 +557,12 @@ STRINGLIB(default_find)(const STRINGLIB_CHAR* s, Py_ssize_t n, + const Py_ssize_t w = n - m; + Py_ssize_t mlast = m - 1, count = 0; + Py_ssize_t gap = mlast; ++ Py_ssize_t i; + const STRINGLIB_CHAR last = p[mlast]; + const STRINGLIB_CHAR *const ss = &s[mlast]; + + unsigned long mask = 0; +- for (Py_ssize_t i = 0; i < mlast; i++) { ++ for (i = 0; i < mlast; i++) { + STRINGLIB_BLOOM_ADD(mask, p[i]); + if (p[i] == last) { + gap = mlast - i - 1; +@@ -555,7 +570,7 @@ STRINGLIB(default_find)(const STRINGLIB_CHAR* s, Py_ssize_t n, + } + STRINGLIB_BLOOM_ADD(mask, last); + +- for (Py_ssize_t i = 0; i <= w; i++) { ++ for (i = 0; i <= w; i++) { + if (ss[i] == last) { + /* candidate match */ + Py_ssize_t j; +@@ -604,11 +619,12 @@ STRINGLIB(adaptive_find)(const STRINGLIB_CHAR* s, Py_ssize_t n, + Py_ssize_t mlast = m - 1, count = 0; + Py_ssize_t gap = mlast; + Py_ssize_t hits = 0, res; ++ Py_ssize_t i; + const STRINGLIB_CHAR last = p[mlast]; + const STRINGLIB_CHAR *const ss = &s[mlast]; + + unsigned long mask = 0; +- for (Py_ssize_t i = 0; i < mlast; i++) { ++ for (i = 0; i < mlast; i++) { + STRINGLIB_BLOOM_ADD(mask, p[i]); + if (p[i] == last) { + gap = mlast - i - 1; +@@ -616,7 +632,7 @@ STRINGLIB(adaptive_find)(const STRINGLIB_CHAR* s, Py_ssize_t n, + } + STRINGLIB_BLOOM_ADD(mask, last); + +- for (Py_ssize_t i = 0; i <= w; i++) { ++ for (i = 0; i <= w; i++) { + if (ss[i] == last) { + /* candidate match */ + Py_ssize_t j; +diff --git a/Objects/stringlib/partition.h b/Objects/stringlib/partition.h +index bcc2176..d4ff95a 100644 +--- a/Objects/stringlib/partition.h ++++ b/Objects/stringlib/partition.h +@@ -17,6 +17,9 @@ STRINGLIB(partition)(PyObject* str_obj, + { + PyObject* out; + Py_ssize_t pos; ++#if !STRINGLIB_MUTABLE ++ PyObject *empty; ++#endif + + if (sep_len == 0) { + PyErr_SetString(PyExc_ValueError, "empty separator"); +@@ -42,7 +45,7 @@ STRINGLIB(partition)(PyObject* str_obj, + #else + Py_INCREF(str_obj); + PyTuple_SET_ITEM(out, 0, (PyObject*) str_obj); +- PyObject *empty = (PyObject*)STRINGLIB_GET_EMPTY(); ++ empty = (PyObject*)STRINGLIB_GET_EMPTY(); + assert(empty != NULL); + Py_INCREF(empty); + PyTuple_SET_ITEM(out, 1, empty); +@@ -74,6 +77,9 @@ STRINGLIB(rpartition)(PyObject* str_obj, + { + PyObject* out; + Py_ssize_t pos; ++#if !STRINGLIB_MUTABLE ++ PyObject *empty; ++#endif + + if (sep_len == 0) { + PyErr_SetString(PyExc_ValueError, "empty separator"); +@@ -97,7 +103,7 @@ STRINGLIB(rpartition)(PyObject* str_obj, + return NULL; + } + #else +- PyObject *empty = (PyObject*)STRINGLIB_GET_EMPTY(); ++ empty = (PyObject*)STRINGLIB_GET_EMPTY(); + assert(empty != NULL); + Py_INCREF(empty); + PyTuple_SET_ITEM(out, 0, empty); diff --git a/PC/pyconfig.h b/PC/pyconfig.h index 959b851..6d8ce84 100644 --- a/PC/pyconfig.h @@ -6797,6 +14873,18 @@ index 959b851..6d8ce84 100644 /* For Windows the Python core is in a DLL by default. Test Py_NO_ENABLE_SHARED to find out. Also support MS_NO_COREDLL for b/w compat */ #if !defined(MS_NO_COREDLL) && !defined(Py_NO_ENABLE_SHARED) +diff --git a/Python/dynload_win.c b/Python/dynload_win.c +index 5dc4095..e97a972 100644 +--- a/Python/dynload_win.c ++++ b/Python/dynload_win.c +@@ -13,6 +13,7 @@ + #include "importdl.h" + #include "patchlevel.h" + #include ++#include "pycore_win9x_loadlibrary.h" // SDK fallbacks for VC6 + + #ifdef _DEBUG + #define PYD_DEBUG_SUFFIX "_d" diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 9248e97..4abd5d4 100644 --- a/Python/pylifecycle.c diff --git a/compat/msvc600/inttypes.h b/compat/msvc600/inttypes.h index 1d100f2..8fb57c6 100644 --- a/compat/msvc600/inttypes.h +++ b/compat/msvc600/inttypes.h @@ -22,6 +22,57 @@ typedef unsigned long uint_least32_t; typedef __int64 int_least64_t; typedef unsigned __int64 uint_least64_t; +/* C99 integer limit macros missing from the VC6 standard headers. + * This compatibility directory is used only for the 32-bit Win32 target. */ +#ifndef INT8_MIN +# define INT8_MIN (-127 - 1) +#endif +#ifndef INT8_MAX +# define INT8_MAX 127 +#endif +#ifndef UINT8_MAX +# define UINT8_MAX 0xFFU +#endif +#ifndef INT16_MIN +# define INT16_MIN (-32767 - 1) +#endif +#ifndef INT16_MAX +# define INT16_MAX 32767 +#endif +#ifndef UINT16_MAX +# define UINT16_MAX 0xFFFFU +#endif +#ifndef INT32_MIN +# define INT32_MIN (-2147483647L - 1) +#endif +#ifndef INT32_MAX +# define INT32_MAX 2147483647L +#endif +#ifndef UINT32_MAX +# define UINT32_MAX 0xFFFFFFFFUL +#endif +#ifndef INT64_MAX +# define INT64_MAX 0x7FFFFFFFFFFFFFFFi64 +#endif +#ifndef INT64_MIN +# define INT64_MIN (-INT64_MAX - 1) +#endif +#ifndef UINT64_MAX +# define UINT64_MAX 0xFFFFFFFFFFFFFFFFui64 +#endif +#ifndef INTPTR_MIN +# define INTPTR_MIN (-2147483647L - 1) +#endif +#ifndef INTPTR_MAX +# define INTPTR_MAX 2147483647L +#endif +#ifndef UINTPTR_MAX +# define UINTPTR_MAX 0xFFFFFFFFUL +#endif +#ifndef SIZE_MAX +# define SIZE_MAX 0xFFFFFFFFUL +#endif + #define PRId32 "ld" #define PRIu32 "lu" #define PRIx32 "lx" diff --git a/scripts/fixtures/vc6_stdint_smoke.c b/scripts/fixtures/vc6_stdint_smoke.c index 7788352..8711153 100644 --- a/scripts/fixtures/vc6_stdint_smoke.c +++ b/scripts/fixtures/vc6_stdint_smoke.c @@ -14,6 +14,19 @@ #include #include +#if UINT16_MAX != 0xFFFFU +#error "VC6 uint16_t maximum is incorrect" +#endif +#if SIZE_MAX != 0xFFFFFFFFUL +#error "VC6 size_t maximum must match the 32-bit Win32 target" +#endif +#if INT64_MAX != 0x7FFFFFFFFFFFFFFFi64 +#error "VC6 int64_t maximum is incorrect" +#endif +#if UINT64_MAX != 0xFFFFFFFFFFFFFFFFui64 +#error "VC6 uint64_t maximum is incorrect" +#endif + typedef char vc6_stdint_check_uint8_is_1_byte[(sizeof(uint8_t) == 1) ? 1 : -1]; typedef char vc6_stdint_check_uint32_is_4_bytes[(sizeof(uint32_t) == 4) ? 1 : -1]; typedef char vc6_stdint_check_uint64_is_8_bytes[(sizeof(uint64_t) == 8) ? 1 : -1]; diff --git a/scripts/vc6-bytearray-join-c89.test.py b/scripts/vc6-bytearray-join-c89.test.py new file mode 100644 index 0000000..3e2665d --- /dev/null +++ b/scripts/vc6-bytearray-join-c89.test.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +"""Source-level C89 and ownership-order check for bytearray_join.""" +import re +import sys +from pathlib import Path + +if len(sys.argv) != 2: + raise SystemExit("usage: vc6-bytearray-join-c89.test.py bytearrayobject.c") +source = Path(sys.argv[1]).read_text() +signature = "bytearray_join(PyByteArrayObject *self, PyObject *iterable_of_bytes)" +start = source.index(signature) +body_start = source.index("{", start) +body_end = source.index("\n}", body_start) +body = source[body_start + 1:body_end] + +if not body.lstrip().startswith("PyObject *ret;"): + raise SystemExit("ret must be declared in bytearray_join's opening declaration group") +if len(re.findall(r"\bPyObject\s*\*\s*ret\s*;", body)) != 1: + raise SystemExit("bytearray_join must declare ret exactly once") +if re.search(r"\bPyObject\s*\*\s*ret\s*=", body): + raise SystemExit("bytearray_join must assign ret separately from its declaration") +pattern = re.compile( + r"self->ob_exports\+\+;.*?" + r"ret\s*=\s*stringlib_bytes_join\(\s*\(PyObject\*\)self,\s*" + r"iterable_of_bytes\s*\);\s*" + r"self->ob_exports--;\s*(?://[^\n]*\s*)?return ret;", + re.DOTALL, +) +if not pattern.search(body): + raise SystemExit("bytearray_join export/join/unexport/return order changed") +print("bytearray_join C89 declaration and export order: PASS") diff --git a/scripts/vc6-bytes-clinic-c89.test.py b/scripts/vc6-bytes-clinic-c89.test.py new file mode 100644 index 0000000..e595192 --- /dev/null +++ b/scripts/vc6-bytes-clinic-c89.test.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Check C89 declaration placement in generated bytes Clinic wrappers.""" +import re +import sys +from pathlib import Path + +if len(sys.argv) != 2: + raise SystemExit("usage: vc6-bytes-clinic-c89.test.py bytesobject.c.h") +source = Path(sys.argv[1]).read_text() +checks = { + "bytes_decode(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)": ( + "encoding_length", "errors_length"), + "bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)": ( + "encoding_length", "errors_length"), +} +for signature, names in checks.items(): + start = source.index(signature) + body_start = source.index("\n{\n", start) + 2 + body_end = source.index("\n}", body_start) + body = source[body_start:body_end] + unpack = re.search(r"^\s*(?:args|fastargs) = _PyArg_UnpackKeywords", body, re.MULTILINE) + if unpack is None: + raise SystemExit(signature.split("(", 1)[0] + ": argument unpack statement missing") + opening = body[:unpack.start()] + execution = body[unpack.end():] + for name in names: + declaration = "Py_ssize_t " + name + ";" + if opening.count(declaration) != 1 or body.count(declaration) != 1: + raise SystemExit(signature.split("(", 1)[0] + ": " + name + " must be declared once before argument unpacking") + if re.search(r"\bPy_ssize_t\s+" + re.escape(name) + r"\b", execution): + raise SystemExit(signature.split("(", 1)[0] + ": late declaration remains for " + name) + for name in names: + if "&" + name not in execution: + raise SystemExit(signature.split("(", 1)[0] + ": original UTF-8 length use missing for " + name) +print("bytes Clinic C89 declaration placement: PASS") diff --git a/scripts/vc6-bytes-core-c89.test.py b/scripts/vc6-bytes-core-c89.test.py new file mode 100644 index 0000000..62b4c20 --- /dev/null +++ b/scripts/vc6-bytes-core-c89.test.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +"""Check C89 locals in bytes find and repeat helpers.""" +import sys +from pathlib import Path + +if len(sys.argv) != 2: + raise SystemExit("usage: vc6-bytes-core-c89.test.py bytesobject.c") +source = Path(sys.argv[1]).read_text() + +def function_body(signature): + start = source.index(signature) + body_start = source.index("\n{\n", start) + 2 + body_end = source.index("\n}\n", body_start) + return source[body_start:body_end] + +find = function_body("_PyBytes_Find(const char *haystack") +if not find.lstrip().startswith("Py_ssize_t res;"): + raise SystemExit("_PyBytes_Find res must be declared in its entry group") +if "Py_ssize_t res =" in find: + raise SystemExit("_PyBytes_Find res declaration must be separate from assignment") +if "if (res == -1) {\n Py_ssize_t last_align;" not in find: + raise SystemExit("_PyBytes_Find last_align must be declared at the conditional block entry") +if not (find.index("res = stringlib_find(") < find.index("if (res == -1)") < find.index("last_align = len_haystack - len_needle;") < find.index("memcmp(haystack + last_align")): + raise SystemExit("_PyBytes_Find search and trailing-alignment order changed") + +repeat = function_body("_PyBytes_Repeat(char* dest") +if "else {\n Py_ssize_t copied;\n if (src != dest)" not in repeat: + raise SystemExit("_PyBytes_Repeat copied must be declared at the else-block entry") +if "Py_ssize_t copied =" in repeat: + raise SystemExit("_PyBytes_Repeat copied declaration must be separate from assignment") +if not (repeat.index("memcpy(dest, src, len_src);") < repeat.index("copied = len_src;") < repeat.index("while (copied < len_dest)")): + raise SystemExit("_PyBytes_Repeat copy loop initialization order changed") +print("bytes find/repeat C89 declarations and execution order: PASS") diff --git a/scripts/vc6-bytes-format-c89.test.py b/scripts/vc6-bytes-format-c89.test.py new file mode 100644 index 0000000..d1df0e0 --- /dev/null +++ b/scripts/vc6-bytes-format-c89.test.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +"""Check debug-only before pointer is C89-declared in bytes formatting.""" +import sys +from pathlib import Path + +if len(sys.argv) != 2: + raise SystemExit("usage: vc6-bytes-format-c89.test.py bytesobject.c") +source = Path(sys.argv[1]).read_text() +signature = "_PyBytes_FormatEx(const char *format, Py_ssize_t format_len," +start = source.index(signature) +body_start = source.index("\n{\n", start) + 2 +body_end = source.index("\n}\n", body_start) +body = source[body_start:body_end] +branch_marker = "else {\n /* Got a format specifier */" +branch_start = body.index(branch_marker) +branch_end = body.index("\n fmt++;", branch_start) +branch = body[branch_start:branch_end] +expected_decl = "#ifndef NDEBUG\n char *before;\n#endif" +if expected_decl not in branch: + raise SystemExit("debug before pointer must be declared in the format-specifier block's declaration group") +fmt_statement = body.index("\n fmt++;", branch_start) +if branch_start + branch.index(expected_decl) > fmt_statement: + raise SystemExit("debug before declaration occurs after executable statements") +if source.count("char *before;") != 1 or "char *before = res;" in source: + raise SystemExit("debug before pointer must have one declaration and a separate assignment") +prepare = body.index("_PyBytesWriter_Prepare(&writer, res, alloc - 2)") +assignment = body.index("before = res;") +assertion = body.index("assert((res - before) == alloc);") +if not (prepare < assignment < body.index("/* Write the sign if needed */") < assertion): + raise SystemExit("bytes format pointer capture / output / size assertion order changed") +print("bytes format debug pointer C89 declaration and capture order: PASS") diff --git a/scripts/vc6-call-first-c89.test.py b/scripts/vc6-call-first-c89.test.py new file mode 100644 index 0000000..5db0e69 --- /dev/null +++ b/scripts/vc6-call-first-c89.test.py @@ -0,0 +1,412 @@ +#!/usr/bin/env python3 +"""Check the first VC6 C89 frontier in Objects/call.c.""" +import sys +from pathlib import Path + +if len(sys.argv) != 2: + raise SystemExit("usage: vc6-call-first-c89.test.py call.c") +source = Path(sys.argv[1]).read_text() +signature = "_PyObject_FastCallDictTstate(PyThreadState *tstate, PyObject *callable," +start = source.index(signature) +body_start = source.index("\n{\n", start) + 2 +body_end = source.index("\n}\n", body_start) +body = source[body_start:body_end] +first_statement = body.index("assert(callable != NULL);") +for declaration in ("Py_ssize_t nargs;", "vectorcallfunc func;", "PyObject *res;"): + if body.count(declaration) != 1 or body.index(declaration) > first_statement: + raise SystemExit("_PyObject_FastCallDictTstate locals must be declared before the first statement") +if "Py_ssize_t nargs =" in body or "vectorcallfunc func =" in body: + raise SystemExit("_PyObject_FastCallDictTstate declarations must be separate from assignments") +order = ( + "assert(callable != NULL);", + "assert(!_PyErr_Occurred(tstate));", + "nargs = PyVectorcall_NARGS(nargsf);", + "func = _PyVectorcall_Function(callable);", + "if (func == NULL)", + "if (kwargs == NULL || PyDict_GET_SIZE(kwargs) == 0)", + "return _Py_CheckFunctionResult(tstate, callable, res, NULL);", +) +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("vectorcall validation/dispatch/result-check order changed") + +signature = "_PyObject_MakeTpCall(PyThreadState *tstate, PyObject *callable," +start = source.index(signature) +body_start = source.index("\n{\n", start) + 2 +body_end = source.index("\n}\n", body_start) +body = source[body_start:body_end] +first_statement = body.index("assert(nargs >= 0);") +for declaration in ("ternaryfunc call;", "PyObject *argstuple;", "PyObject *kwdict;", "PyObject *result;"): + if body.count(declaration) != 1 or body.index(declaration) > first_statement: + raise SystemExit("_PyObject_MakeTpCall locals must be declared before the first statement") +if any(declaration in body for declaration in ("ternaryfunc call =", "PyObject *argstuple =", "PyObject *result =")): + raise SystemExit("_PyObject_MakeTpCall locals must be declared separately from assignment") +order = ( + "assert(nargs >= 0);", + "assert(keywords == NULL || PyTuple_Check(keywords) || PyDict_Check(keywords));", + "call = Py_TYPE(callable)->tp_call;", + "if (call == NULL)", + "argstuple = _PyTuple_FromArray(args, nargs);", + "if (keywords == NULL || PyDict_Check(keywords))", + "result = NULL;", + "if (_Py_EnterRecursiveCallTstate(tstate,", + "_Py_LeaveRecursiveCallTstate(tstate);", + "\n Py_DECREF(argstuple);\n if (kwdict != keywords)", + "if (kwdict != keywords)", + "return _Py_CheckFunctionResult(tstate, callable, result, NULL);", +) +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("tp_call argument construction/reference cleanup order changed") + +signature = "_PyVectorcall_Call(PyThreadState *tstate, vectorcallfunc func," +start = source.index(signature) +body_start = source.index("\n{\n", start) + 2 +body_end = source.index("\n}\n", body_start) +body = source[body_start:body_end] +first_statement = body.index("assert(func != NULL);") +for declaration in ("Py_ssize_t nargs;", "PyObject *const *args;", "PyObject *kwnames;", "PyObject *result;"): + if body.count(declaration) != 1 or body.index(declaration) > first_statement: + raise SystemExit("_PyVectorcall_Call locals must be declared before the first statement") +if "Py_ssize_t nargs =" in body or "PyObject *result =" in body: + raise SystemExit("_PyVectorcall_Call declarations must be separate from assignments") +order = ( + "assert(func != NULL);", + "nargs = PyTuple_GET_SIZE(tuple);", + "if (kwargs == NULL || PyDict_GET_SIZE(kwargs) == 0)", + "args = _PyStack_UnpackDict(tstate,", + "if (args == NULL)", + "result = func(callable, args,", + "_PyStack_UnpackDict_Free(args, nargs, kwnames);", + "return _Py_CheckFunctionResult(tstate, callable, result, NULL);", +) +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("vectorcall argument unpack/call/free/check order changed") + +signature = "PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs)" +start = source.index(signature) +body_start = source.index("\n{\n", start) + 2 +body_end = source.index("\n}\n", body_start) +body = source[body_start:body_end] +for declaration in ("PyThreadState *tstate;", "Py_ssize_t offset;", "vectorcallfunc func;"): + if body.count(declaration) != 1 or body.index(declaration) > body.index("tstate = _PyThreadState_GET();"): + raise SystemExit("PyVectorcall_Call locals must be declared before initialization") +if "PyThreadState *tstate =" in body or "Py_ssize_t offset =" in body: + raise SystemExit("PyVectorcall_Call declarations must be separate from assignments") +order = ( + "tstate = _PyThreadState_GET();", + "offset = Py_TYPE(callable)->tp_vectorcall_offset;", + "if (offset <= 0)", + "assert(PyCallable_Check(callable));", + "memcpy(&func, (char *) callable + offset, sizeof(func));", + "if (func == NULL)", + "return _PyVectorcall_Call(tstate, func, callable, tuple, kwargs);", +) +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("PyVectorcall_Call validation/copy/dispatch order changed") + +signature = "_PyObject_Call(PyThreadState *tstate, PyObject *callable," +start = source.index(signature) +body_start = source.index("\n{\n", start) + 2 +body_end = source.index("\n}\n", body_start) +body = source[body_start:body_end] +first_statement = body.index("assert(!_PyErr_Occurred(tstate));") +if body.count("vectorcallfunc vector_func;") != 1 or body.index("vectorcallfunc vector_func;") > first_statement: + raise SystemExit("_PyObject_Call vectorcall local must be declared before validation") +if "vectorcallfunc vector_func =" in body: + raise SystemExit("_PyObject_Call vectorcall declaration must be separate from lookup") +order = ( + "assert(!_PyErr_Occurred(tstate));", + "assert(PyTuple_Check(args));", + "vector_func = _PyVectorcall_Function(callable);", + "if (vector_func != NULL)", + "call = Py_TYPE(callable)->tp_call;", + "if (call == NULL)", + "if (_Py_EnterRecursiveCallTstate(tstate,", + "result = (*call)(callable, args, kwargs);", + "_Py_LeaveRecursiveCallTstate(tstate);", + "return _Py_CheckFunctionResult(tstate, callable, result, NULL);", +) +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("_PyObject_Call validation/dispatch/recursion order changed") + +signature = "PyObject_CallOneArg(PyObject *func, PyObject *arg)" +start = source.index(signature) +body_start = source.index("\n{\n", start) + 2 +body_end = source.index("\n}\n", body_start) +body = source[body_start:body_end] +first_statement = body.index("assert(arg != NULL);") +for declaration in ("PyObject *_args[2];", "PyObject **args;", "PyThreadState *tstate;", "size_t nargsf;"): + if body.count(declaration) != 1 or body.index(declaration) > first_statement: + raise SystemExit("PyObject_CallOneArg locals must be declared before the assertion") +if any(text in body for text in ("PyObject **args =", "PyThreadState *tstate =", "size_t nargsf =")): + raise SystemExit("PyObject_CallOneArg declarations must be separate from assignments") +order = ( + "assert(arg != NULL);", + "args = _args + 1;", + "args[0] = arg;", + "tstate = _PyThreadState_GET();", + "nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET;", + "return _PyObject_VectorcallTstate(tstate, func, args, nargsf, NULL);", +) +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("PyObject_CallOneArg vectorcall argument setup order changed") + +signature = "_PyFunction_Vectorcall(PyObject *func, PyObject* const* stack," +start = source.index(signature) +body_start = source.index("\n{\n", start) + 2 +body_end = source.index("\n}\n", body_start) +body = source[body_start:body_end] +first_statement = body.index("assert(PyFunction_Check(func));") +for declaration in ("PyFunctionObject *f;", "Py_ssize_t nargs;", "PyThreadState *tstate;"): + if body.count(declaration) != 1 or body.index(declaration) > first_statement: + raise SystemExit("_PyFunction_Vectorcall locals must be declared before the assertion") +if any(text in body for text in ("PyFunctionObject *f =", "Py_ssize_t nargs =", "PyThreadState *tstate =")): + raise SystemExit("_PyFunction_Vectorcall declarations must be separate from assignments") +order = ( + "assert(PyFunction_Check(func));", + "f = (PyFunctionObject *)func;", + "nargs = PyVectorcall_NARGS(nargsf);", + "assert(nargs >= 0);", + "tstate = _PyThreadState_GET();", + "assert(nargs == 0 || stack != NULL);", + "if (((PyCodeObject *)f->func_code)->co_flags & CO_OPTIMIZED)", + "return _PyEval_Vector(tstate, f, NULL, stack, nargs, kwnames);", + "return _PyEval_Vector(tstate, f, f->func_globals, stack, nargs, kwnames);", +) +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("function vectorcall validation/state/dispatch order changed") + +signature = "_PyObject_Call_Prepend(PyThreadState *tstate, PyObject *callable," +start = source.index(signature) +body_start = source.index("\n{\n", start) + 2 +body_end = source.index("\n}\n", body_start) +body = source[body_start:body_end] +first_statement = body.index("assert(PyTuple_Check(args));") +for declaration in ( + "PyObject *small_stack[_PY_FASTCALL_SMALL_STACK];", + "PyObject **stack;", + "Py_ssize_t argcount;", + "PyObject *result;", +): + if body.count(declaration) != 1 or body.index(declaration) > first_statement: + raise SystemExit("_PyObject_Call_Prepend locals must be declared before the assertion") +if "Py_ssize_t argcount =" in body or "PyObject *result =" in body: + raise SystemExit("_PyObject_Call_Prepend declarations must be separate from assignments") +order = ( + "assert(PyTuple_Check(args));", + "argcount = PyTuple_GET_SIZE(args);", + "if (argcount + 1 <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack))", + "stack = PyMem_Malloc((argcount + 1) * sizeof(PyObject *));", + "stack[0] = obj;", + "memcpy(&stack[1],", + "result = _PyObject_FastCallDictTstate(tstate, callable,", + "if (stack != small_stack)", + "return result;", +) +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("_PyObject_Call_Prepend stack/call/free order changed") + +signature = "_PyObject_CallFunction_SizeT(PyObject *callable, const char *format, ...)" +start = source.index(signature) +body_start = source.index("\n{\n", start) + 2 +body_end = source.index("\n}\n", body_start) +body = source[body_start:body_end] +first_call = body.index("va_start(va, format);") +for declaration in ("va_list va;", "PyObject *result;"): + if body.count(declaration) != 1 or body.index(declaration) > first_call: + raise SystemExit("_PyObject_CallFunction_SizeT locals must be declared before va_start") +if "PyObject *result =" in body: + raise SystemExit("_PyObject_CallFunction_SizeT result must be assigned after va_start") +order = ( + "tstate = _PyThreadState_GET();", + "va_start(va, format);", + "result = _PyObject_CallFunctionVa(tstate, callable, format, va, 1);", + "va_end(va);", + "return result;", +) +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("SizeT wrapper thread/va/result lifecycle order changed") + +method_wrappers = ( + ("PyObject_CallMethod(PyObject *obj, const char *name, const char *format, ...)", "PyObject_GetAttrString(obj, name)", 0), + ("PyEval_CallMethod(PyObject *obj, const char *name, const char *format, ...)", "PyObject_GetAttrString(obj, name)", 0), + ("_PyObject_CallMethod(PyObject *obj, PyObject *name,", "PyObject_GetAttr(obj, name)", 1), + ("_PyObject_CallMethodId(PyObject *obj, _Py_Identifier *name,", "_PyObject_GetAttrId(obj, name)", 0), + ("_PyObject_CallMethodFormat(PyThreadState *tstate, PyObject *callable,", None, 0), + ("_PyObject_CallMethod_SizeT(PyObject *obj, const char *name,", "PyObject_GetAttrString(obj, name)", 1), + ("_PyObject_CallMethodId_SizeT(PyObject *obj, _Py_Identifier *name,", "_PyObject_GetAttrId(obj, name)", 1), +) +for signature, lookup, size_t in method_wrappers: + start = source.index(signature) + body_start = source.index("\n{\n", start) + 2 + body_end = source.index("\n}\n", body_start) + body = source[body_start:body_end] + first_statement = body.index("if (obj == NULL || name == NULL)") if lookup else body.index("va_start(va, format);") + required = ["va_list va;", "PyObject *retval;"] + if lookup: + required.append("PyObject *callable;") + for declaration in required: + if body.count(declaration) != 1 or body.index(declaration) > first_statement: + raise SystemExit(signature.split("(", 1)[0] + " locals must be declared before execution") + if "PyObject *retval =" in body or (lookup and "PyObject *callable =" in body): + raise SystemExit(signature.split("(", 1)[0] + " locals must be declared separately from assignments") + if lookup: + order = ( + "if (obj == NULL || name == NULL)", + lookup + ";", + "if (callable == NULL)", + "va_start(va, format);", + "retval = callmethod(tstate, callable, format, va, " + str(size_t) + ");", + "va_end(va);", + "Py_DECREF(callable);", + "return retval;", + ) + else: + order = ( + "va_start(va, format);", + "retval = callmethod(tstate, callable, format, va, " + str(size_t) + ");", + "va_end(va);", + "return retval;", + ) + missing = [item for item in order if item not in body] + if missing: + raise SystemExit(signature.split("(", 1)[0] + " missing expected order text: " + repr(missing)) + positions = [body.index(item) for item in order] + if positions != sorted(positions): + raise SystemExit(signature.split("(", 1)[0] + " lookup/va/cleanup order changed") + +signature = "PyObject_VectorcallMethod(PyObject *name, PyObject *const *args," +start = source.index(signature) +body_start = source.index("\n{\n", start) + 2 +body_end = source.index("\n}\n", body_start) +body = source[body_start:body_end] +first_statement = body.index("assert(name != NULL);") +for declaration in ("PyThreadState *tstate;", "PyObject *callable;", "int unbound;", "PyObject *result;"): + if body.count(declaration) != 1 or body.index(declaration) > first_statement: + raise SystemExit("PyObject_VectorcallMethod locals must be declared before validation") +if "PyObject *callable =" in body or "int unbound =" in body or "PyObject *result =" in body: + raise SystemExit("PyObject_VectorcallMethod declarations must be separate from initialization") +order = ( + "assert(name != NULL);", + "assert(args != NULL);", + "assert(PyVectorcall_NARGS(nargsf) >= 1);", + "tstate = _PyThreadState_GET();", + "callable = NULL;", + "unbound = _PyObject_GetMethod(args[0], name, &callable);", + "if (callable == NULL)", + "if (unbound)", + "else {", + "result = _PyObject_VectorcallTstate(tstate, callable,", + "Py_DECREF(callable);", + "return result;", +) +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("vectorcall method lookup/argument adjustment/cleanup order changed") + +method_objargs = ( + ("PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...)", False), + ("_PyObject_CallMethodIdObjArgs(PyObject *obj, _Py_Identifier *name, ...)", True), +) +for signature, has_oname in method_objargs: + start = source.index(signature) + body_start = source.index("\n{\n", start) + 2 + body_end = source.index("\n}\n", body_start) + body = source[body_start:body_end] + first_statement = body.index("if (obj == NULL || name == NULL)") + required = ["PyObject *callable;", "int is_method;", "va_list vargs;", "PyObject *result;"] + if has_oname: + required.append("PyObject *oname;") + for declaration in required: + if body.count(declaration) != 1 or body.index(declaration) > first_statement: + raise SystemExit(signature.split("(", 1)[0] + " locals must be declared before execution") + if "PyObject *callable =" in body or "int is_method =" in body or "PyObject *result =" in body: + raise SystemExit(signature.split("(", 1)[0] + " declarations must be separate from initialization") + order = ["if (obj == NULL || name == NULL)"] + if has_oname: + order.extend(("oname = _PyUnicode_FromId(name);", "if (!oname)")) + order.extend(( + "callable = NULL;", + "is_method = _PyObject_GetMethod(obj, " + ("oname" if has_oname else "name") + ", &callable);", + "if (callable == NULL)", + "obj = is_method ? obj : NULL;", + "va_start(vargs, name);", + "result = object_vacall(tstate, obj, callable, vargs);", + "va_end(vargs);", + "Py_DECREF(callable);", + "return result;", + )) + missing = [item for item in order if item not in body] + if missing: + raise SystemExit(signature.split("(", 1)[0] + " missing expected order text: " + repr(missing)) + positions = [body.index(item) for item in order] + if positions != sorted(positions): + raise SystemExit(signature.split("(", 1)[0] + " lookup/va/cleanup order changed") + +signature = "_PyStack_UnpackDict(PyThreadState *tstate," +start = source.rindex(signature) +body_start = source.index("\n{\n", start) + 2 +body_end = source.index("\n}\n", body_start) +body = source[body_start:body_end] +first_statement = body.index("assert(nargs >= 0);") +declarations = ( + "Py_ssize_t nkwargs;", "Py_ssize_t maxnargs;", "PyObject **stack;", + "PyObject *kwnames;", "PyObject **kwstack;", "Py_ssize_t pos;", + "Py_ssize_t i;", "PyObject *key;", "PyObject *value;", + "unsigned long keys_are_strings;", +) +for declaration in declarations: + if body.count(declaration) != 1 or body.index(declaration) > first_statement: + raise SystemExit("_PyStack_UnpackDict locals must be declared before the first assertion") +if "for (Py_ssize_t i =" in body or "Py_ssize_t nkwargs =" in body or "Py_ssize_t maxnargs =" in body: + raise SystemExit("_PyStack_UnpackDict declaration/initialization separation failed") +order = ( + "assert(nargs >= 0);", + "assert(kwargs != NULL);", + "nkwargs = PyDict_GET_SIZE(kwargs);", + "maxnargs = PY_SSIZE_T_MAX / sizeof(args[0]) - 1;", + "if (nargs > maxnargs - nkwargs)", + "stack = PyMem_Malloc((1 + nargs + nkwargs) * sizeof(args[0]));", + "kwnames = PyTuple_New(nkwargs);", + "stack++; /* For PY_VECTORCALL_ARGUMENTS_OFFSET */", + "for (i = 0; i < nargs; i++)", + "kwstack = stack + nargs;", + "pos = 0;", + "\n i = 0;\n", + "keys_are_strings = Py_TPFLAGS_UNICODE_SUBCLASS;", + "while (PyDict_Next(kwargs, &pos, &key, &value))", + "if (!keys_are_strings)", +) +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("_PyStack_UnpackDict validation/allocation/iteration order changed: " + repr(list(zip(order, positions)))) + +signature = "_PyStack_UnpackDict_Free(PyObject *const *stack, Py_ssize_t nargs," +start = source.rindex(signature) +body_start = source.index("\n{\n", start) + 2 +body_end = source.index("\n}\n", body_start) +body = source[body_start:body_end] +loop = body.index("for (i = 0;") +if body.count("Py_ssize_t i;") != 1 or body.index("Py_ssize_t i;") > loop: + raise SystemExit("_PyStack_UnpackDict_Free loop index must be declared before execution") +if "for (Py_ssize_t i =" in body: + raise SystemExit("_PyStack_UnpackDict_Free loop must use its entry-declared index") +order = ( + "Py_ssize_t n = PyTuple_GET_SIZE(kwnames) + nargs;", + "for (i = 0; i < n; i++)", + "Py_DECREF(stack[i]);", + "PyMem_Free((PyObject **)stack - 1);", + "Py_DECREF(kwnames);", +) +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("_PyStack_UnpackDict_Free release order changed") +print("call.c C89 declarations and stack cleanup order: PASS") diff --git a/scripts/vc6-classobject-c89.test.py b/scripts/vc6-classobject-c89.test.py new file mode 100644 index 0000000..b094c80 --- /dev/null +++ b/scripts/vc6-classobject-c89.test.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +"""Check C89 locals and mutation/cleanup order in method_vectorcall.""" +import sys +from pathlib import Path + +if len(sys.argv) != 2: + raise SystemExit("usage: vc6-classobject-c89.test.py classobject.c") +source = Path(sys.argv[1]).read_text() +signature = "method_vectorcall(PyObject *method, PyObject *const *args," +start = source.index(signature) +body_start = source.index("\n{\n", start) + 2 +body_end = source.index("\n}\n", body_start) +body = source[body_start:body_end] +first_statement = body.index("assert(Py_IS_TYPE(method, &PyMethod_Type));") +for declaration in ("PyThreadState *tstate;", "PyObject *self;", "PyObject *func;", "Py_ssize_t nargs;", "PyObject *result;"): + if body.count(declaration) != 1 or body.index(declaration) > first_statement: + raise SystemExit("method_vectorcall locals must be declared before the assertion") +if any(text in body for text in ("PyThreadState *tstate =", "PyObject *self =", "PyObject *func =", "Py_ssize_t nargs =")): + raise SystemExit("method_vectorcall declarations must be separate from assignments") +branch_start = body.index("if (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET) {") +branch_end = body.index("\n else {", branch_start) +branch = body[branch_start:branch_end] +if "PyObject **newargs;\n PyObject *tmp;" not in branch: + raise SystemExit("offset branch locals must be declared at block entry") +if not (branch.index("newargs = (PyObject**)args - 1;") < branch.index("nargs += 1;") < branch.index("tmp = newargs[0];") < branch.index("newargs[0] = self;") < branch.index("result = _PyObject_VectorcallTstate") < branch.index("newargs[0] = tmp;")): + raise SystemExit("offset argument mutation and restoration order changed") +else_start = body.index("\n else {", branch_start) +else_body = body[else_start:] +first_exec = else_body.index("nkwargs =") +for declaration in ("Py_ssize_t nkwargs;", "Py_ssize_t totalargs;", "PyObject *newargs_stack[_PY_FASTCALL_SMALL_STACK];", "PyObject **newargs;"): + if else_body.count(declaration) != 1 or else_body.index(declaration) > first_exec: + raise SystemExit("non-offset branch locals must be declared before initialization") +if not (else_body.index("nkwargs =") < else_body.index("totalargs =") < else_body.index("if (totalargs == 0)") < else_body.index("if (totalargs <=") < else_body.index("newargs = newargs_stack;") < else_body.index("newargs[0] = self;") < else_body.index("result = _PyObject_VectorcallTstate") < else_body.index("if (newargs != newargs_stack)")): + raise SystemExit("non-offset allocation/call/free order changed") + +signature = "PyMethod_New(PyObject *func, PyObject *self)" +start = source.index(signature) +body_start = source.index("\n{\n", start) + 2 +body_end = source.index("\n}\n", body_start) +body = source[body_start:body_end] +first_statement = body.index("if (self == NULL)") +if body.count("PyMethodObject *im;") != 1 or body.index("PyMethodObject *im;") > first_statement: + raise SystemExit("PyMethod_New object local must be declared before validation") +if "PyMethodObject *im =" in body: + raise SystemExit("PyMethod_New allocation must remain at its original execution point") +order = ( + "if (self == NULL)", + "im = PyObject_GC_New(PyMethodObject, &PyMethod_Type);", + "if (im == NULL)", + "im->im_weakreflist = NULL;", + "Py_INCREF(func);", + "im->im_func = func;", + "Py_INCREF(self);", + "im->im_self = self;", + "im->vectorcall = method_vectorcall;", + "_PyObject_GC_TRACK(im);", + "return (PyObject *)im;", +) +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("PyMethod_New allocation/reference/GC order changed") +print("classobject C89 declarations and method initialization order: PASS") diff --git a/scripts/vc6-classobject-typeinit.test.py b/scripts/vc6-classobject-typeinit.test.py new file mode 100644 index 0000000..207d8c0 --- /dev/null +++ b/scripts/vc6-classobject-typeinit.test.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Verify legacy MSVC gets runtime-assigned classobject type fields.""" +import sys +from pathlib import Path + +if len(sys.argv) != 3: + raise SystemExit("usage: vc6-classobject-typeinit.test.py classobject.c object.c") +class_source = Path(sys.argv[1]).read_text() +object_source = Path(sys.argv[2]).read_text() +legacy_condition = "!defined(MS_WINDOWS) || !defined(_MSC_VER) || _MSC_VER >= 1300" +for symbol in ("PyMethod_Type", "PyInstanceMethod_Type"): + start = class_source.index("PyTypeObject " + symbol + " = {") + end = class_source.index("\n};", start) + initializer = class_source[start:end] + if "#if " + legacy_condition not in initializer: + raise SystemExit(symbol + " designated initializer must be excluded for VC6 Windows") + +start = object_source.index("_PyTypes_InitTypes(PyInterpreterState *interp)") +body_start = object_source.index("\n{\n", start) + 2 +body_end = object_source.index("\n}\n", body_start) +body = object_source[body_start:body_end] +ready = body.index("PyType_Ready(type)") +call = body.index("_PyClassObject_InitTypes();") +if call > ready: + raise SystemExit("legacy type fields must be initialized before PyType_Ready") + +helper = class_source.index("_PyClassObject_InitTypes(void)") +helper_body_start = class_source.index("\n{\n", helper) + 2 +helper_body_end = class_source.index("\n}\n", helper_body_start) +helper_body = class_source[helper_body_start:helper_body_end] +assignments = { + "PyMethod_Type": ( + "tp_name", "tp_basicsize", "tp_dealloc", "tp_vectorcall_offset", "tp_repr", + "tp_hash", "tp_call", "tp_getattro", "tp_setattro", "tp_flags", "tp_doc", + "tp_traverse", "tp_richcompare", "tp_weaklistoffset", "tp_methods", "tp_members", + "tp_getset", "tp_new", + ), + "PyInstanceMethod_Type": ( + "tp_name", "tp_basicsize", "tp_dealloc", "tp_repr", "tp_call", "tp_getattro", + "tp_setattro", "tp_flags", "tp_doc", "tp_traverse", "tp_richcompare", + "tp_members", "tp_getset", "tp_descr_get", "tp_new", + ), +} +for symbol, fields in assignments.items(): + for field in fields: + assignment = symbol + "." + field + " =" + if assignment not in helper_body: + raise SystemExit("missing legacy setup assignment " + assignment) +print("classobject VC6 type initialization coverage: PASS") diff --git a/scripts/vc6-codeobject-c89.test.py b/scripts/vc6-codeobject-c89.test.py new file mode 100644 index 0000000..38d80d2 --- /dev/null +++ b/scripts/vc6-codeobject-c89.test.py @@ -0,0 +1,353 @@ +#!/usr/bin/env python3 +"""Check early codeobject.c C89 locals and evaluation order.""" +import sys +from pathlib import Path + +if len(sys.argv) != 2: + raise SystemExit("usage: vc6-codeobject-c89.test.py codeobject.c") +source = Path(sys.argv[1]).read_text() + +def body_for(signature): + start = source.index(signature) + brace = source.index("{", start) + depth = 0 + for pos in range(brace, len(source)): + if source[pos] == "{": + depth += 1 + elif source[pos] == "}": + depth -= 1 + if depth == 0: + return source[brace + 1:pos] + raise SystemExit("unterminated function " + signature) + +body = body_for("intern_string_constants(PyObject *tuple, int *modified)") +if body.count("Py_ssize_t i;") != 1 or "for (Py_ssize_t i =" in body: + raise SystemExit("intern_string_constants loop index must be declared at function entry") +if body.index("Py_ssize_t i;") > body.index("for (i ="): + raise SystemExit("intern_string_constants loop index is declared too late") +frozen = body[body.index("else if (PyFrozenSet_CheckExact(v))") :] +if frozen.index("int tmp_modified;") > frozen.index("if (tmp == NULL)"): + raise SystemExit("tmp_modified must be declared before the first statement in the frozen-set block") +order = ("PyObject *tmp = PySequence_Tuple(v);", "if (tmp == NULL)", "tmp_modified = 0;", "intern_string_constants(tmp, &tmp_modified)") +if [frozen.index(x) for x in order] != sorted(frozen.index(x) for x in order): + raise SystemExit("frozen-set tuple construction/error/intern order changed") + +body = body_for("get_localsplus_counts(PyObject *names, PyObject *kinds,") +if body.count("int i;") != 1 or "for (int i =" in body: + raise SystemExit("get_localsplus_counts loop index must be declared at function entry") +if body.index("int i;") > body.index("for (i ="): + raise SystemExit("get_localsplus_counts loop index is declared too late") + +body = body_for("get_localsplus_names(PyCodeObject *co, _PyLocals_Kind kind, int num)") +if "int index;" not in body or "int offset;" not in body: + raise SystemExit("get_localsplus_names indices must be declared at function entry") +if "for (int offset =" in body or body.index("int index;") > body.index("if (names == NULL)"): + raise SystemExit("get_localsplus_names declarations must precede execution") +order = ("if (names == NULL)", "index = 0;", "for (offset =", "_PyLocals_Kind k;", "k = _PyLocals_GetKind", "assert(index < num);", "name = PyTuple_GET_ITEM", "Py_INCREF(name);", "PyTuple_SET_ITEM(names, index, name);") +if [body.index(x) for x in order] != sorted(body.index(x) for x in order): + raise SystemExit("get_localsplus_names allocation/reference/insertion order changed") + +body = body_for("_PyCode_Validate(struct _PyCodeConstructor *con)") +for name in ("nlocals", "nplainlocals"): + if "int " + name + ";" not in body: + raise SystemExit("_PyCode_Validate " + name + " must be declared at function entry") +if body.index("int nlocals;") > body.index("/* Check argument types */"): + raise SystemExit("_PyCode_Validate locals must precede validation") +if "int nplainlocals =" in body: + raise SystemExit("_PyCode_Validate nplainlocals initializer must stay at its execution point") +order = ("/* Check argument types */", "/* Make sure that code is indexable", "get_localsplus_counts(", "nplainlocals = nlocals -") +if [body.index(x) for x in order] != sorted(body.index(x) for x in order): + raise SystemExit("_PyCode_Validate validation/count computation order changed") + +body = body_for("init_code(PyCodeObject *co, struct _PyCodeConstructor *con)") +if "int entry_point;" not in body or body.index("int entry_point;") > body.index("get_localsplus_counts("): + raise SystemExit("init_code entry_point must be declared before execution") +order = ("memcpy(_PyCode_CODE(co)", "entry_point = 0;", "while (entry_point < Py_SIZE(co)", "co->_co_firsttraceable = entry_point;") +if [body.index(x) for x in order] != sorted(body.index(x) for x in order): + raise SystemExit("init_code byte copy / entry-point scan order changed") + +body = body_for("remove_column_info(PyObject *locations)") +for name in ("uint8_t *output;", "Py_ssize_t write_offset;"): + if name not in body: + raise SystemExit("remove_column_info must declare " + name) +if body.index("uint8_t *output;") > body.index("if (res == NULL)"): + raise SystemExit("remove_column_info output declaration must precede execution") +loop = body[body.index("while (offset <") : body.index("\n }\n write_offset =")] +if loop.index("int code;") > loop.index("write_offset = output -"): + raise SystemExit("remove_column_info loop code local must be declared at block entry") +order = ("if (res == NULL)", "output = (uint8_t *)PyBytes_AS_STRING(res);", "while (offset <", "code = (data[offset] >> 3) & 15;", "\n }\n write_offset = output -", "if (_PyBytes_Resize(&res, write_offset))") +if [body.index(x) for x in order] != sorted(body.index(x) for x in order): + raise SystemExit("remove_column_info allocation/resizing order changed") + +body = body_for("_PyCode_New(struct _PyCodeConstructor *con)") +for declaration in ("PyObject *replacement_locations;", "Py_ssize_t size;", "PyCodeObject *co;"): + if declaration not in body or body.index(declaration) > body.index("/* Ensure that strings are ready Unicode string */"): + raise SystemExit("_PyCode_New entry declaration missing or late: " + declaration) +if "PyObject *replacement_locations =" in body or "Py_ssize_t size =" in body or "PyCodeObject *co =" in body: + raise SystemExit("_PyCode_New initializers must remain at their original execution points") +order = ("/* Ensure that strings are ready Unicode string */", "intern_strings(con->names)", "intern_string_constants(con->consts", "replacement_locations = NULL;", "if (!_Py_GetConfig()->code_debug_ranges)", "replacement_locations = remove_column_info", "con->linetable = replacement_locations;", "size = PyBytes_GET_SIZE(con->code)", "co = PyObject_NewVar", "if (co == NULL)", "init_code(co, con);") +if [body.index(x) for x in order] != sorted(body.index(x) for x in order): + raise SystemExit("_PyCode_New validation/allocation order changed") +if not (body.index("init_code(co, con);") < body.rindex("Py_XDECREF(replacement_locations);") < body.index("return co;")): + raise SystemExit("_PyCode_New success cleanup order changed") + +body = body_for("PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount,") +for declaration in ("int nvarnames;", "int ncellvars;", "int nfreevars;", "int nlocalsplus;", "int offset;", "int i;", "int j;", "PyObject *local_name;", "int argoffset;", "int cmp;", "_PyLocals_Kind kind;", "struct _PyCodeConstructor con = {0};"): + if declaration not in body or body.index(declaration) > body.index("if (varnames == NULL"): + raise SystemExit("PyCode_NewWithPosOnlyArgs entry declaration missing or late: " + declaration) +if "for (int " in body or "int nvarnames =" in body or "int offset =" in body: + raise SystemExit("PyCode_NewWithPosOnlyArgs contains a C99 local declaration") +fields = ("filename", "name", "qualname", "flags", "code", "firstlineno", "linetable", "consts", "names", "localsplusnames", "localspluskinds", "argcount", "posonlyargcount", "kwonlyargcount", "stacksize", "exceptiontable") +field_positions = [body.index("con." + field + " =") for field in fields] +if field_positions != sorted(field_positions): + raise SystemExit("code constructor field initialization order changed") +order = ("if (varnames == NULL", "nvarnames =", "ncellvars =", "nfreevars =", "nlocalsplus =", "localsplusnames = PyTuple_New", "localspluskinds = PyBytes_FromStringAndSize", "offset = 0;", "for (i = 0;", "for (i = 0;", "for (j = 0;", "for (i = 0;", "_PyTuple_Resize", "con.filename =", "_PyCode_Validate(&con)", "co = _PyCode_New(&con);", "error:", "Py_XDECREF(localsplusnames);", "Py_XDECREF(localspluskinds);") +positions = [] +for item in order: + positions.append(body.index(item, positions[-1] + 1 if positions else 0)) +if positions != sorted(positions): + raise SystemExit("legacy constructor iteration/validation/cleanup order changed") + +body = body_for("PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)") +if "struct _PyCodeConstructor con = {0};" not in body: + raise SystemExit("PyCode_NewEmpty constructor must be zero-initialized at function entry") +fields = ("filename", "name", "qualname", "code", "firstlineno", "linetable", "consts", "names", "localsplusnames", "localspluskinds", "exceptiontable", "stacksize") +field_positions = [body.index("con." + field + " =") for field in fields] +if field_positions != sorted(field_positions): + raise SystemExit("PyCode_NewEmpty constructor field order changed") +order = ("nulltuple = PyTuple_New", "funcname_ob = PyUnicode_FromString", "filename_ob = PyUnicode_DecodeFSDefault", "code_ob = PyBytes_FromStringAndSize", "linetable_ob = PyBytes_FromStringAndSize", "con.filename =", "con.name =", "con.qualname =", "con.code =", "con.firstlineno =", "con.linetable =", "con.consts =", "con.names =", "con.localsplusnames =", "con.localspluskinds =", "con.exceptiontable =", "con.stacksize =", "result = _PyCode_New(&con);", "failed:", "Py_XDECREF(nulltuple);", "return result;") +positions = [body.index(x) for x in order] +if positions != sorted(positions): + raise SystemExit("PyCode_NewEmpty allocation/cleanup order changed") + +body = body_for("_PyCode_CreateLineArray(PyCodeObject *co)") +for declaration in ("PyCodeAddressRange bounds;", "int size;", "int max_line;"): + if declaration not in body or body.index(declaration) > body.index("assert(co->_co_linearray == NULL);"): + raise SystemExit("line-array entry declaration missing or late: " + declaration) +if body.count("for (int index =") != 0: + raise SystemExit("line-array inner loop must not declare its index in the for header") +order = ("assert(co->_co_linearray == NULL);", "max_line = 0;", "_PyCode_InitAddressRange(co, &bounds);", "while(_PyLineTable_NextAddressRange(&bounds))", "if (max_line < (1 << 15))", "co->_co_linearray = PyMem_Malloc", "if (co->_co_linearray == NULL)", "co->_co_linearray_entry_size = size;", "_PyCode_InitAddressRange(co, &bounds);", "for (index = start; index < end; index++)") +positions = [] +for item in order: + positions.append(body.index(item, positions[-1] + 1 if positions else 0)) +if positions != sorted(positions): + raise SystemExit("line-array range/allocation/fill order changed") +body = body_for("PyCode_Addr2Line(PyCodeObject *co, int addrq)") +if body.count("PyCodeAddressRange bounds;") != 1 or body.index("PyCodeAddressRange bounds;") > body.index("if (addrq < 0)"): + raise SystemExit("PyCode_Addr2Line bounds must be declared before execution") +order = ("if (addrq < 0)", "return co->co_firstlineno;", "assert(addrq >= 0", "if (co->_co_linearray)", "return _PyCode_LineNumberFromArray", "_PyCode_InitAddressRange(co, &bounds);", "return _PyCode_CheckLineNumber(addrq, &bounds);") +positions = [body.index(x) for x in order] +if positions != sorted(positions): + raise SystemExit("PyCode_Addr2Line early-return/lookup/range initialization order changed") + +body = body_for("_PyCode_InitAddressRange(PyCodeObject* co, PyCodeAddressRange *bounds)") +first_statement = body.index("assert(co->co_linetable != NULL);") +for declaration in ("const char *linetable;", "Py_ssize_t length;"): + if body.count(declaration) != 1 or body.index(declaration) > first_statement: + raise SystemExit("_PyCode_InitAddressRange entry declaration missing or late: " + declaration) +order = ( + "assert(co->co_linetable != NULL);", + "\n linetable = PyBytes_AS_STRING(co->co_linetable);", + "\n length = PyBytes_GET_SIZE(co->co_linetable);", + "_PyLineTable_InitAddressRange(linetable, length, co->co_firstlineno, bounds);", + "return bounds->ar_line;", +) +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("_PyCode_InitAddressRange assertion/byte access/length/range initialization order changed") + +body = body_for("previous_code_delta(PyCodeAddressRange *bounds)") +if body.count("const uint8_t *ptr;") != 1 or body.index("const uint8_t *ptr;") > body.index("if (bounds->ar_start == 0)"): + raise SystemExit("previous_code_delta ptr must be declared before execution") +if "const uint8_t *ptr =" in body: + raise SystemExit("previous_code_delta ptr assignment must remain after the first-entry return") +order = ("if (bounds->ar_start == 0)", "return 1;", "ptr = bounds->opaque.lo_next-1;", "while (((*ptr) & 128) == 0)", "ptr--;", "return (((*ptr) & 7) + 1) * sizeof(_Py_CODEUNIT);") +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("previous_code_delta first-entry return and backward scan order changed") + +body = body_for("advance_with_locations(PyCodeAddressRange *bounds, int *endline, int *column, int *endcolumn)") +first_statement = body.index("ASSERT_VALID_BOUNDS(bounds);") +for declaration in ("int first_byte;", "int code;"): + if body.count(declaration) != 1 or body.index(declaration) > first_statement: + raise SystemExit("advance_with_locations entry declaration missing or late: " + declaration) +if "int first_byte =" in body or "int code =" in body: + raise SystemExit("advance_with_locations initializers must remain at their execution points") +execution_prefix = """ASSERT_VALID_BOUNDS(bounds); + first_byte = read_byte(bounds); + code = (first_byte >> 3) & 15; + bounds->ar_start = bounds->ar_end; + bounds->ar_end = bounds->ar_start + ((first_byte & 7) + 1) * sizeof(_Py_CODEUNIT); + switch(code) {""" +if not body[first_statement:].startswith(execution_prefix): + raise SystemExit("advance_with_locations assertion/read/decode/range/dispatch order changed") + +body = body_for("PyCode_Addr2Location(PyCodeObject *co, int addrq,") +if body.count("PyCodeAddressRange bounds;") != 1 or body.index("PyCodeAddressRange bounds;") > body.index("if (addrq < 0)"): + raise SystemExit("PyCode_Addr2Location bounds must be declared before execution") +order = ( + "if (addrq < 0)", + "*start_line = *end_line = co->co_firstlineno;", + "*start_column = *end_column = 0;", + "return 1;", + "assert(addrq >= 0 && addrq < _PyCode_NBYTES(co));", + "_PyCode_InitAddressRange(co, &bounds);", + "_PyCode_CheckLineNumber(addrq, &bounds);", + "retreat(&bounds);", + "advance_with_locations(&bounds, end_line, start_column, end_column);", + "*start_line = bounds.ar_line;", + "return 1;", +) +positions = [] +for item in order: + positions.append(body.index(item, positions[-1] + 1 if positions else 0)) +if positions != sorted(positions): + raise SystemExit("PyCode_Addr2Location early-return/assertion/range initialization/lookup order changed") + +body = body_for("_PyLineTable_StartsLine(PyCodeAddressRange *range)") +for declaration in ("const uint8_t *ptr;", "int code;"): + if declaration not in body or body.index(declaration) > body.index("if (range->ar_start <= 0)"): + raise SystemExit("StartsLine entry declaration missing or late: " + declaration) +order = ("if (range->ar_start <= 0)", "ptr = range->opaque.lo_next;", "do {", "ptr--;") +positions = [body.index(x) for x in order] +if positions != sorted(positions): + raise SystemExit("StartsLine backward-scan order changed") + +body = body_for("emit_pair(PyObject **bytes, int *offset, int a, int b)") +if "unsigned char *lnotab;" not in body or body.index("unsigned char *lnotab;") > body.index("if (*offset + 2 >= len)"): + raise SystemExit("emit_pair lnotab pointer must be declared before possible resize") +order = ("if (*offset + 2 >= len)", "if (_PyBytes_Resize(bytes, len * 2) < 0)", "lnotab = (unsigned char *) PyBytes_AS_STRING(*bytes);", "lnotab += *offset;", "*lnotab++ = a;", "*lnotab++ = b;", "*offset += 2;") +if [body.index(x) for x in order] != sorted(body.index(x) for x in order): + raise SystemExit("emit_pair resize/pointer/write order changed") + +body = body_for("lineiter_next(lineiterator *li)") +for declaration in ("PyObject *start;", "PyObject *end;", "PyObject *line;", "PyObject *result;"): + if declaration not in body or body.index(declaration) > body.index("if (!_PyLineTable_NextAddressRange(bounds))"): + raise SystemExit("lineiter_next entry declaration missing or late: " + declaration) +order = ("if (!_PyLineTable_NextAddressRange(bounds))", "start = NULL;", "end = NULL;", "line = NULL;", "result = PyTuple_New(3);", "start = PyLong_FromLong", "end = PyLong_FromLong", "if (bounds->ar_line < 0)", "if (result == NULL", "PyTuple_SET_ITEM(result, 0, start);", "return result;", "error:", "Py_XDECREF(start);", "Py_XDECREF(result);") +if [body.index(x) for x in order] != sorted(body.index(x) for x in order): + raise SystemExit("lineiter_next conversion/insertion/cleanup order changed") + +# Extra-slot locals must be C89 declarations; preserve the complete execution body. +body = body_for('_PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra)') +entry = 'PyCodeObject *o;\n_PyCodeObjectExtra *co_extra;' +if " ".join(body.split()).startswith(" ".join(entry.split())) is False: + raise SystemExit("_PyCode_GetExtra locals must be declared at function entry") +execution = body +for declaration in entry.splitlines(): + execution = execution.replace(declaration, "", 1) +expected_execution = """if (!PyCode_Check(code)) { + PyErr_BadInternalCall(); + return -1; + } + + o = (PyCodeObject*) code; + co_extra = (_PyCodeObjectExtra*) o->co_extra; + + if (co_extra == NULL || index < 0 || co_extra->ce_size <= index) { + *extra = NULL; + return 0; + } + + *extra = co_extra->ce_extras[index]; + return 0;""" +if " ".join(execution.split()) != " ".join(expected_execution.split()): + raise SystemExit("_PyCode_GetExtra branches/allocation/free/assignment order changed") + +body = body_for('_PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra)') +entry = 'PyInterpreterState *interp = _PyInterpreterState_GET();\nPyCodeObject *o;\n_PyCodeObjectExtra *co_extra;\nPy_ssize_t i;\nfreefunc free;' +if " ".join(body.split()).startswith(" ".join(entry.split())) is False: + raise SystemExit("_PyCode_SetExtra locals must be declared at function entry") +execution = body +for declaration in entry.splitlines(): + execution = execution.replace(declaration, "", 1) +expected_execution = """if (!PyCode_Check(code) || index < 0 || + index >= interp->co_extra_user_count) { + PyErr_BadInternalCall(); + return -1; + } + + o = (PyCodeObject*) code; + co_extra = (_PyCodeObjectExtra *) o->co_extra; + + if (co_extra == NULL || co_extra->ce_size <= index) { + i = (co_extra == NULL ? 0 : co_extra->ce_size); + co_extra = PyMem_Realloc( + co_extra, + sizeof(_PyCodeObjectExtra) + + (interp->co_extra_user_count-1) * sizeof(void*)); + if (co_extra == NULL) { + return -1; + } + for (; i < interp->co_extra_user_count; i++) { + co_extra->ce_extras[i] = NULL; + } + co_extra->ce_size = interp->co_extra_user_count; + o->co_extra = co_extra; + } + + if (co_extra->ce_extras[index] != NULL) { + free = interp->co_extra_freefuncs[index]; + if (free != NULL) { + free(co_extra->ce_extras[index]); + } + } + + co_extra->ce_extras[index] = extra; + return 0;""" +if " ".join(execution.split()) != " ".join(expected_execution.split()): + raise SystemExit("_PyCode_SetExtra branches/allocation/free/assignment order changed") + +body = body_for("deopt_code(_Py_CODEUNIT *instructions, Py_ssize_t len)") +if "int i;" not in body or "for (int i =" in body: + raise SystemExit("deopt_code loop index must be declared before the loop") +order = ("for (i = 0; i < len; i++)", "_Py_CODEUNIT instruction = instructions[i];", "int opcode =", "int caches =", "instructions[i] =", "while (caches--)", "instructions[++i] =") +if [body.index(x) for x in order] != sorted(body.index(x) for x in order): + raise SystemExit("deopt_code instruction/cache rewrite order changed") + +body = body_for("_PyCode_GetCode(PyCodeObject *co)") +if "PyObject *code;" not in body or body.index("PyObject *code;") > body.index("if (co->_co_code != NULL)"): + raise SystemExit("_PyCode_GetCode code object must be declared at entry") +if "PyObject *code =" in body: + raise SystemExit("_PyCode_GetCode allocation must remain after cache check") +order = ("if (co->_co_code != NULL)", "return Py_NewRef(co->_co_code);", "code = PyBytes_FromStringAndSize", "if (code == NULL)", "deopt_code(", "co->_co_code = Py_NewRef(code);", "return code;") +if [body.index(x) for x in order] != sorted(body.index(x) for x in order): + raise SystemExit("_PyCode_GetCode cached return/allocation/deopt/cache order changed") + +body = body_for("code_dealloc(PyCodeObject *co)") +if "Py_ssize_t i;" not in body or "for (Py_ssize_t i =" in body: + raise SystemExit("code_dealloc loop index must be declared before its loop") +order = ("if (co->co_extra != NULL)", "Py_ssize_t i;", "for (i = 0; i < co_extra->ce_size; i++)", "free_extra = interp->co_extra_freefuncs[i];", "free_extra(co_extra->ce_extras[i]);", "PyMem_Free(co_extra);", "Py_XDECREF(co->co_consts);", "PyObject_Free(co);") +if [body.index(x) for x in order] != sorted(body.index(x) for x in order): + raise SystemExit("code_dealloc extra cleanup/refcount order changed") + +body = body_for("code_richcompare(PyObject *self, PyObject *other, int op)") +if "int i;" not in body or "for (int i =" in body: + raise SystemExit("code_richcompare loop index must be declared before its loop") +order = ("if ((op != Py_EQ", "co = (PyCodeObject *)self;", "cp = (PyCodeObject *)other;", "for (i = 0; i < Py_SIZE(co); i++)", "_Py_SET_OPCODE(co_instr", "eq = co_instr == cp_instr;", "i += _PyOpcode_Caches", "consts1 = _PyCode_ConstantKey", "Py_DECREF(consts1);", "Py_DECREF(consts2);", "Py_INCREF(res);", "return res;") +if [body.index(x) for x in order] != sorted(body.index(x) for x in order): + raise SystemExit("code_richcompare validation/instruction/constants order changed") + +body = body_for("code_hash(PyCodeObject *co)") +if "Py_hash_t h4, h5;" not in body or body.index("Py_hash_t h4, h5;") > body.index("h0 = PyObject_Hash"): + raise SystemExit("code_hash h4/h5 must be declared before hash evaluation") +if "Py_hash_t h4 =" in body or "Py_hash_t h5 =" in body: + raise SystemExit("code_hash h4/h5 hash calls must remain at their original execution points") +order = ("h3 = PyObject_Hash", "h4 = PyObject_Hash", "if (h4 == -1)", "h5 = PyObject_Hash", "if (h5 == -1)", "h = h0 ^ h1 ^ h2 ^ h3 ^ h4 ^ h5 ^") +if [body.index(x) for x in order] != sorted(body.index(x) for x in order): + raise SystemExit("code_hash error handling/hash combination order changed") +body = body_for("code_replace_impl(PyCodeObject *self, int co_argcount,") +for declaration in ("PyObject *code;", "PyCodeObject *co;", "PyObject *varnames;", "PyObject *cellvars;", "PyObject *freevars;"): + if declaration not in body or body.index(declaration) > body.index("#define CHECK_INT_ARG(ARG)"): + raise SystemExit("code_replace_impl entry declaration missing or late: " + declaration) +for initializer in ("PyObject *code =", "PyCodeObject *co =", "PyObject *varnames =", "PyObject *cellvars =", "PyObject *freevars ="): + if initializer in body: + raise SystemExit("code_replace_impl initializer must remain after validation/audit: " + initializer) +order = ("CHECK_INT_ARG(co_firstlineno);", "code = NULL;", "if (co_code == NULL)", "if (PySys_Audit", "co = NULL;", "varnames = NULL;", "cellvars = NULL;", "freevars = NULL;", "if (co_varnames == NULL)", "if (co_cellvars == NULL)", "if (co_freevars == NULL)", "co = PyCode_NewWithPosOnlyArgs(", "error:") +if [body.index(x) for x in order] != sorted(body.index(x) for x in order): + raise SystemExit("code_replace_impl validation/audit/allocation order changed") +cleanup = [body.rindex(x) for x in ("Py_XDECREF(code);", "Py_XDECREF(varnames);", "Py_XDECREF(cellvars);", "Py_XDECREF(freevars);")] +if cleanup != sorted(cleanup) or cleanup[-1] > body.index("return (PyObject *)co;"): + raise SystemExit("code_replace_impl cleanup order changed") + +print("codeobject C89 declarations and ownership order: PASS") diff --git a/scripts/vc6-descrobject-classmethod-c89.test.py b/scripts/vc6-descrobject-classmethod-c89.test.py new file mode 100644 index 0000000..bd87531 --- /dev/null +++ b/scripts/vc6-descrobject-classmethod-c89.test.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +"""Check classmethod_get C89 local placement and validation order.""" +import sys +from pathlib import Path + +if len(sys.argv) != 3: + raise SystemExit("usage: vc6-descrobject-classmethod-c89.test.py descrobject.c object.c") +source = Path(sys.argv[1]).read_text() +object_source = Path(sys.argv[2]).read_text() + +def body_for(signature, text=None): + if text is None: + text = source + start = text.index(signature) + brace = text.index("{", start) + depth = 0 + for pos in range(brace, len(text)): + if text[pos] == "{": + depth += 1 + elif text[pos] == "}": + depth -= 1 + if depth == 0: + return text[brace + 1:pos] + raise SystemExit("unterminated function: " + signature) + +signature = "classmethod_get(PyMethodDescrObject *descr, PyObject *obj, PyObject *type)" +body = body_for(signature) +if "PyTypeObject *cls;" not in body or body.index("PyTypeObject *cls;") > body.index("if (type == NULL)"): + raise SystemExit("classmethod_get cls must be declared before validation") +if "PyTypeObject *cls =" in body: + raise SystemExit("classmethod_get cls initialization must remain after subtype validation") +order = ("if (type == NULL)", "if (!PyType_Check(type))", "if (!PyType_IsSubtype", "cls = NULL;", "if (descr->d_method->ml_flags & METH_METHOD)", "cls = descr->d_common.d_type;", "return PyCMethod_New(descr->d_method, type, NULL, cls);") +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("classmethod_get validation/class-selection/construction order changed") + + +def require_entry_declarations(signature, declarations, first_statement): + body = body_for(signature) + first = body.index(first_statement) + for declaration in declarations: + if declaration not in body or body.index(declaration) > first: + raise SystemExit(signature + " local missing or late: " + declaration) + return body + +body = require_entry_declarations("method_check_args(PyObject *func,", ("PyObject *self;",), "assert(!PyErr_Occurred());") +if body.index("if (nargs < 1)") > body.index("self = args[0];") or body.index("self = args[0];") > body.index("descr_check((PyDescrObject *)func, self)"): + raise SystemExit("method_check_args validation/self-check order changed") + +wrappers = ( + ("method_vectorcall_VARARGS(\n", ("PyObject *argstuple;", "PyCFunction meth;", "PyObject *result;"), "if (method_check_args(func, args, nargs, kwnames))", ("argstuple = _PyTuple_FromArray", "meth = (PyCFunction)method_enter_call", "result = _PyCFunction_TrampolineCall", "_Py_LeaveRecursiveCallTstate(tstate);", "return result;")), + ("method_vectorcall_VARARGS_KEYWORDS(\n", ("PyObject *argstuple;", "PyObject *result;", "PyObject *kwdict;", "PyCFunctionWithKeywords meth;"), "if (method_check_args(func, args, nargs, NULL))", ("argstuple = _PyTuple_FromArray", "result = NULL;", "kwdict = NULL;", "if (kwnames != NULL", "meth = (PyCFunctionWithKeywords)", "result = _PyCFunctionWithKeywords_TrampolineCall", "_Py_LeaveRecursiveCallTstate(tstate);", "exit:", "Py_DECREF(argstuple);", "Py_XDECREF(kwdict);", "return result;")), + ("method_vectorcall_FASTCALL_KEYWORDS_METHOD(\n", ("PyCMethod meth;", "PyObject *result;"), "if (method_check_args(func, args, nargs, NULL))", ("meth = (PyCMethod) method_enter_call", "result = meth(args[0],", "_Py_LeaveRecursiveCall();", "return result;")), + ("method_vectorcall_FASTCALL(\n", ("_PyCFunctionFast meth;", "PyObject *result;"), "if (method_check_args(func, args, nargs, kwnames))", ("meth = (_PyCFunctionFast)", "result = meth(args[0],", "_Py_LeaveRecursiveCallTstate(tstate);", "return result;")), + ("method_vectorcall_FASTCALL_KEYWORDS(\n", ("_PyCFunctionFastWithKeywords meth;", "PyObject *result;"), "if (method_check_args(func, args, nargs, NULL))", ("meth = (_PyCFunctionFastWithKeywords)", "result = meth(args[0],", "_Py_LeaveRecursiveCallTstate(tstate);", "return result;")), + ("method_vectorcall_NOARGS(\n", ("PyObject *funcstr;", "PyCFunction meth;", "PyObject *result;"), "if (method_check_args(func, args, nargs, kwnames))", ("if (nargs != 1)", "funcstr = _PyObject_FunctionStr(func);", "meth = (PyCFunction)method_enter_call", "result = _PyCFunction_TrampolineCall", "_Py_LeaveRecursiveCallTstate(tstate);", "return result;")), + ("method_vectorcall_O(\n", ("PyObject *funcstr;", "PyCFunction meth;", "PyObject *result;"), "if (method_check_args(func, args, nargs, kwnames))", ("if (nargs != 2)", "funcstr = _PyObject_FunctionStr(func);", "meth = (PyCFunction)method_enter_call", "result = _PyCFunction_TrampolineCall", "_Py_LeaveRecursiveCallTstate(tstate);", "return result;")), +) +for signature, declarations, first_statement, order in wrappers: + body = require_entry_declarations(signature, declarations, first_statement) + positions = [body.index(item) for item in order] + if positions != sorted(positions): + raise SystemExit(signature + " dispatch/cleanup order changed") + +body = require_entry_declarations("classmethoddescr_call(PyMethodDescrObject *descr,", ("PyObject *self;", "PyObject *bound;", "PyObject *res;"), "if (argc < 1)") +order = ("argc = PyTuple_GET_SIZE(args);", "if (argc < 1)", "self = PyTuple_GET_ITEM(args, 0);", "bound = classmethod_get(descr, NULL, self);", "if (bound == NULL)", "res = PyObject_VectorcallDict", "Py_DECREF(bound);", "return res;") +if [body.index(item) for item in order] != sorted(body.index(item) for item in order): + raise SystemExit("classmethoddescr_call bind/call/decref order changed") +mapping_guard = "#if !defined(MS_WINDOWS) || !defined(_MSC_VER) || _MSC_VER >= 1300" +if mapping_guard not in source or "static PyNumberMethods mappingproxy_as_number = {0};" not in source: + raise SystemExit("mappingproxy number table must avoid designated initialization on VC6") +number_init = body_for("_PyDesc_InitTypes(void)") +if "mappingproxy_as_number.nb_or = mappingproxy_or;" not in number_init or "mappingproxy_as_number.nb_inplace_or = mappingproxy_ior;" not in number_init: + raise SystemExit("VC6 mappingproxy slots must be assigned before type readiness") +init_body = body_for("_PyTypes_InitTypes(PyInterpreterState *interp)", object_source) +order = ("_PyClassObject_InitTypes();", "_PyDesc_InitTypes();", "for (size_t i=0;", "PyType_Ready(type)") +positions = [init_body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("legacy static type slots must be initialized before PyType_Ready") + +body = body_for("PyDescr_NewMethod(PyTypeObject *type, PyMethodDef *method)") +for declaration in ("PyMethodDescrObject *descr;", "vectorcallfunc vectorcall;"): + if declaration not in body or body.index(declaration) > body.index("switch (method->ml_flags"): + raise SystemExit("PyDescr_NewMethod local missing or late: " + declaration) +if body.index("PyMethodDescrObject *descr;") > body.index("switch (method->ml_flags"): + raise SystemExit("PyDescr_NewMethod descr must be declared before flag dispatch") +order = ("switch (method->ml_flags", "vectorcall = method_vectorcall_VARARGS;", "default:", "descr = (PyMethodDescrObject *)descr_new", "descr->d_method = method;", "descr->vectorcall = vectorcall;", "return (PyObject *)descr;") +positions = [body.index(item) for item in order] +if positions != sorted(positions): + raise SystemExit("PyDescr_NewMethod dispatch/allocation/initialization order changed") +body = body_for("property_set_name(PyObject *self, PyObject *args)") +for declaration in ("propertyobject *prop;", "PyObject *name;"): + if declaration not in body or body.index(declaration) > body.index("if (PyTuple_GET_SIZE(args) != 2)"): + raise SystemExit("property_set_name local missing or late: " + declaration) +if "propertyobject *prop =" in body or "PyObject *name =" in body: + raise SystemExit("property_set_name name/property assignments must remain after arity validation") +order = ("if (PyTuple_GET_SIZE(args) != 2)", "prop = (propertyobject *)self;", "name = PyTuple_GET_ITEM(args, 1);", "Py_XINCREF(name);", "Py_XSETREF(prop->prop_name, name);", "Py_RETURN_NONE;") +if [body.index(item) for item in order] != sorted(body.index(item) for item in order): + raise SystemExit("property_set_name arity/reference-replacement order changed") + +body = body_for("property_descr_get(PyObject *self, PyObject *obj, PyObject *type)") +if "propertyobject *gs;" not in body or body.index("propertyobject *gs;") > body.index("if (obj == NULL || obj == Py_None)"): + raise SystemExit("property_descr_get gs must be declared before its early return") +if "propertyobject *gs =" in body: + raise SystemExit("property_descr_get property cast must remain after its early return") +order = ("if (obj == NULL || obj == Py_None)", "return self;", "gs = (propertyobject *)self;", "if (gs->prop_get == NULL)", "PyObject *qualname = PyType_GetQualName", "return PyObject_CallOneArg(gs->prop_get, obj);") +if [body.index(item) for item in order] != sorted(body.index(item) for item in order): + raise SystemExit("property_descr_get early return/getter error/call order changed") +print("descrobject C89 declarations, dispatch, and VC6 type init: PASS") diff --git a/scripts/vc6-dict-consistency-c89.test.py b/scripts/vc6-dict-consistency-c89.test.py new file mode 100644 index 0000000..9ee661a --- /dev/null +++ b/scripts/vc6-dict-consistency-c89.test.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 +"""Check C89 declarations and logic order in unicode equality and dict checks.""" +import sys +from pathlib import Path + +if len(sys.argv) != 3: + raise SystemExit("usage: vc6-dict-consistency-c89.test.py eq.h dictobject.c") +eq_source = Path(sys.argv[1]).read_text() +dict_source = Path(sys.argv[2]).read_text() + +def body_for(source, signature): + start = source.index(signature) + brace = source.index("{", start) + depth = 0 + for pos in range(brace, len(source)): + if source[pos] == "{": + depth += 1 + elif source[pos] == "}": + depth -= 1 + if depth == 0: + return source[brace + 1:pos] + raise SystemExit("unterminated function: " + signature) + +body = body_for(eq_source, "unicode_eq(PyObject *aa, PyObject *bb)") +for declaration in ("PyUnicodeObject *a;", "PyUnicodeObject *b;"): + if declaration not in body or body.index(declaration) > body.index("assert(PyUnicode_Check(aa));"): + raise SystemExit("unicode_eq local missing or late: " + declaration) +if "PyUnicodeObject *a =" in body or "PyUnicodeObject *b =" in body: + raise SystemExit("unicode_eq object casts must remain after readiness assertions") +order = ("assert(PyUnicode_Check(aa));", "assert(PyUnicode_Check(bb));", "assert(PyUnicode_IS_READY(aa));", "assert(PyUnicode_IS_READY(bb));", "a = (PyUnicodeObject *)aa;", "b = (PyUnicodeObject *)bb;", "if (PyUnicode_GET_LENGTH(a) !=", "return memcmp(") +if [body.index(item) for item in order] != sorted(body.index(item) for item in order): + raise SystemExit("unicode_eq validation/cast/comparison order changed") + +body = body_for(dict_source, "_PyDict_CheckConsistency(PyObject *op, int check_content)") +declarations = ("PyDictObject *mp;", "PyDictKeysObject *keys;", "int splitted;", "Py_ssize_t usable;", "Py_ssize_t i;", "Py_ssize_t ix;", "PyDictKeyEntry *key_entries;", "PyDictKeyEntry *dict_entry;", "PyDictUnicodeEntry *unicode_entries;", "PyDictUnicodeEntry *unicode_entry;", "PyObject *key;", "Py_hash_t hash;", "int duplicate_check;", "int index;") +for declaration in declarations: + if declaration not in body or body.index(declaration) > body.index("assert(op != NULL);"): + raise SystemExit("_PyDict_CheckConsistency local missing or late: " + declaration) +for declaration in declarations: + prefix = declaration.split(";")[0] + if prefix + " =" in body: + raise SystemExit("_PyDict_CheckConsistency initializer must remain at execution point: " + declaration) +if "for (Py_ssize_t i" in body: + raise SystemExit("_PyDict_CheckConsistency loop index must be declared before the loops") +order = ("assert(op != NULL);", "CHECK(PyDict_Check(op));", "mp = (PyDictObject *)op;", "keys = mp->ma_keys;", "splitted = _PyDict_HasSplitTable(mp);", "usable = USABLE_FRACTION", "if (check_content)", "for (i=0; i < DK_SIZE(keys); i++)", "ix = dictkeys_get_index", "key_entries = DK_ENTRIES(keys);", "for (i=0; i < usable; i++)", "dict_entry = &key_entries[i];", "unicode_entries = DK_UNICODE_ENTRIES(keys);", "unicode_entry = &unicode_entries[i];", "duplicate_check = 0;", "for (i=0; i < mp->ma_used; i++)", "index = get_index_from_order(mp, i);", "return 1;") +if [body.index(item) for item in order] != sorted(body.index(item) for item in order): + raise SystemExit("_PyDict_CheckConsistency validation/table traversal order changed") +body = body_for(dict_source, "new_keys_object(uint8_t log2_size, bool unicode)") +if "struct _Py_dict_state *state;" not in body or body.index("struct _Py_dict_state *state;") > body.index("assert(log2_size >= PyDict_LOG_MINSIZE);"): + raise SystemExit("new_keys_object freelist state must be declared at function entry") +if "struct _Py_dict_state *state =" in body: + raise SystemExit("new_keys_object state lookup must remain after size calculation") +order = ("assert(log2_size >= PyDict_LOG_MINSIZE);", "usable = USABLE_FRACTION", "if (log2_size < 8)", "state = get_dict_state();", "assert(state->keys_numfree != -1);", "if (log2_size == PyDict_LOG_MINSIZE", "dk = PyObject_Malloc", "dk->dk_refcnt = 1;") +if [body.index(item) for item in order] != sorted(body.index(item) for item in order): + raise SystemExit("new_keys_object size/freelist/allocation initialization order changed") +body = body_for(dict_source, "static void\nfree_keys_object(PyDictKeysObject *keys)") +for declaration in ("PyDictUnicodeEntry *unicode_entries;", "PyDictKeyEntry *key_entries;", "Py_ssize_t i, n;", "struct _Py_dict_state *state;"): + if declaration not in body or body.index(declaration) > body.index("assert(keys != Py_EMPTY_KEYS);"): + raise SystemExit("free_keys_object local missing or late: " + declaration) +if "struct _Py_dict_state *state =" in body or "for (Py_ssize_t i" in body: + raise SystemExit("free_keys_object assignments and indices must remain C89-compatible") +order = ("assert(keys != Py_EMPTY_KEYS);", "if (DK_IS_UNICODE(keys))", "unicode_entries = DK_UNICODE_ENTRIES(keys);", "for (i = 0, n = keys->dk_nentries;", "else {", "key_entries = DK_ENTRIES(keys);", "for (i = 0, n = keys->dk_nentries;", "state = get_dict_state();", "assert(state->keys_numfree != -1);", "if (DK_LOG_SIZE(keys) ==", "PyObject_Free(keys);") +positions = [] +for item in order: + positions.append(body.index(item, positions[-1] + 1 if positions else 0)) +if positions != sorted(positions): + raise SystemExit("free_keys_object entry traversal/freelist/free order changed") + +body = body_for(dict_source, "new_values(Py_ssize_t size)") +for declaration in ("size_t prefix_size;", "size_t n;", "uint8_t *mem;"): + if declaration not in body or body.index(declaration) > body.index("assert(size > 0);"): + raise SystemExit("new_values local missing or late: " + declaration) +if "size_t prefix_size =" in body or "size_t n =" in body or "uint8_t *mem =" in body: + raise SystemExit("new_values arithmetic/allocation must remain after its assertions") +order = ("assert(size > 0);", "prefix_size = _Py_SIZE_ROUND_UP", "assert(prefix_size < 256);", "n = prefix_size +", "mem = PyMem_Malloc(n);", "if (mem == NULL)", "mem[prefix_size-1] =", "return (PyDictValues*)") +if [body.index(item) for item in order] != sorted(body.index(item) for item in order): + raise SystemExit("new_values size/assert/allocation/initialization order changed") + +body = body_for(dict_source, "new_dict(PyDictKeysObject *keys,") +if "struct _Py_dict_state *state;" not in body or body.index("struct _Py_dict_state *state;") > body.index("assert(keys != NULL);"): + raise SystemExit("new_dict freelist state must be declared at function entry") +if "struct _Py_dict_state *state =" in body: + raise SystemExit("new_dict state lookup must remain at its execution point") +order = ("assert(keys != NULL);", "state = get_dict_state();", "assert(state->numfree != -1);", "if (state->numfree)", "mp = state->free_list", "mp = PyObject_GC_New", "mp->ma_keys = keys;", "ASSERT_CONSISTENT(mp);", "return (PyObject *)mp;") +if [body.index(item) for item in order] != sorted(body.index(item) for item in order): + raise SystemExit("new_dict freelist/allocation/object initialization order changed") +body = body_for(dict_source, "clone_combined_dict_keys(PyDictObject *orig)") +declarations = ("size_t keys_size;", "PyDictKeysObject *keys;", "PyObject **pkey;", "PyObject **pvalue;", "size_t offs;", "Py_ssize_t n;", "Py_ssize_t i;", "PyDictUnicodeEntry *unicode_ep0;", "PyDictKeyEntry *key_ep0;") +for declaration in declarations: + if declaration not in body or body.index(declaration) > body.index("assert(PyDict_Check(orig));"): + raise SystemExit("clone_combined_dict_keys local missing or late: " + declaration) +for initializer in ("size_t keys_size =", "PyDictKeysObject *keys =", "PyObject **pkey,", "size_t offs;\n if", "Py_ssize_t n =", "for (Py_ssize_t i ="): + if initializer in body: + raise SystemExit("clone_combined_dict_keys local initializer/declaration remains late: " + initializer) +order = ("assert(PyDict_Check(orig));", "keys_size = _PyDict_KeysSize", "keys = PyObject_Malloc(keys_size);", "memcpy(keys, orig->ma_keys, keys_size);", "if (DK_IS_UNICODE(orig->ma_keys))", "unicode_ep0 = DK_UNICODE_ENTRIES(keys);", "pkey = &unicode_ep0->me_key;", "else {", "key_ep0 = DK_ENTRIES(keys);", "pkey = &key_ep0->me_key;", "n = keys->dk_nentries;", "for (i = 0; i < n; i++)", "Py_INCREF(value);", "pvalue += offs;", "_Py_RefTotal++;", "return keys;") +positions = [] +for item in order: + positions.append(body.index(item, positions[-1] + 1 if positions else 0)) +if positions != sorted(positions): + raise SystemExit("clone_combined_dict_keys assertions/copy/reference transfer order changed") +body = body_for(dict_source, "unicodekeys_lookup_generic(PyDictObject *mp,") +if "int cmp;" not in body or body.index("int cmp;") > body.index("for (;;)"): + raise SystemExit("unicodekeys_lookup_generic comparison result must be declared before probing") +if "int cmp =" in body: + raise SystemExit("unicodekeys_lookup_generic compare must remain after retaining the key") +order = ("if (unicode_get_hash(ep->me_key) == hash)", "PyObject *startkey = ep->me_key;", "Py_INCREF(startkey);", "cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);", "Py_DECREF(startkey);", "if (cmp < 0)", "if (dk == mp->ma_keys && ep->me_key == startkey)") +if [body.index(item) for item in order] != sorted(body.index(item) for item in order): + raise SystemExit("unicodekeys_lookup_generic retain/compare/decref/restart order changed") +body = body_for(dict_source, "dictkeys_generic_lookup(PyDictObject *mp,") +if "int cmp;" not in body or body.index("int cmp;") > body.index("for (;;)"): + raise SystemExit("dictkeys_generic_lookup comparison result must be declared before probing") +if "int cmp =" in body: + raise SystemExit("dictkeys_generic_lookup compare must remain after retaining the key") +order = ("if (ep->me_hash == hash)", "PyObject *startkey = ep->me_key;", "Py_INCREF(startkey);", "cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);", "Py_DECREF(startkey);", "if (cmp < 0)", "if (dk == mp->ma_keys && ep->me_key == startkey)") +if [body.index(item) for item in order] != sorted(body.index(item) for item in order): + raise SystemExit("dictkeys_generic_lookup retain/compare/decref/restart order changed") +body = body_for(dict_source, "_PyDictKeys_StringLookup(PyDictKeysObject* dk,") +for declaration in ("DictKeysKind kind;", "Py_hash_t hash;"): + if declaration not in body or body.index(declaration) > body.index("kind = dk->dk_kind;"): + raise SystemExit("_PyDictKeys_StringLookup local missing or late: " + declaration) +if "Py_hash_t hash =" in body: + raise SystemExit("_PyDictKeys_StringLookup hash calculation must remain after key validation") +order = ("kind = dk->dk_kind;", "if (!PyUnicode_CheckExact(key)", "hash = unicode_get_hash(key);", "if (hash == -1)", "return unicodekeys_lookup_unicode(dk, key, hash);") +if [body.index(item) for item in order] != sorted(body.index(item) for item in order): + raise SystemExit("_PyDictKeys_StringLookup validation/hash/fallback order changed") + +body = body_for(dict_source, "find_empty_slot(PyDictKeysObject *keys,") +for declaration in ("size_t mask;", "size_t i;", "size_t perturb;", "Py_ssize_t ix;"): + if declaration not in body or body.index(declaration) > body.index("assert(keys != NULL);"): + raise SystemExit("find_empty_slot local missing or late: " + declaration) +if "for (size_t perturb" in body: + raise SystemExit("find_empty_slot perturb index must be declared before probing") +order = ("assert(keys != NULL);", "mask = DK_MASK(keys);", "i = hash & mask;", "ix = dictkeys_get_index(keys, i);", "for (perturb = hash;", "return i;") +if [body.index(item) for item in order] != sorted(body.index(item) for item in order): + raise SystemExit("find_empty_slot validation/probe order changed") + +body = body_for(dict_source, "insert_into_dictkeys(PyDictKeysObject *keys,") +for declaration in ("Py_hash_t hash;", "Py_ssize_t ix;", "Py_ssize_t hashpos;", "PyDictUnicodeEntry *ep;"): + if declaration not in body or body.index(declaration) > body.index("assert(PyUnicode_CheckExact(name));"): + raise SystemExit("insert_into_dictkeys local missing or late: " + declaration) +if any(token in body for token in ("Py_hash_t hash =", "Py_ssize_t ix =", "Py_ssize_t hashpos =", "PyDictUnicodeEntry *ep =")): + raise SystemExit("insert_into_dictkeys insertion state must be assigned at its execution point") +order = ("assert(PyUnicode_CheckExact(name));", "hash = unicode_get_hash(name);", "ix = unicodekeys_lookup_unicode", "Py_INCREF(name);", "hashpos = find_empty_slot(keys, hash);", "dictkeys_set_index(keys, hashpos, ix);", "ep->me_key = name;") +if [body.index(item) for item in order] != sorted(body.index(item) for item in order): + raise SystemExit("insert_into_dictkeys hash/refcount/table-update order changed") + +body = body_for(dict_source, "insert_to_emptydict(PyDictObject *mp,") +for declaration in ("int unicode;", "PyDictKeysObject *newkeys;", "size_t hashpos;", "PyDictUnicodeEntry *unicode_ep;", "PyDictKeyEntry *key_ep;"): + if declaration not in body or body.index(declaration) > body.index("assert(mp->ma_keys == Py_EMPTY_KEYS);"): + raise SystemExit("insert_to_emptydict local missing or late: " + declaration) +order = ("assert(mp->ma_keys == Py_EMPTY_KEYS);", "unicode = PyUnicode_CheckExact(key);", "newkeys = new_keys_object", "if (newkeys == NULL)", "dictkeys_decref(Py_EMPTY_KEYS);", "MAINTAIN_TRACKING(mp, key, value);", "hashpos = (size_t)hash", "dictkeys_set_index", "if (unicode)", "mp->ma_used++;") +if [body.index(item) for item in order] != sorted(body.index(item) for item in order): + raise SystemExit("insert_to_emptydict validation/allocation/tracking/insertion order changed") + +for signature in ("build_indices_generic(PyDictKeysObject *keys,", "build_indices_unicode(PyDictKeysObject *keys,"): + body = body_for(dict_source, signature) + for declaration in ("size_t mask;", "size_t i;", "size_t perturb;", "Py_ssize_t ix;", "Py_hash_t hash;"): + if declaration not in body or body.index(declaration) > body.index("mask = DK_MASK(keys);"): + raise SystemExit("build_indices local missing or late: " + declaration) + if any(token in body for token in ("for (Py_ssize_t ix", "for (size_t perturb", "size_t i = hash")): + raise SystemExit("build_indices loop locals must be declared at function entry") + if body.index("for (ix = 0; ix != n") > body.index("dictkeys_set_index(keys, i, ix);"): + raise SystemExit("build_indices table index insertion order changed") + +body = body_for(dict_source, "dictresize(PyDictObject *mp, uint8_t log2_newsize, int unicode)") +declarations = ("Py_ssize_t numentries;", "Py_ssize_t i;", "int index;", "PyDictUnicodeEntry *old_unicode_entries;", "PyDictUnicodeEntry *new_unicode_entries;", "PyDictUnicodeEntry *unicode_entry;", "PyDictKeyEntry *old_key_entries;", "PyDictKeyEntry *new_key_entries;", "PyDictKeyEntry *key_entry;", "struct _Py_dict_state *state;") +for declaration in declarations: + if declaration not in body or body.index(declaration) > body.index("if (log2_newsize >="): + raise SystemExit("dictresize local missing or late: " + declaration) +if any(token in body for token in ("for (Py_ssize_t i", "PyDictKeyEntry *newentries =", "PyDictUnicodeEntry *newentries =", "struct _Py_dict_state *state =")): + raise SystemExit("dictresize branch/loop locals must be declared at function entry") +order = ("if (log2_newsize >=", "oldkeys = mp->ma_keys;", "mp->ma_keys = new_keys_object", "numentries = mp->ma_used;", "if (oldvalues != NULL)", "old_unicode_entries = DK_UNICODE_ENTRIES(oldkeys);", "dictkeys_decref(oldkeys);", "else { // oldkeys is combined.", "old_key_entries = DK_ENTRIES(oldkeys);", "old_unicode_entries = DK_UNICODE_ENTRIES(oldkeys);", "state = get_dict_state();", "mp->ma_keys->dk_usable -= numentries;", "ASSERT_CONSISTENT(mp);", "return 0;") +positions = [] +for item in order: + positions.append(body.index(item, positions[-1] + 1 if positions else 0)) +if positions != sorted(positions): + raise SystemExit("dictresize allocation/conversion/freelist/commit order changed") +body = body_for(dict_source, "dict_keys(PyDictObject *mp)") +opening, execution = body.split("again:", 1) +if opening.split() != "PyObject *v; Py_ssize_t n; Py_ssize_t j, pos; PyObject *key;".split(): + raise SystemExit("dict_keys locals must be declared at function entry without initializers") +expected_execution = """ + n = mp->ma_used; + v = PyList_New(n); + if (v == NULL) + return NULL; + if (n != mp->ma_used) { + /* Durnit. The allocations caused the dict to resize. + * Just start over, this shouldn't normally happen. + */ + Py_DECREF(v); + goto again; + } + + /* Nothing we do below makes any function calls. */ + j = 0; + pos = 0; + while (_PyDict_Next((PyObject*)mp, &pos, &key, NULL, NULL)) { + assert(j < n); + Py_INCREF(key); + PyList_SET_ITEM(v, j, key); + j++; + } + assert(j == n); + return v; +""" +if execution.split() != expected_execution.split(): + raise SystemExit("dict_keys allocation/retry/initialization/iteration/refcount order changed") + +print("dict consistency C89 declarations and logic order: PASS") diff --git a/scripts/vc6-probe-pythoncore-frontier.test.sh b/scripts/vc6-probe-pythoncore-frontier.test.sh index 07049ab..02ce45a 100755 --- a/scripts/vc6-probe-pythoncore-frontier.test.sh +++ b/scripts/vc6-probe-pythoncore-frontier.test.sh @@ -259,6 +259,94 @@ else bad "_pickle batch_list_exact uses C89 declaration-order-safe error local" fi +# Keep the current posixmodule.c VC6/C89 frontier explicit: os_spawnv_impl() +# must hoist saved_errno before the first executable statement. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("os_spawnv_impl(PyObject *module, int mode, path_t *path, PyObject *argv)") +end = source.index("\n}\n\n/*[clinic input]", start) + 2 +function = source[start:end] +if " int saved_errno;" not in function: + raise SystemExit("os_spawnv_impl saved_errno declaration missing from opening group") +if " int saved_errno = errno;" in function: + raise SystemExit("os_spawnv_impl saved_errno remains a mixed declaration") +if " saved_errno = errno;\n free_string_array(argvlist, argc);" not in function: + raise SystemExit("os_spawnv_impl saved_errno assignment/cleanup order changed") +PY +then + pass "posix os_spawnv_impl uses a C89 declaration-order-safe saved_errno" +else + bad "posix os_spawnv_impl uses a C89 declaration-order-safe saved_errno" +fi + +# Keep the current posixmodule.c VC6 frontier explicit: MSVC6 rejects the +# spelling "long long", while CPython's PY_LONG_LONG alias is already the +# project-compatible 64-bit type. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("_pystat_fromstructstat(PyObject *module, STRUCT_STAT *st)") +end = source.index("\n}\n", start) + 2 +function = source[start:end] +if "sizeof(PY_LONG_LONG) >= sizeof(st->st_size)" not in function: + raise SystemExit("VC6 stat-size assertion must use PY_LONG_LONG") +if "sizeof(long long) >= sizeof(st->st_size)" in function: + raise SystemExit("VC6 stat-size assertion still spells long long") +PY +then + pass "posix stat-size assertion uses the VC6-compatible PY_LONG_LONG spelling" +else + bad "posix stat-size assertion uses the VC6-compatible PY_LONG_LONG spelling" +fi + +# Keep the Windows waitpid status local in the opening C89 declaration group, +# preserving the original conversion point and shifted result argument order. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_WAITPID_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("os_waitpid_impl(PyObject *module, intptr_t pid, int options)") +start = source.index("\n{\n", start) +end = source.index("\n}\n", start) + 3 +expected = """ +{ + int status; + intptr_t res; + int async_err = 0; + PY_LONG_LONG ustatus; + + do { + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH + res = _cwait(&status, pid, options); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + if (res < 0) + return (!async_err) ? posix_error() : NULL; + + ustatus = (unsigned int)status; + + /* shift the status left a byte so this is more like the POSIX waitpid */ + return Py_BuildValue(_Py_PARSE_INTPTR "K", res, ustatus << 8); +} +""" +assert source[start:end] == expected, ( + "os_waitpid_impl must hoist PY_LONG_LONG ustatus into the opening C89 " + "declaration group and preserve conversion/result order" +) +PY_WAITPID_TEST +then + pass "posix os_waitpid_impl preserves C89 declarations and conversion/result order" +else + bad "posix os_waitpid_impl preserves C89 declarations and conversion/result order" +fi + # Keep the next _pickle.c VC6/C89 slice explicit: save_set() must declare its # shared error local before the loop's executable statements. if python3 - "$REPO_ROOT/cpython/Modules/_pickle.c" <<'PY' @@ -2800,6 +2888,7045 @@ else bad "perm_comb_small uses a C89 declaration-order-safe fast_comb_limits2 array" fi +# VC6 rejects the long long keyword in MD5's signed 64-bit typedef. +if python3 - "$REPO_ROOT/cpython/Modules/md5module.c" <<'PY' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +code = re.sub(r"/\*.*?\*/|//[^\n]*", "", source, flags=re.S) +assert re.findall(r"\btypedef\s+([^;]+?)\s+MD5_INT64\s*;", code) == ["PY_LONG_LONG"], ( + "MD5_INT64 must use CPython's compiler-compatible PY_LONG_LONG" +) +PY +then + pass "MD5_INT64 uses CPython's compiler-compatible signed 64-bit type" +else + bad "MD5_INT64 uses CPython's compiler-compatible signed 64-bit type" +fi + +# VC6 requires md5_type_spec fields in PyType_Spec declaration order. +if python3 - "$REPO_ROOT/cpython/Modules/md5module.c" <<'PY' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("static PyType_Spec md5_type_spec = {") +end = source.index("\n};", start) + 3 +expected = '''static PyType_Spec md5_type_spec = { + "_md5.md5", + sizeof(MD5object), + 0, + (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), + md5_type_slots +};''' +assert re.sub(r"\s+", "", source[start:end]) == re.sub(r"\s+", "", expected), ( + "md5_type_spec missing declared-order positional initializer with zero itemsize" +) +PY +then + pass "md5_type_spec uses a C89-compatible declared-order positional initializer" +else + bad "md5_type_spec uses a C89-compatible declared-order positional initializer" +fi + +# _md5_md5_impl() must declare st before statements and assign after buffer acquisition. +if python3 - "$REPO_ROOT/cpython/Modules/md5module.c" <<'PY' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("_md5_md5_impl(PyObject *module, PyObject *string, int usedforsecurity)") +end = source.index("\n}\n", start) + 2 +function = source[start:end] +opening, statements = function.split(" if (string)", 1) +assert ( + " MD5State *st;\n" in opening + and "MD5State *st =" not in function + and function.count(" st = md5_get_state(module);\n") == 1 + and statements.startswith( + "\n GET_BUFFER_VIEW_OR_ERROUT(string, &buf);\n\n" + " st = md5_get_state(module);\n" + " if ((new = newMD5object(st)) == NULL) {" + ) +), "_md5_md5_impl needs an opening st declaration and original-point assignment" +PY +then + pass "_md5_md5_impl uses an opening C89 declaration and original-point assignment" +else + bad "_md5_md5_impl uses an opening C89 declaration and original-point assignment" +fi + +# VC6 requires _md5module fields in PyModuleDef declaration order. +if python3 - "$REPO_ROOT/cpython/Modules/md5module.c" <<'PY' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("static struct PyModuleDef _md5module = {") +end = source.index("\n};", start) + 3 +expected = '''static struct PyModuleDef _md5module = { + PyModuleDef_HEAD_INIT, + "_md5", + NULL, + sizeof(MD5State), + MD5_functions, + _md5_slots, + _md5_traverse, + _md5_clear, + _md5_free, +};''' +assert re.sub(r"\s+", "", source[start:end]) == re.sub(r"\s+", "", expected), ( + "_md5module missing declared-order positional initializer with NULL m_doc" +) +PY +then + pass "_md5module uses a C89-compatible declared-order positional initializer" +else + bad "_md5module uses a C89-compatible declared-order positional initializer" +fi + +# mmap's Windows sizes and offsets retain signed 64-bit declarations and casts. +if python3 - "$REPO_ROOT/cpython/Modules/mmapmodule.c" <<'PY' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +code = re.sub(r"/\*.*?\*/|//[^\n]*", "", source, flags=re.S) +assert ( + not re.search(r"\blong\s+long\b", code) + and code.count("PY_LONG_LONG") == 5 + and all(fragment in code for fragment in ( + "PY_LONG_LONG offset;", + "PY_LONG_LONG size;", + "PY_LONG_LONG offset = 0, size;", + "size = (((PY_LONG_LONG)high)<<32) + low;", + "size = (((PY_LONG_LONG) high) << 32) + low;", + "return PyLong_FromLongLong(size);", + )) +), "mmap must use PY_LONG_LONG for signed 64-bit declarations and size-combining casts" +PY +then + pass "mmap preserves signed 64-bit sizes and offsets using PY_LONG_LONG" +else + bad "mmap preserves signed 64-bit sizes and offsets using PY_LONG_LONG" +fi + +# mmap_close_method Windows locals must precede statements without moving reads. +if python3 - "$REPO_ROOT/cpython/Modules/mmapmodule.c" <<'PY' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("mmap_close_method(mmap_object *self, PyObject *unused)") +end = source.index("\n}\n", start) + 2 +function = re.sub(r"/\*.*?\*/", "", source[start:end], flags=re.S) +opening, statements = function.split(" if (self->exports > 0)", 1) +windows = statements.split("#ifdef MS_WINDOWS", 1)[1].split("#endif", 1)[0] +assert ( + opening.endswith( + "{\n#ifdef MS_WINDOWS\n" + " HANDLE map_handle;\n" + " HANDLE file_handle;\n" + " char *data;\n" + "#endif\n" + ) + and re.sub(r"\s+", " ", windows).strip().startswith( + "map_handle = self->map_handle; file_handle = self->file_handle; " + "data = self->data; self->map_handle = NULL; " + "self->file_handle = INVALID_HANDLE_VALUE; self->data = NULL; " + "Py_BEGIN_ALLOW_THREADS" + ) +), "mmap_close_method missing C89-safe opening Windows declarations and original-point assignments" +PY +then + pass "mmap_close_method uses C89-safe Windows declarations and original-point assignments" +else + bad "mmap_close_method uses C89-safe Windows declarations and original-point assignments" +fi + +# mmap_gfind must hoist res while preserving its branch-specific assignments. +if python3 - "$REPO_ROOT/cpython/Modules/mmapmodule.c" <<'PY' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("mmap_gfind(mmap_object *self,") +end = source.index("\n}\n", start) + 2 +function = source[start:end] +opening, statements = function.split(" CHECK_VALID(NULL);", 1) +assert ( + " Py_ssize_t res;\n" in opening + and function.count("Py_ssize_t res;") == 1 + and statements.endswith(''' CHECK_VALID_OR_RELEASE(NULL, view); + if (end < start) { + res = -1; + } + else if (reverse) { + assert(0 <= start && start <= end && end <= self->size); + res = _PyBytes_ReverseFind( + self->data + start, end - start, + view.buf, view.len, start); + } + else { + assert(0 <= start && start <= end && end <= self->size); + res = _PyBytes_Find( + self->data + start, end - start, + view.buf, view.len, start); + } + PyBuffer_Release(&view); + return PyLong_FromSsize_t(res); + } +}''') +), "mmap_gfind needs an opening res declaration and unchanged branch assignments" +PY +then + pass "mmap_gfind uses an opening C89 res declaration and original-point assignments" +else + bad "mmap_gfind uses an opening C89 res declaration and original-point assignments" +fi + +# VC6 requires mmap's type and module fields in declaration order. +if python3 - "$REPO_ROOT/cpython/Modules/mmapmodule.c" <<'PY' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +expected_blocks = ( + '''static PyType_Spec mmap_object_spec = { + "mmap.mmap", + sizeof(mmap_object), + 0, + (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), + mmap_object_slots, +};''', + '''static struct PyModuleDef mmapmodule = { + PyModuleDef_HEAD_INIT, + "mmap", + NULL, + sizeof(mmap_state), + NULL, + mmap_slots, + mmap_traverse, + mmap_clear, + mmap_free, +};''', +) +failures = [] +for expected in expected_blocks: + declaration = expected.split(" {", 1)[0] + start = source.index(declaration + " {") + end = source.index("\n};", start) + 3 + if re.sub(r"\s+", "", source[start:end]) != re.sub(r"\s+", "", expected): + failures.append(declaration + " needs declared-order positional fields, including implicit zeros") +assert not failures, "\n".join(failures) +PY +then + pass "mmap initializers preserve all fields in C89-compatible declaration order" +else + bad "mmap initializers preserve all fields in C89-compatible declaration order" +fi + +# VC6 requires opcodemodule fields in PyModuleDef declaration order. +if python3 - "$REPO_ROOT/cpython/Modules/_opcode.c" <<'PY' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("static struct PyModuleDef opcodemodule = {") +end = source.index("\n};", start) + 3 +expected = '''static struct PyModuleDef opcodemodule = { + PyModuleDef_HEAD_INIT, + "_opcode", + "Opcode support module.", + 0, + opcode_functions, + NULL, + NULL, + NULL, + NULL +};''' +assert re.sub(r"\s+", "", source[start:end]) == re.sub(r"\s+", "", expected), ( + "opcodemodule missing declared-order positional initializer with explicit NULL omitted fields" +) +PY +then + pass "opcodemodule uses a C89-compatible declared-order positional initializer" +else + bad "opcodemodule uses a C89-compatible declared-order positional initializer" +fi + +# itemgetter_new must declare state before statements and retain the lookup point. +if python3 - "$REPO_ROOT/cpython/Modules/_operator.c" <<'PY' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("itemgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)") +end = source.index("\n}\n", start) + 2 +function = source[start:end] +opening, statements = function.split(' if (!_PyArg_NoKeywords("itemgetter", kwds))', 1) +assert ( + opening.endswith( + "{\n itemgetterobject *ig;\n PyObject *item;\n" + " Py_ssize_t nitems;\n Py_ssize_t index;\n" + " _operator_state *state;\n\n" + ) + and function.count("_operator_state *state") == 1 + and function.count("PyType_GetModuleState(type)") == 1 + and ''' } else { + item = args; + } + state = PyType_GetModuleState(type); + /* create itemgetterobject structure */ + ig = PyObject_GC_New(itemgetterobject, (PyTypeObject *) state->itemgetter_type);''' in statements +), "itemgetter_new needs an opening C89 state declaration and original-point assignment" +PY +then + pass "itemgetter_new uses an opening C89 state declaration and original-point assignment" +else + bad "itemgetter_new uses an opening C89 state declaration and original-point assignment" +fi + +# itemgetter_vectorcall must declare nargs before statements without moving evaluation. +if python3 - "$REPO_ROOT/cpython/Modules/_operator.c" <<'PY' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("itemgetter_vectorcall(PyObject *ig, PyObject *const *args,") +end = source.index("\n}\n", start) + 2 +body = source[start:end].split("{\n", 1)[1] +assert body == ''' Py_ssize_t nargs; + + if (!_PyArg_NoKwnames("itemgetter", kwnames)) { + return NULL; + } + nargs = PyVectorcall_NARGS(nargsf); + if (!_PyArg_CheckPositional("itemgetter", nargs, 1, 1)) { + return NULL; + } + return itemgetter_call_impl((itemgetterobject *)ig, args[0]); +}''', "itemgetter_vectorcall needs an opening C89 nargs declaration and original-point assignment" +PY +then + pass "itemgetter_vectorcall uses an opening C89 nargs declaration and original-point assignment" +else + bad "itemgetter_vectorcall uses an opening C89 nargs declaration and original-point assignment" +fi + +# VC6 requires itemgetter_type_spec fields in PyType_Spec declaration order. +if python3 - "$REPO_ROOT/cpython/Modules/_operator.c" <<'PY' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("static PyType_Spec itemgetter_type_spec = {") +end = source.index("\n};", start) + 3 +expected = '''static PyType_Spec itemgetter_type_spec = { + "operator.itemgetter", + sizeof(itemgetterobject), + 0, + (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_VECTORCALL), + itemgetter_type_slots, +};''' +assert re.sub(r"\s+", "", source[start:end]) == re.sub(r"\s+", "", expected), ( + "itemgetter_type_spec missing declared-order positional initializer with unchanged values" +) +PY +then + pass "itemgetter_type_spec uses a C89-compatible declared-order positional initializer" +else + bad "itemgetter_type_spec uses a C89-compatible declared-order positional initializer" +fi + +# attrgetter_new must declare state before statements and retain the lookup point. +if python3 - "$REPO_ROOT/cpython/Modules/_operator.c" <<'PY' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)") +end = source.index("\n}\n", start) + 2 +function = source[start:end] +opening, statements = function.split(' if (!_PyArg_NoKeywords("attrgetter", kwds))', 1) +assert ( + opening.endswith( + "{\n attrgetterobject *ag;\n PyObject *attr;\n" + " Py_ssize_t nattrs, idx, char_idx;\n" + " _operator_state *state;\n\n" + ) + and function.count("_operator_state *state") == 1 + and function.count("PyType_GetModuleState(type)") == 1 + and ''' PyTuple_SET_ITEM(attr, idx, attr_chain); + } + } + + state = PyType_GetModuleState(type); + /* create attrgetterobject structure */ + ag = PyObject_GC_New(attrgetterobject, (PyTypeObject *)state->attrgetter_type);''' in statements +), "attrgetter_new needs an opening C89 state declaration and original-point assignment" +PY +then + pass "attrgetter_new uses an opening C89 state declaration and original-point assignment" +else + bad "attrgetter_new uses an opening C89 state declaration and original-point assignment" +fi + +# attrgetter_vectorcall must declare nargs before statements without moving evaluation. +if python3 - "$REPO_ROOT/cpython/Modules/_operator.c" <<'PY' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("attrgetter_vectorcall(PyObject *ag, PyObject *const *args,") +end = source.index("\n}\n", start) + 2 +body = source[start:end].split("{\n", 1)[1] +assert body == ''' Py_ssize_t nargs; + + if (!_PyArg_NoKwnames("attrgetter", kwnames)) { + return NULL; + } + nargs = PyVectorcall_NARGS(nargsf); + if (!_PyArg_CheckPositional("attrgetter", nargs, 1, 1)) { + return NULL; + } + return attrgetter_call_impl((attrgetterobject *)ag, args[0]); +}''', "attrgetter_vectorcall needs an opening C89 nargs declaration and original-point assignment" +PY +then + pass "attrgetter_vectorcall uses an opening C89 nargs declaration and original-point assignment" +else + bad "attrgetter_vectorcall uses an opening C89 nargs declaration and original-point assignment" +fi + +# VC6 requires attrgetter_type_spec fields in PyType_Spec declaration order. +if python3 - "$REPO_ROOT/cpython/Modules/_operator.c" <<'PY' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("static PyType_Spec attrgetter_type_spec = {") +end = source.index("\n};", start) + 3 +expected = '''static PyType_Spec attrgetter_type_spec = { + "operator.attrgetter", + sizeof(attrgetterobject), + 0, + (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_VECTORCALL), + attrgetter_type_slots, +};''' +assert re.sub(r"\s+", "", source[start:end]) == re.sub(r"\s+", "", expected), ( + "attrgetter_type_spec missing declared-order positional initializer with unchanged values" +) +PY +then + pass "attrgetter_type_spec uses a C89-compatible declared-order positional initializer" +else + bad "attrgetter_type_spec uses a C89-compatible declared-order positional initializer" +fi + +# methodcaller_new must declare state before statements and retain the lookup point. +if python3 - "$REPO_ROOT/cpython/Modules/_operator.c" <<'PY' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds)") +end = source.index("\n}\n", start) + 2 +function = source[start:end] +opening, statements = function.split(" if (PyTuple_GET_SIZE(args) < 1)", 1) +assert ( + opening.endswith( + "{\n methodcallerobject *mc;\n PyObject *name;\n" + " _operator_state *state;\n\n" + ) + and function.count("_operator_state *state") == 1 + and function.count("PyType_GetModuleState(type)") == 1 + and ''' name = PyTuple_GET_ITEM(args, 0); + if (!PyUnicode_Check(name)) { + PyErr_SetString(PyExc_TypeError, + "method name must be a string"); + return NULL; + } + + state = PyType_GetModuleState(type); + /* create methodcallerobject structure */ + mc = PyObject_GC_New(methodcallerobject, (PyTypeObject *)state->methodcaller_type);''' in statements +), "methodcaller_new needs an opening C89 state declaration and original-point assignment" +PY +then + pass "methodcaller_new uses an opening C89 state declaration and original-point assignment" +else + bad "methodcaller_new uses an opening C89 state declaration and original-point assignment" +fi + +# VC6 requires methodcaller_type_spec fields in PyType_Spec declaration order. +if python3 - "$REPO_ROOT/cpython/Modules/_operator.c" <<'PY' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("static PyType_Spec methodcaller_type_spec = {") +end = source.index("\n};", start) + 3 +expected = '''static PyType_Spec methodcaller_type_spec = { + "operator.methodcaller", + sizeof(methodcallerobject), + 0, + (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + methodcaller_type_slots, +};''' +assert re.sub(r"\s+", "", source[start:end]) == re.sub(r"\s+", "", expected), ( + "methodcaller_type_spec missing declared-order positional initializer with unchanged values" +) +PY +then + pass "methodcaller_type_spec uses a C89-compatible declared-order positional initializer" +else + bad "methodcaller_type_spec uses a C89-compatible declared-order positional initializer" +fi + +# VC6 requires operatormodule fields in PyModuleDef declaration order. +if python3 - "$REPO_ROOT/cpython/Modules/_operator.c" <<'PY' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("static struct PyModuleDef operatormodule = {") +end = source.index("\n};", start) + 3 +expected = '''static struct PyModuleDef operatormodule = { + PyModuleDef_HEAD_INIT, + "_operator", + operator_doc, + sizeof(_operator_state), + operator_methods, + operator_slots, + operator_traverse, + operator_clear, + operator_free, +};''' +assert re.sub(r"\s+", "", source[start:end]) == re.sub(r"\s+", "", expected), ( + "operatormodule missing declared-order positional initializer with unchanged values" +) +PY +then + pass "operatormodule uses a C89-compatible declared-order positional initializer" +else + bad "operatormodule uses a C89-compatible declared-order positional initializer" +fi + +# VC6/Win9x mkdir must use null security attributes without NT-only SDDL. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PYTEST' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("os_mkdir_impl(PyObject *module, path_t *path, int mode, int dir_fd)") +end = source.index("\n}\n", start) + 2 +function = source[start:end] +guard = "#if !defined(_MSC_VER) || _MSC_VER >= 1300\n" +modern_blocks = re.findall(re.escape(guard) + r"(.*?)#endif\n", function, re.S) +vc6 = re.sub(re.escape(guard) + r".*?#endif\n", "", function, flags=re.S) +assert ( + '# if !defined(_MSC_VER) || _MSC_VER >= 1300\n' + '# include // SDDL_REVISION_1\n# endif' in source + and len(modern_blocks) == 3 + and "SECURITY_ATTRIBUTES secAttr = { sizeof(secAttr) };" in modern_blocks[0] + and "if (mode == 0700" in modern_blocks[1] + and "ConvertStringSecurityDescriptorToSecurityDescriptorW(" in modern_blocks[1] + and "SDDL_REVISION_1," in modern_blocks[1] + and "LocalFree(secAttr.lpSecurityDescriptor)" in modern_blocks[2] + and "secAttr" not in vc6 + and "SDDL" not in vc6 + and "ConvertStringSecurityDescriptor" not in vc6 + and "SECURITY_ATTRIBUTES *pSecAttr = NULL;" in vc6 + and vc6.count("pSecAttr") == 2 + and "result = CreateDirectoryW(path->wide, pSecAttr);" in vc6 +), "VC6 mkdir must exclude SDDL and retain CreateDirectoryW with null security attributes" +PYTEST +then + pass "posix mkdir excludes SDDL on VC6 and uses null security attributes" +else + bad "posix mkdir excludes SDDL on VC6 and uses null security attributes" +fi + +# VC6 needs C89 declarations and the VC98 SDK's missing attribute sentinel. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_ATTRIBUTES_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("win32_xstat_impl(") +end = source.index("\n}\n", start) + 2 +function = source[start:end] +assert ( + """ if (fileType != FILE_TYPE_DISK) { + DWORD fileAttributes; + if (fileType == FILE_TYPE_UNKNOWN && GetLastError() != 0) { + retval = -1; + goto cleanup; + } + fileAttributes = GetFileAttributesW(path); + memset(result, 0, sizeof(*result)); + if (fileAttributes != INVALID_FILE_ATTRIBUTES && + fileAttributes & FILE_ATTRIBUTE_DIRECTORY) {""" in function + and function.count("DWORD fileAttributes") == 1 + and """# if defined(_MSC_VER) && _MSC_VER < 1300 +# ifndef INVALID_FILE_ATTRIBUTES +# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) +# endif +# endif""" in source.split('#include "structmember.h"', 1)[0] +), "fileAttributes needs a C89 declaration, original-point query, and VC6-only SDK sentinel fallback" +PY_ATTRIBUTES_TEST +then + pass "posix fileAttributes preserves query order with C89 declaration and VC6 sentinel" +else + bad "posix fileAttributes preserves query order with C89 declaration and VC6 sentinel" +fi + +# VC6 rejects long long; the Windows file offset type must use CPython's alias. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_OFF_T_TEST' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +assert re.search( + r"#ifdef MS_WINDOWS\s+typedef\s+PY_LONG_LONG\s+Py_off_t\s*;\s*#else", + source, +), "Windows Py_off_t must use PY_LONG_LONG" +PY_OFF_T_TEST +then + pass "Windows Py_off_t uses PY_LONG_LONG" +else + bad "Windows Py_off_t uses PY_LONG_LONG" +fi + +# VC6/Win9x has no reparse points and VC98 lacks the query operation. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_REPARSE_TEST' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("win32_get_reparse_tag(HANDLE reparse_point_handle, ULONG *reparse_tag)") +end = source.index("\n}\n", start) +body = source[start:end].split("{\n", 1)[1] +branches = re.fullmatch( + r"#if defined\(_MSC_VER\) && _MSC_VER < 1300\n(.*?)#else\n(.*?)\n#endif", + body, re.S, +) +expected_modern = " char target_buffer[_Py_MAXIMUM_REPARSE_DATA_BUFFER_SIZE];\n _Py_REPARSE_DATA_BUFFER *rdb = (_Py_REPARSE_DATA_BUFFER *)target_buffer;\n DWORD n_bytes_returned;\n\n if (0 == DeviceIoControl(\n reparse_point_handle,\n FSCTL_GET_REPARSE_POINT,\n NULL, 0, /* in buffer */\n target_buffer, sizeof(target_buffer),\n &n_bytes_returned,\n NULL)) /* we're not using OVERLAPPED_IO */\n return FALSE;\n\n if (reparse_tag)\n *reparse_tag = rdb->ReparseTag;\n\n return TRUE;" +assert ( + branches is not None + and re.sub(r"/\*.*?\*/", "", branches[1], flags=re.S).strip() == "return FALSE;" + and "FSCTL_GET_REPARSE_POINT" not in branches[1] + and branches[2] == expected_modern +), "VC6 reparse query must return FALSE without FSCTL_GET_REPARSE_POINT and preserve the modern body" +print("ok - VC6 reparse query returns FALSE and preserves the modern body") +PY_REPARSE_TEST +then + pass "posix reparse query excludes the unavailable operation on VC6" +else + bad "posix reparse query excludes the unavailable operation on VC6" +fi + +# VC6 requires the UNC flag declaration before statements, but its value must +# still be computed after the final current-directory lookup and error check. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_WCHDIR_TEST' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("win32_wchdir(LPCWSTR path)\n{") +end = source.index("\n}\n", start) +body = source[start:end] +opening = body.split(" if(!SetCurrentDirectoryW(path))", 1)[0] +expected_assignment = r''' result = GetCurrentDirectoryW(result, new_path); + if (!result) { + PyMem_RawFree(new_path); + return FALSE; + } + } + is_unc_like_path = (wcsncmp(new_path, L"\\\\", 2) == 0 || + wcsncmp(new_path, L"//", 2) == 0); + if (!is_unc_like_path) {''' +assert ( + " int is_unc_like_path;\n" in opening + and len(re.findall(r"\bint\s+is_unc_like_path\b", body)) == 1 + and len(re.findall(r"\bis_unc_like_path\s*=", body)) == 1 + and expected_assignment in body +), "win32_wchdir must declare the UNC flag up front and assign it at its original point" +PY_WCHDIR_TEST +then + pass "win32_wchdir preserves C89 UNC declaration and assignment order" +else + bad "win32_wchdir preserves C89 UNC declaration and assignment order" +fi + +# VC98 lacks the tag-info type and query API; preserve the modern query verbatim. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_TAG_INFO_TEST' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("win32_xstat_impl(const wchar_t *path,") +end = source.index("\n}\n", start) +function = source[start:end] +legacy_guard = "#if defined(_MSC_VER) && _MSC_VER < 1300\n" +modern_guard = "#if !defined(_MSC_VER) || _MSC_VER >= 1300\n" +declaration = re.search( + re.escape(legacy_guard) + r"(.*?)#else\n(.*?)#endif\n", function, re.S, +) +query = re.search(re.escape(modern_guard) + r"(.*?)#endif\n", function, re.S) +expected_query = ' /* Query the reparse tag, and traverse a non-link. */\n if (!traverse) {\n if (!GetFileInformationByHandleEx(hFile, FileAttributeTagInfo,\n &tagInfo, sizeof(tagInfo))) {\n /* Allow devices that do not support FileAttributeTagInfo. */\n switch (GetLastError()) {\n case ERROR_INVALID_PARAMETER:\n case ERROR_INVALID_FUNCTION:\n case ERROR_NOT_SUPPORTED:\n tagInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;\n tagInfo.ReparseTag = 0;\n break;\n default:\n retval = -1;\n goto cleanup;\n }\n } else if (tagInfo.FileAttributes &\n FILE_ATTRIBUTE_REPARSE_POINT) {\n if (IsReparseTagNameSurrogate(tagInfo.ReparseTag)) {\n if (isUnhandledTag) {\n /* Traversing previously failed for either this link\n or its target. */\n SetLastError(ERROR_CANT_ACCESS_FILE);\n retval = -1;\n goto cleanup;\n }\n /* Traverse a non-link, but not if traversing already failed\n for an unhandled tag. */\n } else if (!isUnhandledTag) {\n CloseHandle(hFile);\n return win32_xstat_impl(path, result, TRUE);\n }\n }\n }\n\n' +vc6 = re.sub(re.escape(modern_guard) + r".*?#endif\n", "", function, flags=re.S) +vc6 = re.sub( + re.escape(legacy_guard) + r"(.*?)#else\n.*?#endif\n", + lambda match: match[1], vc6, flags=re.S, +) +assert ( + declaration is not None + and re.sub(r"\s+", " ", declaration[1]).strip() + == "struct { DWORD FileAttributes; DWORD ReparseTag; } tagInfo = { 0 };" + and declaration[2] == " FILE_ATTRIBUTE_TAG_INFO tagInfo = { 0 };\n" + and query is not None and query[1] == expected_query + and not any(name in vc6 for name in ( + "FILE_ATTRIBUTE_TAG_INFO", "FileAttributeTagInfo", "GetFileInformationByHandleEx", + )) + and "&tagInfo.ReparseTag" in vc6 + and "_Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, result);" in vc6 +), "VC6 xstat needs zeroed local tag-info and no unavailable query, preserving the modern path" +PY_TAG_INFO_TEST +then + pass "win32_xstat_impl excludes the tag-info API family on VC6" +else + bad "win32_xstat_impl excludes the tag-info API family on VC6" +fi + +# fill_time's complete statement sequence pins C89 declarations and evaluation/cleanup order. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_FILL_TIME_TEST' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("fill_time(PyObject *module,") +start = source.index("{\n", start) +end = source.index("\n}\n", start) + 2 +body = source[start:end] +expected = """{ + int res = -1; + PyObject *s_in_ns = NULL; + PyObject *ns_total = NULL; + PyObject *float_s = NULL; + PyObject *s; + PyObject *ns_fractional; + + assert(!PyErr_Occurred()); + + s = _PyLong_FromTime_t(sec); + ns_fractional = PyLong_FromUnsignedLong(nsec); + if (!(s && ns_fractional)) { + goto exit; + } + + s_in_ns = PyNumber_Multiply(s, get_posix_state(module)->billion); + if (!s_in_ns) { + goto exit; + } + + ns_total = PyNumber_Add(s_in_ns, ns_fractional); + if (!ns_total) + goto exit; + + float_s = PyFloat_FromDouble(sec + 1e-9*nsec); + if (!float_s) { + goto exit; + } + + PyStructSequence_SET_ITEM(v, index, s); + PyStructSequence_SET_ITEM(v, index+3, float_s); + PyStructSequence_SET_ITEM(v, index+6, ns_total); + s = NULL; + float_s = NULL; + ns_total = NULL; + + assert(!PyErr_Occurred()); + res = 0; + +exit: + Py_XDECREF(s); + Py_XDECREF(ns_fractional); + Py_XDECREF(s_in_ns); + Py_XDECREF(ns_total); + Py_XDECREF(float_s); + return res; +}""" +# Exact token sequence also rejects any mixed declarations in the body. +assert re.sub(r"\s+", " ", body).strip() == re.sub(r"\s+", " ", expected).strip(), ( + "fill_time must declare res, s_in_ns, ns_total, float_s, s, and ns_fractional " + "before assert, without mixed declarations, preserving evaluation and cleanup order" +) +PY_FILL_TIME_TEST +then + pass "fill_time preserves C89 declarations and original evaluation/cleanup order" +else + bad "fill_time preserves C89 declarations and original evaluation/cleanup order" +fi + +# The active posixmodule.c VC6/C89 frontier must hoist _pystat_fromstructstat() +# locals before the initial executable assertion while preserving evaluation order. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_pystat_fromstructstat(PyObject *module, STRUCT_STAT *st)") +end = source.index("\n}\n\n#ifdef MS_WINDOWS", start) + 2 +function = source[start:end] +opening = function[:function.index(" assert(!PyErr_Occurred());")] +for declaration in (" PyObject *StatResultType;\n", " PyObject *v;\n"): + if declaration not in opening: + raise SystemExit("_pystat_fromstructstat declaration missing from opening group") +if " PyObject *StatResultType =" in function or " PyObject *v =" in function: + raise SystemExit("_pystat_fromstructstat retains mixed declaration") +sequence = ( + " StatResultType = get_posix_state(module)->StatResultType;\n" + " v = PyStructSequence_New((PyTypeObject *)StatResultType);\n" +) +if sequence not in function: + raise SystemExit("_pystat_fromstructstat assignment sequence changed") +PY +then + pass "_pystat_fromstructstat uses C89 declaration-order-safe locals" +else + bad "_pystat_fromstructstat uses C89 declaration-order-safe locals" +fi + +# Nanosecond locals must precede statements without moving branch assignments. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_STAT_NSEC_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_pystat_fromstructstat(PyObject *module, STRUCT_STAT *st)") +function = source[start:source.index("\n}\n", start) + 2] +opening = """_pystat_fromstructstat(PyObject *module, STRUCT_STAT *st) +{ + PyObject *StatResultType; + PyObject *v; + unsigned long ansec, mnsec, cnsec; + + assert(!PyErr_Occurred()); +""" +sequence = """ SET_ITEM(6, PyLong_FromLongLong(st->st_size)); + + // Set st_atime, st_mtime and st_ctime +#if defined(HAVE_STAT_TV_NSEC) + ansec = st->st_atim.tv_nsec; + mnsec = st->st_mtim.tv_nsec; + cnsec = st->st_ctim.tv_nsec; +#elif defined(HAVE_STAT_TV_NSEC2) + ansec = st->st_atimespec.tv_nsec; + mnsec = st->st_mtimespec.tv_nsec; + cnsec = st->st_ctimespec.tv_nsec; +#elif defined(HAVE_STAT_NSEC) + ansec = st->st_atime_nsec; + mnsec = st->st_mtime_nsec; + cnsec = st->st_ctime_nsec; +#else + ansec = mnsec = cnsec = 0; +#endif + if (fill_time(module, v, 7, st->st_atime, ansec) < 0) { + goto error; + } + if (fill_time(module, v, 8, st->st_mtime, mnsec) < 0) { + goto error; + } + if (fill_time(module, v, 9, st->st_ctime, cnsec) < 0) { + goto error; + } +""" +assert (function.startswith(opening) + and function.count("unsigned long ansec, mnsec, cnsec;") == 1 + and sequence in function), ( + "_pystat_fromstructstat nanosecond opening declaration group or original assignment/branch sequence changed" +) +PY_STAT_NSEC_TEST +then + pass "_pystat_fromstructstat nanoseconds preserve C89 declarations and branch assignments" +else + bad "_pystat_fromstructstat nanoseconds preserve C89 declarations and branch assignments" +fi + +# The active posixmodule.c VC6/C89 frontier must hoist posix_getcwd()'s +# result local before the initial executable Windows API and error handling. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("posix_getcwd(int use_bytes)\n{") +end = source.index("\n#else\n const size_t chunk = 1024;", start) +function = source[start:end] +opening = function[:function.index(" Py_BEGIN_ALLOW_THREADS")] +if " PyObject *resobj;\n" not in opening: + raise SystemExit("posix_getcwd resobj declaration missing from opening group") +if " PyObject *resobj = PyUnicode_FromWideChar" in function: + raise SystemExit("posix_getcwd retains mixed resobj declaration") +if function.count(" resobj = PyUnicode_FromWideChar(wbuf2, len);\n") != 1: + raise SystemExit("posix_getcwd resobj assignment missing or duplicated") +sequence = ( + " resobj = PyUnicode_FromWideChar(wbuf2, len);\n" + " if (wbuf2 != wbuf) {\n" + " PyMem_RawFree(wbuf2);\n" + " }" +) +if sequence not in function: + raise SystemExit("posix_getcwd result/free sequence changed") +PY +then + pass "posix_getcwd uses a C89 declaration-order-safe resobj local" +else + bad "posix_getcwd uses a C89 declaration-order-safe resobj local" +fi + +# Keep os__getfullpathname_impl's result declaration before executable code, +# preserving the original conversion, free, error, and reference handling. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_FULLPATH_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("os__getfullpathname_impl(PyObject *module, path_t *path)") +start = source.index("\n{\n", start) +end = source.index("\n}\n", start) + 3 +expected = ''' +{ + wchar_t *abspath; + PyObject *str; + + if (_PyOS_getfullpathname(path->wide, &abspath) < 0) { + return win32_error_object("GetFullPathNameW", path->object); + } + if (abspath == NULL) { + return PyErr_NoMemory(); + } + + str = PyUnicode_FromWideChar(abspath, wcslen(abspath)); + PyMem_RawFree(abspath); + if (str == NULL) { + return NULL; + } + if (path->narrow) { + Py_SETREF(str, PyUnicode_EncodeFSDefault(str)); + } + return str; +} +''' +assert source[start:end] == expected, ( + "os__getfullpathname_impl must hoist str and preserve conversion/free/error/refcount order" +) +PY_FULLPATH_TEST +then + pass "os__getfullpathname_impl uses a C89 declaration-order-safe str local" +else + bad "os__getfullpathname_impl uses a C89 declaration-order-safe str local" +fi + +# VC98 lacks the DOS-volume flag; modern SDK definitions and the call stay intact. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_VOLUME_NAME_DOS_TEST' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +fallback = """# if defined(_MSC_VER) && _MSC_VER < 1300 +# ifndef VOLUME_NAME_DOS +# define VOLUME_NAME_DOS 0x0 +# endif +# endif""" +headers = source.split('#include "structmember.h"', 1)[0] +assert fallback in headers, "VOLUME_NAME_DOS needs a VC6-only ifndef fallback with value 0x0" +assert len(re.findall(r"^\s*#\s*define\s+VOLUME_NAME_DOS\b", source, re.M)) == 1, ( + "modern SDK VOLUME_NAME_DOS definitions must remain untouched" +) +assert """ result_length = GetFinalPathNameByHandleW(hFile, target_path, + buf_size, VOLUME_NAME_DOS);""" in source, ( + "the final-path query must retain its original DOS-volume flag" +) +PY_VOLUME_NAME_DOS_TEST +then + pass "posix VOLUME_NAME_DOS fallback is VC6-only and preserves the modern path" +else + bad "posix VOLUME_NAME_DOS fallback is VC6-only and preserves the modern path" +fi + +# Keep the final-path realloc temporary in the opening C89 declaration group. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_FINALPATH_TMP_TEST' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("os__getfinalpathname_impl(PyObject *module, path_t *path)") +start = source.index("\n{\n", start) +end = source.index("\n}\n", start) + 3 +function = source[start:end] +opening, body = function.split(" Py_BEGIN_ALLOW_THREADS", 1) +sequence = """ tmp = PyMem_Realloc(target_path != buf ? target_path : NULL, + result_length * sizeof(*tmp)); + if (!tmp) { + result = PyErr_NoMemory(); + goto cleanup; + } + + buf_size = result_length; + target_path = tmp;""" +assert ( + opening.count(" wchar_t *tmp;\n") == 1 + and len(re.findall(r"\bwchar_t\s*\*\s*tmp\b", function)) == 1 + and not re.search(r"\bwchar_t\s*\*\s*tmp\b", body) + and body.count(sequence) == 1 +), "os__getfinalpathname_impl must hoist tmp and preserve realloc/NULL-error/size/target order" +PY_FINALPATH_TMP_TEST +then + pass "os__getfinalpathname_impl uses a C89 declaration-order-safe tmp local" +else + bad "os__getfinalpathname_impl uses a C89 declaration-order-safe tmp local" +fi + +# Keep the split-root slash iterator in the opening C89 declaration group. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_SPLITROOT_P_TEST' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("os__path_splitroot_impl(PyObject *module, path_t *path)") +start = source.index("\n{\n", start) +end = source.index("\n}\n", start) + 3 +function = source[start:end] +opening, body = function.split(" buffer =", 1) +sequence = r""" wcscpy(buffer, path->wide); + for (p = wcschr(buffer, L'/'); p; p = wcschr(p, L'/')) { + *p = L'\\'; + }""" +assert ( + opening.count(" wchar_t *p;\n") == 1 + and len(re.findall(r"\bwchar_t\s*\*\s*p\b", function)) == 1 + and body.count(sequence) == 1 +), "os__path_splitroot_impl must hoist p and preserve copy/search/replace order" +PY_SPLITROOT_P_TEST +then + pass "os__path_splitroot_impl uses a C89 declaration-order-safe p local" +else + bad "os__path_splitroot_impl uses a C89 declaration-order-safe p local" +fi + +# Keep normpath locals in the opening C89 declaration group, with all +# evaluations, error returns, and cleanup at their original points. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_NORMPATH_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("os__path_normpath_impl(PyObject *module, PyObject *path)") +start = source.index("\n{\n", start) +end = source.index("\n}\n", start) + 3 +expected = """ +{ + Py_ssize_t len; + wchar_t *buffer; + Py_ssize_t norm_len; + wchar_t *norm_path; + PyObject *result; + + if (!PyUnicode_Check(path)) { + PyErr_Format(PyExc_TypeError, "expected 'str', not '%.200s'", + Py_TYPE(path)->tp_name); + return NULL; + } + buffer = PyUnicode_AsWideCharString(path, &len); + if (!buffer) { + return NULL; + } + norm_path = _Py_normpath_and_size(buffer, len, &norm_len); + result = PyUnicode_FromWideChar(norm_path, norm_len); + PyMem_Free(buffer); + return result; +} +""" +assert source[start:end] == expected, ( + "os__path_normpath_impl must use an opening C89 declaration group and " + "preserve type-check/allocation/NULL-error/normalization/result/free/return order" +) +PY_NORMPATH_TEST +then + pass "os__path_normpath_impl preserves C89 declarations and evaluation/cleanup order" +else + bad "os__path_normpath_impl preserves C89 declarations and evaluation/cleanup order" +fi + +# Keep the current posixmodule.c VC6 frontier explicit: the VC98 SDK lacks +# the reparse-tag constants used by Py_DeleteFileW(). +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("#ifdef MS_WINDOWS") +end = source.index("#endif /* MS_WINDOWS */", start) +header = source[start:end] +if "# if defined(_MSC_VER) && _MSC_VER < 1300" not in header: + raise SystemExit("VC6 reparse-tag fallback guard missing") +if "# define IO_REPARSE_TAG_SYMLINK 0xA000000C" not in header: + raise SystemExit("VC6 symlink reparse-tag fallback missing") +if "# define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003" not in header: + raise SystemExit("VC6 mount-point reparse-tag fallback missing") +if "find_data.dwReserved0 == IO_REPARSE_TAG_SYMLINK" not in source: + raise SystemExit("Py_DeleteFileW symlink tag check missing") +if "find_data.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT" not in source: + raise SystemExit("Py_DeleteFileW mount-point tag check missing") +PY +then + pass "posixmodule provides VC6 reparse-tag fallbacks and preserves Py_DeleteFileW checks" +else + bad "posixmodule provides VC6 reparse-tag fallbacks and preserves Py_DeleteFileW checks" +fi + +# Keep environment-loop locals C89-compatible without moving evaluations or cleanup. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_ENVLIST_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("parse_envlist(PyObject* env, Py_ssize_t *envc_ptr)") +start = source.index("\n{\n", start) +end = source.index("\n}\n", start) + 3 +expected = r""" +{ + Py_ssize_t i, pos, envc; + PyObject *keys=NULL, *vals=NULL; + PyObject *key2, *val2, *keyval; + PyObject *key, *val; + EXECV_CHAR **envlist; + + i = PyMapping_Size(env); + if (i < 0) + return NULL; + envlist = PyMem_NEW(EXECV_CHAR *, i + 1); + if (envlist == NULL) { + PyErr_NoMemory(); + return NULL; + } + envc = 0; + keys = PyMapping_Keys(env); + if (!keys) + goto error; + vals = PyMapping_Values(env); + if (!vals) + goto error; + if (!PyList_Check(keys) || !PyList_Check(vals)) { + PyErr_Format(PyExc_TypeError, + "env.keys() or env.values() is not a list"); + goto error; + } + + for (pos = 0; pos < i; pos++) { + key = PyList_GetItem(keys, pos); // Borrowed ref. + if (key == NULL) { + goto error; + } + val = PyList_GetItem(vals, pos); // Borrowed ref. + if (val == NULL) { + goto error; + } + +#if defined(HAVE_WEXECV) || defined(HAVE_WSPAWNV) + if (!PyUnicode_FSDecoder(key, &key2)) + goto error; + if (!PyUnicode_FSDecoder(val, &val2)) { + Py_DECREF(key2); + goto error; + } + /* Search from index 1 because on Windows starting '=' is allowed for + defining hidden environment variables. */ + if (PyUnicode_GET_LENGTH(key2) == 0 || + PyUnicode_FindChar(key2, '=', 1, PyUnicode_GET_LENGTH(key2), 1) != -1) + { + PyErr_SetString(PyExc_ValueError, "illegal environment variable name"); + Py_DECREF(key2); + Py_DECREF(val2); + goto error; + } + keyval = PyUnicode_FromFormat("%U=%U", key2, val2); +#else + if (!PyUnicode_FSConverter(key, &key2)) + goto error; + if (!PyUnicode_FSConverter(val, &val2)) { + Py_DECREF(key2); + goto error; + } + if (PyBytes_GET_SIZE(key2) == 0 || + strchr(PyBytes_AS_STRING(key2) + 1, '=') != NULL) + { + PyErr_SetString(PyExc_ValueError, "illegal environment variable name"); + Py_DECREF(key2); + Py_DECREF(val2); + goto error; + } + keyval = PyBytes_FromFormat("%s=%s", PyBytes_AS_STRING(key2), + PyBytes_AS_STRING(val2)); +#endif + Py_DECREF(key2); + Py_DECREF(val2); + if (!keyval) + goto error; + + if (!fsconvert_strdup(keyval, &envlist[envc++])) { + Py_DECREF(keyval); + goto error; + } + + Py_DECREF(keyval); + } + Py_DECREF(vals); + Py_DECREF(keys); + + envlist[envc] = 0; + *envc_ptr = envc; + return envlist; + +error: + Py_XDECREF(keys); + Py_XDECREF(vals); + free_string_array(envlist, envc); + return NULL; +} +""" +assert source[start:end] == expected, ( + "parse_envlist must hoist key and val into the C89 declaration group and " + "preserve PyList_GetItem/evaluation/error/cleanup order" +) +PY_ENVLIST_TEST +then + pass "parse_envlist preserves C89 key/val declarations and evaluation/error/cleanup order" +else + bad "parse_envlist preserves C89 key/val declarations and evaluation/error/cleanup order" +fi + +# Keep os_kill_impl declarations before the audit without moving evaluations. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_OS_KILL_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("os_kill_impl(PyObject *module, pid_t pid, Py_ssize_t signal)") +start = source.index("\n{\n", start) +end = source.index("\n}\n", start) + 3 +expected = """ +{ +#ifdef MS_WINDOWS + PyObject *result; + DWORD sig; + DWORD err; + HANDLE handle; +#endif + + if (PySys_Audit("os.kill", "in", pid, signal) < 0) { + return NULL; + } +#ifndef MS_WINDOWS + if (kill(pid, (int)signal) == -1) { + return posix_error(); + } + + // Check immediately if the signal was sent to the current process. + // Don't micro-optimize pid == getpid(), since PyErr_SetString() check + // is cheap. + if (PyErr_CheckSignals()) { + return NULL; + } + + Py_RETURN_NONE; +#else /* !MS_WINDOWS */ + sig = (DWORD)signal; + + /* Console processes which share a common console can be sent CTRL+C or + CTRL+BREAK events, provided they handle said events. */ + if (sig == CTRL_C_EVENT || sig == CTRL_BREAK_EVENT) { + if (GenerateConsoleCtrlEvent(sig, (DWORD)pid) == 0) { + err = GetLastError(); + PyErr_SetFromWindowsErr(err); + } + else + Py_RETURN_NONE; + } + + /* If the signal is outside of what GenerateConsoleCtrlEvent can use, + attempt to open and terminate the process. */ + handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid); + if (handle == NULL) { + err = GetLastError(); + return PyErr_SetFromWindowsErr(err); + } + + if (TerminateProcess(handle, sig) == 0) { + err = GetLastError(); + result = PyErr_SetFromWindowsErr(err); + } else { + Py_INCREF(Py_None); + result = Py_None; + } + + CloseHandle(handle); + return result; +#endif /* !MS_WINDOWS */ +} +""" +assert source[start:end] == expected, ( + "os_kill_impl must hoist Windows declarations before the audit and preserve " + "audit/signal/API/error/cleanup order" +) +PY_OS_KILL_TEST +then + pass "os_kill_impl preserves C89 declarations and evaluation/error/cleanup order" +else + bad "os_kill_impl preserves C89 declarations and evaluation/error/cleanup order" +fi + +# Keep the current posixmodule.c VC6 frontier explicit: the VC98 SDK has no +# FSCTL_GET_REPARSE_POINT, so os_readlink_impl must be unavailable on VC6 +# rather than compiling the NT reparse-point implementation. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("os_readlink_impl(PyObject *module, path_t *path, int dir_fd)") +end = source.index("\n#endif /* defined(HAVE_READLINK) || defined(MS_WINDOWS) */", start) +function = source[start:end] +expected = """#if defined(_MSC_VER) && _MSC_VER < 1300 + PyErr_SetString(PyExc_NotImplementedError, + \"os.readlink is unavailable on this Windows build\"); + return NULL; +#else +""" +if expected not in function: + raise SystemExit("VC6 os_readlink fallback missing") +if "FSCTL_GET_REPARSE_POINT" not in function: + raise SystemExit("modern os_readlink reparse implementation missing") +if function.count("#endif") < 1: + raise SystemExit("os_readlink VC6 guard is incomplete") +PY +then + pass "os_readlink excludes the unavailable VC6 reparse API and preserves the modern implementation" +else + bad "os_readlink excludes the unavailable VC6 reparse API and preserves the modern implementation" +fi + +# Windows 9x has neither AddDllDirectory nor RemoveDllDirectory. The VC6 +# implementations must report this explicitly while retaining the modern API. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_DLL_DIRECTORY_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +add_start = source.index("os__add_dll_directory_impl(PyObject *module, path_t *path)") +add_start = source.index("\n{\n", add_start) +add_end = source.index("\n}\n", add_start) + 3 +add = source[add_start:add_end] +remove_start = source.index("os__remove_dll_directory_impl(PyObject *module, PyObject *cookie)") +remove_start = source.index("\n{\n", remove_start) +remove_end = source.index("\n}\n", remove_start) + 3 +remove = source[remove_start:remove_end] +fallback = """#if defined(_MSC_VER) && _MSC_VER < 1300 + PyErr_SetString(PyExc_NotImplementedError, + \"DLL directory management is unavailable on this Windows build\"); + return NULL; +#else +""" +for name, function in (("add", add), ("remove", remove)): + if fallback not in function: + raise SystemExit(f"VC6 os.{name}_dll_directory fallback missing") + if "#endif" not in function: + raise SystemExit(f"os.{name}_dll_directory modern branch not preserved") +if "typedef DLL_DIRECTORY_COOKIE" not in source: + raise SystemExit("modern DLL_DIRECTORY_COOKIE typedefs missing") +PY_DLL_DIRECTORY_TEST +then + pass "DLL directory operations reject unsupported VC6 APIs and preserve modern implementation" +else + bad "DLL directory operations reject unsupported VC6 APIs and preserve modern implementation" +fi + +# The VC98 SDK lacks the directory flag used by Windows symlink creation. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("#ifdef MS_WINDOWS\n# include ") +end = source.index('\n#include "structmember.h"', start) +expected = """# if defined(_MSC_VER) && _MSC_VER < 1300 +# ifndef SYMBOLIC_LINK_FLAG_DIRECTORY +# define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1 +# endif +# endif +""" +assert expected in source[start:end], ( + "missing Windows-only VC6 SYMBOLIC_LINK_FLAG_DIRECTORY fallback (0x1), " + "guarded to preserve SDK definitions" +) +PY +then + pass "posixmodule provides a guarded VC6 symlink directory flag fallback" +else + bad "posixmodule provides a guarded VC6 symlink directory flag fallback" +fi + +# Keep win32_putenv locals in the opening C89 declaration group while +# preserving every evaluation, cleanup, error, and return in the body. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_WIN32_PUTENV_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("win32_putenv(PyObject *name, PyObject *value)") +start = source.index("\n{\n", start) +end = source.index("\n}\n", start) + 3 +expected = """ +{ + PyObject *unicode; + Py_ssize_t size; + wchar_t *env; + int err; + + /* Search from index 1 because on Windows starting '=' is allowed for + defining hidden environment variables. */ + if (PyUnicode_GET_LENGTH(name) == 0 || + PyUnicode_FindChar(name, '=', 1, PyUnicode_GET_LENGTH(name), 1) != -1) + { + PyErr_SetString(PyExc_ValueError, "illegal environment variable name"); + return NULL; + } + if (value != NULL) { + unicode = PyUnicode_FromFormat("%U=%U", name, value); + } + else { + unicode = PyUnicode_FromFormat("%U=", name); + } + if (unicode == NULL) { + return NULL; + } + + env = PyUnicode_AsWideCharString(unicode, &size); + Py_DECREF(unicode); + + if (env == NULL) { + return NULL; + } + if (size > _MAX_ENV) { + PyErr_Format(PyExc_ValueError, + "the environment variable is longer than %u characters", + _MAX_ENV); + PyMem_Free(env); + return NULL; + } + if (wcslen(env) != (size_t)size) { + PyErr_SetString(PyExc_ValueError, + "embedded null character"); + PyMem_Free(env); + return NULL; + } + + /* _wputenv() and SetEnvironmentVariableW() update the environment in the + Process Environment Block (PEB). _wputenv() also updates CRT 'environ' + and '_wenviron' variables, whereas SetEnvironmentVariableW() does not. + + Prefer _wputenv() to be compatible with C libraries using CRT + variables and CRT functions using these variables (ex: getenv()). */ + err = _wputenv(env); + + if (err) { + posix_error(); + PyMem_Free(env); + return NULL; + } + PyMem_Free(env); + + Py_RETURN_NONE; +} +""" +assert source[start:end] == expected, ( + "win32_putenv must hoist unicode/size/env/err into the opening C89 declaration " + "group and preserve evaluation/cleanup/error/return order" +) +PY_WIN32_PUTENV_TEST +then + pass "win32_putenv preserves C89 declarations and evaluation/cleanup/error/return order" +else + bad "win32_putenv preserves C89 declarations and evaluation/cleanup/error/return order" +fi + +# The active posixmodule.c VC6 frontier must provide the VC98 CRT's missing +# _MAX_ENV limit without overriding newer SDK definitions. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +expected = """# if defined(_MSC_VER) && _MSC_VER < 1300 +# ifndef _MAX_ENV +# define _MAX_ENV 32767 +# endif +# endif""" +if expected not in source: + raise SystemExit("VC6 _MAX_ENV fallback missing") +PY +then + pass "posixmodule provides a guarded VC6 _MAX_ENV fallback" +else + bad "posixmodule provides a guarded VC6 _MAX_ENV fallback" +fi + +# Keep terminal-size declarations C89-compatible and preserve the complete body order. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_TERMSIZE_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("os_get_terminal_size_impl(PyObject *module, int fd)") +start = source.index("\n{\n", start) +end = source.index("\n}\n", start) + 3 +expected = r""" +{ + int columns, lines; + PyObject *termsize; + PyObject *TerminalSizeType; + int pos; + + /* Under some conditions stdout may not be connected and + * fileno(stdout) may point to an invalid file descriptor. For example + * GUI apps don't have valid standard streams by default. + * + * If this happens, and the optional fd argument is not present, + * the ioctl below will fail returning EBADF. This is what we want. + */ + +#ifdef TERMSIZE_USE_IOCTL + { + struct winsize w; + if (ioctl(fd, TIOCGWINSZ, &w)) + return PyErr_SetFromErrno(PyExc_OSError); + columns = w.ws_col; + lines = w.ws_row; + } +#endif /* TERMSIZE_USE_IOCTL */ + +#ifdef TERMSIZE_USE_CONIO + { + DWORD nhandle; + HANDLE handle; + CONSOLE_SCREEN_BUFFER_INFO csbi; + switch (fd) { + case 0: nhandle = STD_INPUT_HANDLE; + break; + case 1: nhandle = STD_OUTPUT_HANDLE; + break; + case 2: nhandle = STD_ERROR_HANDLE; + break; + default: + return PyErr_Format(PyExc_ValueError, "bad file descriptor"); + } + handle = GetStdHandle(nhandle); + if (handle == NULL) + return PyErr_Format(PyExc_OSError, "handle cannot be retrieved"); + if (handle == INVALID_HANDLE_VALUE) + return PyErr_SetFromWindowsErr(0); + + if (!GetConsoleScreenBufferInfo(handle, &csbi)) + return PyErr_SetFromWindowsErr(0); + + columns = csbi.srWindow.Right - csbi.srWindow.Left + 1; + lines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; + } +#endif /* TERMSIZE_USE_CONIO */ + + TerminalSizeType = get_posix_state(module)->TerminalSizeType; + termsize = PyStructSequence_New((PyTypeObject *)TerminalSizeType); + if (termsize == NULL) + return NULL; + + pos = 0; + +#define SET_TERMSIZE(CALL) \ + do { \ + PyObject *item = (CALL); \ + if (item == NULL) { \ + Py_DECREF(termsize); \ + return NULL; \ + } \ + PyStructSequence_SET_ITEM(termsize, pos++, item); \ + } while(0) + + SET_TERMSIZE(PyLong_FromLong(columns)); + SET_TERMSIZE(PyLong_FromLong(lines)); +#undef SET_TERMSIZE + + return termsize; +} +""" +assert source[start:end] == expected, ( + "os_get_terminal_size_impl must declare TerminalSizeType/pos in the opening " + "C89 group and preserve assignments/SET_TERMSIZE/error/return order" +) +PY_TERMSIZE_TEST +then + pass "terminal size preserves C89 declarations and evaluation/error/return order" +else + bad "terminal size preserves C89 declarations and evaluation/error/return order" +fi + +# VC6 targets pre-processor-group Windows; retain the modern all-group query. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_CPU_COUNT_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("os_cpu_count_impl(PyObject *module)") +start = source.index("\n{\n", start) +end = source.index("\n}\n", start) + 3 +function = source[start:end] +expected = """ int ncpu = 0; +#ifdef MS_WINDOWS +# if defined(_MSC_VER) && _MSC_VER < 1300 + /* Win9x has no processor groups or GetActiveProcessorCount. */ + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + ncpu = sysinfo.dwNumberOfProcessors; +# else + ncpu = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); +# endif +#elif defined(__hpux)""" +assert ( + expected in function + and function.endswith(""" if (ncpu >= 1) + return PyLong_FromLong(ncpu); + else + Py_RETURN_NONE; +} +""") +), "os_cpu_count must use VC6-only GetSystemInfo and preserve modern all-group query/count-or-None" +PY_CPU_COUNT_TEST +then + pass "os_cpu_count preserves VC6 system count and modern all-group query" +else + bad "os_cpu_count preserves VC6 system count and modern all-group query" +fi + +# DirEntry_dealloc must declare free_func before its first executable DECREF. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_DIRENTRY_FREE_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("DirEntry_dealloc(DirEntry *entry)") +end = source.index("\n}\n", start) + 3 +function = source[start:end] +expected = """DirEntry_dealloc(DirEntry *entry) +{ + PyTypeObject *tp = Py_TYPE(entry); + freefunc free_func; + Py_XDECREF(entry->name); + Py_XDECREF(entry->path); + Py_XDECREF(entry->stat); + Py_XDECREF(entry->lstat); + free_func = PyType_GetSlot(tp, Py_tp_free); + free_func(entry); + Py_DECREF(tp); +} +""" +assert source[start:end] == expected, ( + "DirEntry_dealloc must hoist freefunc free_func before executable statements " + "and preserve cleanup/call order" +) +PY_DIRENTRY_FREE_TEST +then + pass "DirEntry_dealloc preserves C89 declaration and cleanup order" +else + bad "DirEntry_dealloc preserves C89 declaration and cleanup order" +fi + +# ScandirIterator_dealloc must keep its slot callback declaration C89-safe. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_SCANDIR_FREE_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("ScandirIterator_dealloc(ScandirIterator *iterator)") +end = source.index("\n}\n", start) + 3 +expected = """ScandirIterator_dealloc(ScandirIterator *iterator) +{ + PyTypeObject *tp = Py_TYPE(iterator); + freefunc free_func; + if (PyObject_CallFinalizerFromDealloc((PyObject *)iterator) < 0) + return; + + free_func = PyType_GetSlot(tp, Py_tp_free); + free_func(iterator); + Py_DECREF(tp); +} +""" +assert source[start:end] == expected, ( + "ScandirIterator_dealloc must declare free_func before statements " + "and preserve finalizer/free/decref order" +) +PY_SCANDIR_FREE_TEST +then + pass "ScandirIterator_dealloc preserves C89 declaration and finalizer/free order" +else + bad "ScandirIterator_dealloc preserves C89 declaration and finalizer/free order" +fi + +# os_scandir_impl must declare the iterator type before executable statements. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_SCANDIR_IMPL_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("os_scandir_impl(PyObject *module, path_t *path)") +end = source.index("\n}\n", start) + 3 +function = source[start:end] +expected = """os_scandir_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=6eb2668b675ca89e input=6bdd312708fc3bb0]*/ +{ + ScandirIterator *iterator; + PyObject *ScandirIteratorType; +#ifdef MS_WINDOWS + wchar_t *path_strW; +#else + const char *path_str; +#ifdef HAVE_FDOPENDIR + int fd = -1; +#endif +#endif + + if (PySys_Audit("os.scandir", "O", + path->object ? path->object : Py_None) < 0) { + return NULL; + } + + ScandirIteratorType = get_posix_state(module)->ScandirIteratorType; +""" +assert function.startswith(expected), ( + "os_scandir_impl must hoist ScandirIteratorType into its opening C89 " + "declaration group and preserve audit-before-type-lookup order" +) +PY_SCANDIR_IMPL_TEST +then + pass "os_scandir_impl preserves C89 declaration and audit/type lookup order" +else + bad "os_scandir_impl preserves C89 declaration and audit/type lookup order" +fi + +# DirEntry_fetch_stat must preserve path conversion/stat/cleanup order in C89. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_DIRENTRY_STAT_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("DirEntry_fetch_stat(PyObject *module, DirEntry *self, int follow_symlinks)") +end = source.index("\n}\n", start) + 3 +expected = """DirEntry_fetch_stat(PyObject *module, DirEntry *self, int follow_symlinks) +{ + int result; + int saved_errno; + STRUCT_STAT st; + PyObject *ub; +#ifdef MS_WINDOWS +#if USE_UNICODE_WCHAR_CACHE + const wchar_t *path; +#else /* USE_UNICODE_WCHAR_CACHE */ + wchar_t *path; +#endif /* USE_UNICODE_WCHAR_CACHE */ +#else /* POSIX */ + const char *path; +#endif + +#ifdef MS_WINDOWS + if (!PyUnicode_FSDecoder(self->path, &ub)) + return NULL; +#if USE_UNICODE_WCHAR_CACHE +_Py_COMP_DIAG_PUSH +_Py_COMP_DIAG_IGNORE_DEPR_DECLS + path = PyUnicode_AsUnicode(ub); +_Py_COMP_DIAG_POP +#else /* USE_UNICODE_WCHAR_CACHE */ + path = PyUnicode_AsWideCharString(ub, NULL); + Py_DECREF(ub); +#endif /* USE_UNICODE_WCHAR_CACHE */ +#else /* POSIX */ + if (!PyUnicode_FSConverter(self->path, &ub)) + return NULL; + path = PyBytes_AS_STRING(ub); + if (self->dir_fd != DEFAULT_DIR_FD) { +#ifdef HAVE_FSTATAT + if (HAVE_FSTATAT_RUNTIME) { + Py_BEGIN_ALLOW_THREADS + result = fstatat(self->dir_fd, path, &st, + follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW); + Py_END_ALLOW_THREADS + } else + +#endif /* HAVE_FSTATAT */ + { + Py_DECREF(ub); + PyErr_SetString(PyExc_NotImplementedError, "can't fetch stat"); + return NULL; + } + } + else +#endif + { + Py_BEGIN_ALLOW_THREADS + if (follow_symlinks) { + result = STAT(path, &st); + } + else { + result = LSTAT(path, &st); + } + Py_END_ALLOW_THREADS + } + saved_errno = errno; +#if defined(MS_WINDOWS) && !USE_UNICODE_WCHAR_CACHE + PyMem_Free(path); +#else /* USE_UNICODE_WCHAR_CACHE */ + Py_DECREF(ub); +#endif /* USE_UNICODE_WCHAR_CACHE */ + + if (result != 0) { + errno = saved_errno; + path_object_error(self->path); + return NULL; + } + + return _pystat_fromstructstat(module, &st); +} +""" +assert source[start:end] == expected, ( + "DirEntry_fetch_stat must hoist all path variants and saved_errno into the " + "opening C89 declaration group and preserve conversion/stat/errno/cleanup order" +) +PY_DIRENTRY_STAT_TEST +then + pass "DirEntry_fetch_stat preserves C89 declarations and conversion/stat/cleanup order" +else + bad "DirEntry_fetch_stat preserves C89 declarations and conversion/stat/cleanup order" +fi + +# DirEntry_test_mode must preserve stat/error/state-lookup order with C89 locals. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_DIRENTRY_MODE_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("DirEntry_test_mode(PyTypeObject *defining_class, DirEntry *self,", + source.index("/* Set exception and return -1 on error, 0 for False, 1 for True */")) +end = source.index("\n}\n", start) + 3 +expected = """DirEntry_test_mode(PyTypeObject *defining_class, DirEntry *self, + int follow_symlinks, unsigned short mode_bits) +{ + PyObject *stat = NULL; + PyObject *st_mode = NULL; + long mode; + int result; + _posixstate *state; +#if defined(MS_WINDOWS) || defined(HAVE_DIRENT_D_TYPE) + int is_symlink; + int need_stat; +#endif +#ifdef MS_WINDOWS + unsigned long dir_bits; +#endif + +#ifdef MS_WINDOWS + is_symlink = (self->win32_lstat.st_mode & S_IFMT) == S_IFLNK; + need_stat = follow_symlinks && is_symlink; +#elif defined(HAVE_DIRENT_D_TYPE) + is_symlink = self->d_type == DT_LNK; + need_stat = self->d_type == DT_UNKNOWN || (follow_symlinks && is_symlink); +#endif + +#if defined(MS_WINDOWS) || defined(HAVE_DIRENT_D_TYPE) + if (need_stat) { +#endif + stat = os_DirEntry_stat_impl(self, defining_class, follow_symlinks); + if (!stat) { + if (PyErr_ExceptionMatches(PyExc_FileNotFoundError)) { + /* If file doesn't exist (anymore), then return False + (i.e., say it's not a file/directory) */ + PyErr_Clear(); + return 0; + } + goto error; + } + state = get_posix_state(PyType_GetModule(defining_class)); + st_mode = PyObject_GetAttr(stat, state->st_mode); + if (!st_mode) + goto error; + + mode = PyLong_AsLong(st_mode); + if (mode == -1 && PyErr_Occurred()) + goto error; + Py_CLEAR(st_mode); + Py_CLEAR(stat); + result = (mode & S_IFMT) == mode_bits; +#if defined(MS_WINDOWS) || defined(HAVE_DIRENT_D_TYPE) + } + else if (is_symlink) { + assert(mode_bits != S_IFLNK); + result = 0; + } + else { + assert(mode_bits == S_IFDIR || mode_bits == S_IFREG); +#ifdef MS_WINDOWS + dir_bits = self->win32_lstat.st_file_attributes & FILE_ATTRIBUTE_DIRECTORY; + if (mode_bits == S_IFDIR) + result = dir_bits != 0; + else + result = dir_bits == 0; +#else /* POSIX */ + if (mode_bits == S_IFDIR) + result = self->d_type == DT_DIR; + else + result = self->d_type == DT_REG; +#endif + } +#endif + + return result; + +error: + Py_XDECREF(st_mode); + Py_XDECREF(stat); + return -1; +} +""" +assert source[start:end] == expected, ( + "DirEntry_test_mode must hoist state into the opening C89 declaration group " + "and preserve stat/error/state-lookup order" +) +PY_DIRENTRY_MODE_TEST +then + pass "DirEntry_test_mode preserves C89 declarations and stat/error/state-lookup order" +else + bad "DirEntry_test_mode preserves C89 declarations and stat/error/state-lookup order" +fi + +# DirEntry.inode must keep Windows locals C89-safe and preserve both branch orders. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_DIRENTRY_INODE_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("os_DirEntry_inode_impl(DirEntry *self)") +start = source.index("\n{\n", start) +end = source.index("\n}\n", start) + 3 +# Both platform branches must use CPython's VC6-compatible unsigned type. +assert all( + f"static_assert(sizeof(unsigned PY_LONG_LONG) >= sizeof(self->{field})," + in source[start:end] + for field in ("win32_file_index", "d_ino") +), "DirEntry.inode static_asserts must both use unsigned PY_LONG_LONG" +expected = """ +{ +#ifdef MS_WINDOWS + if (!self->got_file_index) { + PyObject *unicode; + STRUCT_STAT stat; + int result; + int saved_errno; +#if USE_UNICODE_WCHAR_CACHE + const wchar_t *path; +#else /* USE_UNICODE_WCHAR_CACHE */ + wchar_t *path; +#endif /* USE_UNICODE_WCHAR_CACHE */ + + if (!PyUnicode_FSDecoder(self->path, &unicode)) + return NULL; +#if USE_UNICODE_WCHAR_CACHE +_Py_COMP_DIAG_PUSH +_Py_COMP_DIAG_IGNORE_DEPR_DECLS + path = PyUnicode_AsUnicode(unicode); + result = LSTAT(path, &stat); + saved_errno = errno; + Py_DECREF(unicode); +_Py_COMP_DIAG_POP +#else /* USE_UNICODE_WCHAR_CACHE */ + path = PyUnicode_AsWideCharString(unicode, NULL); + Py_DECREF(unicode); + result = LSTAT(path, &stat); + + saved_errno = errno; + PyMem_Free(path); +#endif /* USE_UNICODE_WCHAR_CACHE */ + + if (result != 0) { + errno = saved_errno; + return path_object_error(self->path); + } + + self->win32_file_index = stat.st_ino; + self->got_file_index = 1; + } + static_assert(sizeof(unsigned PY_LONG_LONG) >= sizeof(self->win32_file_index), + "DirEntry.win32_file_index is larger than unsigned long long"); + return PyLong_FromUnsignedLongLong(self->win32_file_index); +#else /* POSIX */ + static_assert(sizeof(unsigned PY_LONG_LONG) >= sizeof(self->d_ino), + "DirEntry.d_ino is larger than unsigned long long"); + return PyLong_FromUnsignedLongLong(self->d_ino); +#endif +} +""" +assert source[start:end] == expected, ( + "os_DirEntry_inode_impl must hoist branch-typed path and saved_errno before " + "decoder execution and preserve decoder/path/LSTAT/errno/cleanup order" +) +PY_DIRENTRY_INODE_TEST +then + pass "DirEntry.inode preserves C89 declarations and decoder/stat/errno/cleanup order" +else + bad "DirEntry.inode preserves C89 declarations and decoder/stat/errno/cleanup order" +fi + +# The VC6 Windows wait-status branch must use CPython's MSVC6 64-bit type. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_WAITSTATUS_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("os_waitstatus_to_exitcode_impl(PyObject *module, PyObject *status_obj)") +end = source.index("\n}\n#endif", start) + 2 +function = source[start:end] +windows = function[function.index("#else\n", function.index("#ifndef MS_WINDOWS")):] +declarations = """ unsigned PY_LONG_LONG status; + unsigned PY_LONG_LONG exitcode; +""" +assert declarations in windows, ( + "Windows wait-status locals must be in the opening C89 declaration group" +) +assert windows.index("status = PyLong_AsUnsignedLongLong(status_obj);") < windows.index( + "if (status == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred())" +) < windows.index("exitcode = (status >> 8);") +assert "PyErr_Format(PyExc_ValueError, \"invalid exit code: %llu\", exitcode);" in windows +assert "return PyLong_FromUnsignedLong((unsigned long)exitcode);" in windows +PY_WAITSTATUS_TEST +then + pass "Windows wait-status conversion uses VC6-compatible 64-bit declarations" +else + bad "Windows wait-status conversion uses VC6-compatible 64-bit declarations" +fi + +# VC6's Windows headers predate the documented DLL-search flags. Require the +# shared fallback definitions after windows.h in both code paths that consume them. +if python3 - "$REPO_ROOT/cpython/Include/internal/pycore_win9x_loadlibrary.h" \ + "$REPO_ROOT/cpython/Modules/posixmodule.c" \ + "$REPO_ROOT/cpython/Python/dynload_win.c" <<'PY_WIN9X_LOAD_FLAGS_TEST' +import re +import sys +from pathlib import Path + +header_path, *source_paths = map(Path, sys.argv[1:]) +if not header_path.is_file(): + raise SystemExit("shared Win9x LoadLibrary flag compatibility header missing") +header = header_path.read_text() +if not re.search(r"#\s*if\s+defined\(_MSC_VER\)\s*&&\s*_MSC_VER\s*<\s*1300", header): + raise SystemExit("fallback must be restricted to VC6-era MSVC") +values = { + "LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR": "0x00000100", + "LOAD_LIBRARY_SEARCH_APPLICATION_DIR": "0x00000200", + "LOAD_LIBRARY_SEARCH_USER_DIRS": "0x00000400", + "LOAD_LIBRARY_SEARCH_SYSTEM32": "0x00000800", + "LOAD_LIBRARY_SEARCH_DEFAULT_DIRS": "0x00001000", +} +for name, value in values.items(): + pattern = rf"#\s*ifndef\s+{name}\s+#\s*define\s+{name}\s+{value}\b" + if not re.search(pattern, header): + raise SystemExit(f"missing documented guarded value for {name}") +for path in source_paths: + source = path.read_text() + windows = re.search(r"#\s*include\s*", source) + compat = re.search(r'#\s*include\s*"pycore_win9x_loadlibrary\.h"', source) + if windows is None or compat is None or compat.start() <= windows.start(): + raise SystemExit(f"compatibility header must follow windows.h in {path.name}") +print("VC6 LoadLibrary flags use documented values and are shared after windows.h") +PY_WIN9X_LOAD_FLAGS_TEST +then + pass "VC6 LoadLibrary search flags have guarded shared definitions in both consumers" +else + bad "VC6 LoadLibrary search flags have guarded shared definitions in both consumers" +fi + +# VC6 rejects the mixed declarations and declaration in for-init in posixmodule_exec. +# Keep initialization and traversal operations at their original points. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_POSIXMODULE_EXEC_C89_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("posixmodule_exec(PyObject *m)") +body_start = source.index("\n{\n", start) +end = source.index("\n}\n", body_start) + 3 +function = source[start:end] +expected = """posixmodule_exec(PyObject *m) +{ + _posixstate *state; + PyObject *ScandirIteratorType; + PyObject *list; + const struct have_function *trace; + + state = get_posix_state(m); +""" +if not function.startswith(expected): + raise SystemExit("posixmodule_exec C89 declaration group/initial state assignment mismatch") +if function.count("PyObject *ScandirIteratorType") != 1: + raise SystemExit("ScandirIteratorType declaration must occur once") +if function.count("PyObject *list") != 1: + raise SystemExit("list declaration must occur once") +if function.count("const struct have_function *trace") != 1: + raise SystemExit("trace declaration must occur once") +order = [ + function.index("ScandirIteratorType = PyType_FromModuleAndSpec(m, &ScandirIteratorType_spec, NULL);"), + function.index("state->ScandirIteratorType = ScandirIteratorType;"), + function.index("list = PyList_New(0);"), + function.index("for (trace = have_functions; trace->label; trace++)"), + function.index("return _PyModule_Add(m, \"_have_functions\", list);"), +] +if order != sorted(order): + raise SystemExit("posixmodule_exec initialization/traversal/return order changed") +PY_POSIXMODULE_EXEC_C89_TEST +then + pass "posixmodule_exec hoists C89 locals while preserving initialization and traversal order" +else + bad "posixmodule_exec hoists C89 locals while preserving initialization and traversal order" +fi + +# PyModuleDef uses a C99 designated initializer; preserve its declared field order for VC6. +if python3 - "$REPO_ROOT/cpython/Modules/posixmodule.c" <<'PY_POSIXMODULE_DEF_C89_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("static struct PyModuleDef posixmodule = {") +end = source.index("\n};", start) + 3 +expected = """static struct PyModuleDef posixmodule = { + PyModuleDef_HEAD_INIT, + MODNAME, + posix__doc__, + sizeof(_posixstate), + posix_methods, + posixmodile_slots, + _posix_traverse, + _posix_clear, + _posix_free, +};""" +if source[start:end] != expected: + raise SystemExit("posixmodule PyModuleDef fields must use declared positional order") +PY_POSIXMODULE_DEF_C89_TEST +then + pass "posixmodule PyModuleDef uses C89-compatible declared field order" +else + bad "posixmodule PyModuleDef uses C89-compatible declared field order" +fi + +# VC6 needs its platform 64-bit spelling plus C89-compatible declarations and initializers. +if python3 - "$REPO_ROOT/cpython/Modules/sha1module.c" <<'PY_SHA1_VC6_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +if "typedef PY_LONG_LONG SHA1_INT64;" not in source: + raise SystemExit("SHA1_INT64 must use the compiler-compatible PY_LONG_LONG alias") + +type_start = source.index("static PyType_Spec sha1_type_spec = {") +type_end = source.index("\n};", type_start) + 3 +expected_type = """static PyType_Spec sha1_type_spec = { + "_sha1.sha1", + sizeof(SHA1object), + 0, + (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), + sha1_type_slots +};""" +if source[type_start:type_end] != expected_type: + raise SystemExit("sha1_type_spec must use C89-compatible PyType_Spec field order") + +start = source.index("_sha1_sha1_impl(PyObject *module, PyObject *string, int usedforsecurity)") +body_start = source.index("\n{\n", start) +end = source.index("\n}\n", body_start) + 3 +body = source[body_start + 1:end] +if not body.startswith("""{ + SHA1object *new; + Py_buffer buf; + SHA1State *st; + +"""): + raise SystemExit("_sha1_sha1_impl must hoist st into its C89 declaration group") +if body.count("SHA1State *st") != 1: + raise SystemExit("_sha1_sha1_impl must declare st exactly once") +if not (body.index("GET_BUFFER_VIEW_OR_ERROUT(string, &buf);") < + body.index("st = sha1_get_state(module);") < + body.index("newSHA1object(st)")): + raise SystemExit("_sha1_sha1_impl must preserve buffer/state/object initialization order") + +module_start = source.index("static struct PyModuleDef _sha1module = {") +module_end = source.index("\n};", module_start) + 3 +expected_module = """static struct PyModuleDef _sha1module = { + PyModuleDef_HEAD_INIT, + "_sha1", + NULL, + sizeof(SHA1State), + SHA1_functions, + _sha1_slots, + _sha1_traverse, + _sha1_clear, + _sha1_free +};""" +if source[module_start:module_end] != expected_module: + raise SystemExit("_sha1module must use C89-compatible positional PyModuleDef fields") +PY_SHA1_VC6_TEST +then + pass "sha1module uses VC6-compatible 64-bit, C89 declaration, and initializer forms" +else + bad "sha1module uses VC6-compatible 64-bit, C89 declaration, and initializer forms" +fi + +# VC6 needs C89-compatible type specs, locals, and the module definition in sha256module. +if python3 - "$REPO_ROOT/cpython/Modules/sha256module.c" <<'PY_SHA256_VC6_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +for name, typename in (("sha224_type_spec", "_sha256.sha224"), ("sha256_type_spec", "_sha256.sha256")): + start = source.index(f"static PyType_Spec {name} = {{") + end = source.index("\n};", start) + 3 + expected = f"""static PyType_Spec {name} = {{ + \"{typename}\", + sizeof(SHAobject), + 0, + (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), + sha256_types_slots +}};""" + if source[start:end] != expected: + raise SystemExit(f"{name} must use C89-compatible declared PyType_Spec field order") + +for function, constructor in (("_sha256_sha256_impl", "newSHA256object"), + ("_sha256_sha224_impl", "newSHA224object")): + start = source.index(function + "(PyObject *module, PyObject *string, int usedforsecurity)") + body_start = source.index("\n{\n", start) + end = source.index("\n}\n", body_start) + 3 + body = source[body_start + 1:end] + if not body.startswith("""{ + Py_buffer buf; + _sha256_state *state; + SHAobject *new; + +"""): + raise SystemExit(f"{function} must hoist state/new into its C89 declaration group") + if body.count("_sha256_state *state") != 1 or body.count("SHAobject *new") != 1: + raise SystemExit(f"{function} must declare state and new exactly once") + if not (body.index("GET_BUFFER_VIEW_OR_ERROUT(string, &buf);") < + body.index("state = PyModule_GetState(module);") < + body.index(f"new = {constructor}(state)")): + raise SystemExit(f"{function} must preserve buffer/state/object initialization order") + +start = source.index("static struct PyModuleDef _sha256module = {") +end = source.index("\n};", start) + 3 +expected_module = """static struct PyModuleDef _sha256module = { + PyModuleDef_HEAD_INIT, + "_sha256", + NULL, + sizeof(_sha256_state), + SHA_functions, + _sha256_slots, + _sha256_traverse, + _sha256_clear, + _sha256_free +};""" +if source[start:end] != expected_module: + raise SystemExit("_sha256module must use C89-compatible positional PyModuleDef fields") +PY_SHA256_VC6_TEST +then + pass "sha256module uses C89-compatible type specs, declarations, and module fields" +else + bad "sha256module uses C89-compatible type specs, declarations, and module fields" +fi + +# VC6 rejects C99 ULL suffixes; keep the 64-bit SHA512 constants and casts exact. +if python3 - "$REPO_ROOT/cpython/Modules/sha512module.c" <<'PY_SHA512_VC6_TEST' +import re +import subprocess +import sys +from pathlib import Path + +path = Path(sys.argv[1]) +source = path.read_text() +repo = path.parents[1] +original = subprocess.run( + ["git", "-C", str(repo), "show", "HEAD:Modules/sha512module.c"], + check=True, capture_output=True, text=True, +).stdout +original_values = re.findall(r"(0x[0-9a-fA-F]+)ULL\b", original) +portable_values = re.findall(r"PY_ULL\((0x[0-9a-fA-F]+)\)", source) +if len(original_values) != 83 or original_values != portable_values: + raise SystemExit("all 83 SHA512 64-bit literals must retain their exact values through PY_ULL") +if re.search(r"0x[0-9a-fA-F]+ULL\b", source): + raise SystemExit("sha512module still contains VC6-incompatible ULL literals") +if "unsigned long long" in source: + raise SystemExit("sha512module still contains VC6-incompatible long-long casts") +if source.count("unsigned PY_LONG_LONG") != 3: + raise SystemExit("all SHA512 macro casts must use the compiler-compatible 64-bit type") + +for name, typename, slots in (("sha512_sha384_type_spec", "_sha512.sha384", "sha512_sha384_type_slots"), + ("sha512_sha512_type_spec", "_sha512.sha512", "sha512_sha512_type_slots")): + start = source.index(f"static PyType_Spec {name} = {{") + end = source.index("\n};", start) + 3 + expected = f"""static PyType_Spec {name} = {{ + \"{typename}\", + sizeof(SHAobject), + 0, + (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), + {slots} +}};""" + if source[start:end] != expected: + raise SystemExit(f"{name} must use C89-compatible declared PyType_Spec field order") + +start = source.index("static struct PyModuleDef _sha512module = {") +end = source.index("\n};", start) + 3 +expected_module = """static struct PyModuleDef _sha512module = { + PyModuleDef_HEAD_INIT, + "_sha512", + NULL, + sizeof(SHA512State), + SHA_functions, + _sha512_slots, + _sha512_traverse, + _sha512_clear, + _sha512_free +};""" +if source[start:end] != expected_module: + raise SystemExit("_sha512module must use C89-compatible positional PyModuleDef fields") +PY_SHA512_VC6_TEST +then + pass "sha512module preserves all SHA constants with VC6-compatible literals and casts" +else + bad "sha512module preserves all SHA constants with VC6-compatible literals and casts" +fi + +# VC6 requires positional initialization of the Windows wakeup fields. +if python3 - "$REPO_ROOT/cpython/Modules/signalmodule.c" <<'PY_SIGNAL_WAKEUP_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("#ifdef MS_WINDOWS\n#define INVALID_FD") +end = source.index("#else", start) +expected = """#ifdef MS_WINDOWS +#define INVALID_FD ((SOCKET_T)-1) + +static volatile struct { + SOCKET_T fd; + int warn_on_full_buffer; + int use_send; +} wakeup = {INVALID_FD, 1, 0}; +""" +if source[start:end] != expected: + raise SystemExit("Windows wakeup must use C89 positional {INVALID_FD, 1, 0} in declared field order") +PY_SIGNAL_WAKEUP_TEST +then + pass "signalmodule Windows wakeup uses C89 positional initialization in field order" +else + bad "signalmodule Windows wakeup uses C89 positional initialization in field order" +fi + +# VC6 signal support: exclude absent Bluetooth APIs and keep signal paths C89-safe. +if python3 - "$REPO_ROOT/cpython/Modules/signalmodule.c" \ + "$REPO_ROOT/cpython/Modules/socketmodule.h" \ + "$REPO_ROOT/cpython/Modules/socketmodule.c" \ + "$REPO_ROOT/cpython/Include/internal/pycore_pystate.h" <<'PY_SIGNAL_VC6_TEST' +import re +import sys +from pathlib import Path + +signal = Path(sys.argv[1]).read_text() +socket_header = Path(sys.argv[2]).read_text() +socket_source = Path(sys.argv[3]).read_text() +pystate = Path(sys.argv[4]).read_text() + +def body(signature): + start = signal.index(signature) + opening = signal.index("\n{\n", start) + end = signal.index("\n}\n", opening) + 3 + return signal[opening + 1:end] + +if "#elif defined(MS_WINDOWS) && defined(AF_BTH)" not in socket_header: + raise SystemExit("Windows Bluetooth sockaddr storage must be gated by AF_BTH") +if "#if defined(MS_WINDOWS) && defined(AF_BTH)\n#define sockaddr_rc SOCKADDR_BTH_REDEF" not in socket_source: + raise SystemExit("Windows Bluetooth socket support must be gated by AF_BTH") +if not re.search(r"#\s*ifndef EWOULDBLOCK\s+#\s*define EWOULDBLOCK EAGAIN", signal): + raise SystemExit("VC6 signal write path needs an EWOULDBLOCK fallback") +if not re.search(r"#if defined\(_MSC_VER\) && _MSC_VER < 1300 && !defined\(__func__\)\s+#\s*define __func__", pystate): + raise SystemExit("pycore_pystate must supply VC6's missing __func__ token") +if "for (int " in signal: + raise SystemExit("signalmodule contains a VC6-incompatible loop declaration") + +trip = body("trip_signal(int sig_num)") +if not trip.startswith("""{ + int fd; + PyInterpreterState *interp; + +"""): + raise SystemExit("trip_signal locals must be in the opening C89 declaration group") +if not (trip.index("interp = _PyInterpreterState_Main();") < trip.index("fd = Py_SAFE_DOWNCAST")): + raise SystemExit("trip_signal must preserve interpreter lookup before fd conversion") + +signal_fn = body("signal_signal_impl(PyObject *module, int signalnum, PyObject *handler)") +if not signal_fn.startswith("""{ + _signal_module_state *modstate; + PyObject *old_handler; + void (*func)(int); + PyThreadState *tstate; + + modstate = get_signal_state(module); +""") or "tstate = _PyThreadState_GET();" not in signal_fn: + raise SystemExit("signal_signal_impl must hoist tstate and preserve state lookup") + +wakeup = body("signal_set_wakeup_fd(PyObject *self, PyObject *args, PyObject *kwds)") +if " PyThreadState *tstate;" not in wakeup[:500] or " tstate = _PyThreadState_GET();" not in wakeup: + raise SystemExit("signal_set_wakeup_fd must hoist tstate and assign it after argument parsing") +set_fd = body("PySignal_SetWakeupFd(int fd)") +if not set_fd.startswith("""{ + int old_fd; + + if (fd < 0) { +""") or "old_fd = Py_SAFE_DOWNCAST" not in set_fd: + raise SystemExit("PySignal_SetWakeupFd must hoist old_fd without reordering normalization") + +valid = body("signal_valid_signals_impl(PyObject *module)") +if not (valid.index("PyObject *tup;") < valid.index("Py_BuildValue") and + valid.index("PyObject *set;") < valid.index("Py_BuildValue") and + "set = PySet_New(tup);" in valid): + raise SystemExit("signal_valid_signals must declare Windows locals before statements") + +handlers = body("signal_get_set_handlers(signal_state_t *state, PyObject *mod_dict)") +if "for (signum = 1; signum < Py_NSIG; signum++)" not in handlers: + raise SystemExit("signal_get_set_handlers must use a C89 loop counter") +if not (handlers.index("PyObject *old_func;") < handlers.index("old_func = get_handler(signum);") and + handlers.index("PyObject *sigint_func;") < handlers.index("sigint_func = get_handler(SIGINT);")): + raise SystemExit("signal_get_set_handlers must hoist post-statement locals") + +module_exec = body("signal_module_exec(PyObject *m)") +if not module_exec.startswith("""{ + signal_state_t *state; + _signal_module_state *modstate; + PyObject *d; + PyThreadState *tstate; + + assert(!PyErr_Occurred()); +"""): + raise SystemExit("signal_module_exec must use one opening C89 declaration group") +module_start = signal.index("static struct PyModuleDef signal_module = {") +module_end = signal.index("\n};", module_start) + 3 +expected_module = """static struct PyModuleDef signal_module = { + PyModuleDef_HEAD_INIT, + "_signal", + module_doc, + sizeof(_signal_module_state), + signal_methods, + signal_slots, +#ifdef PYHAVE_ITIMER_ERROR + _signal_module_traverse, + _signal_module_clear, + _signal_module_free, +#endif +};""" +if signal[module_start:module_end] != expected_module: + raise SystemExit("signal_module must use C89-compatible positional PyModuleDef fields") + +fini = body("_PySignal_Fini(void)") +if not fini.startswith("""{ + signal_state_t *state; + int signum; + + state = &signal_global_state; +""") or "for (signum = 1; signum < Py_NSIG; signum++)" not in fini: + raise SystemExit("_PySignal_Fini must hoist its loop counter") + +check = body("_PyErr_CheckSignalsTstate(PyThreadState *tstate)") +if not check.startswith("""{ + _PyInterpreterFrame *frame; + signal_state_t *state; + int i; + +""") or "for (i = 1; i < Py_NSIG; i++)" not in check: + raise SystemExit("_PyErr_CheckSignalsTstate must hoist frame/state/loop locals") +if not (check.index("PyObject *func;") < check.index("func = get_handler(i);") and + check.index("PyObject *arglist;") < check.index("arglist = NULL;") and + check.index("PyObject *result;") < check.index("if (arglist)")): + raise SystemExit("signal handler loop locals must be declared before statements") + +interrupt = body("PyErr_SetInterruptEx(int signum)") +if not interrupt.startswith("""{ + signal_state_t *state; + PyObject *func; + +""") or "state = &signal_global_state;" not in interrupt: + raise SystemExit("PyErr_SetInterruptEx must hoist state and handler locals") +init = body("_PySignal_Init(int install_signal_handlers)") +if not init.startswith("""{ + signal_state_t *state; + int signum; + + state = &signal_global_state; +""") or "for (signum = 1; signum < Py_NSIG; signum++)" not in init: + raise SystemExit("_PySignal_Init must hoist its loop counter") +print("VC6 signal paths use gated Bluetooth, fallback errno, and C89 declarations") +PY_SIGNAL_VC6_TEST +then + pass "signalmodule and socket Bluetooth paths are VC6-compatible" +else + bad "signalmodule and socket Bluetooth paths are VC6-compatible" +fi + +# VC6 symtable module: preserve generated-wrapper and compiler-flag semantics. +if python3 - "$REPO_ROOT/cpython/Modules/symtablemodule.c" \ + "$REPO_ROOT/cpython/Modules/clinic/symtablemodule.c.h" <<'PY_SYMTABLE_VC6_TEST' +from pathlib import Path +import sys + +source = Path(sys.argv[1]).read_text() +clinic = Path(sys.argv[2]).read_text() + +def body(text, signature): + start = text.index(signature) + opening = text.index("\n{\n", start) + closing = text.index("\n}\n", opening) + 3 + return text[opening + 1:closing] + +wrapper = body(clinic, "_symtable_symtable(PyObject *module, PyObject *const *args, Py_ssize_t nargs)") +if not wrapper.startswith("""{ + PyObject *return_value = NULL; + PyObject *source; + PyObject *filename; + const char *startstr; + Py_ssize_t startstr_length; +""") or wrapper.count("Py_ssize_t startstr_length;") != 1: + raise SystemExit("generated _symtable_symtable must declare startstr_length in its opening C89 group") +if wrapper.index("Py_ssize_t startstr_length;") > wrapper.index("if (!_PyArg_CheckPositional"): + raise SystemExit("generated Clinic local must precede statements") + +impl = body(source, "_symtable_symtable_impl(PyObject *module, PyObject *source,") +if "PyCompilerFlags cf = _PyCompilerFlags_INIT;" in impl: + raise SystemExit("symtablemodule must not use the compound-literal compiler-flags initializer") +if not (impl.index("PyCompilerFlags cf;") < impl.index("cf.cf_flags = 0;") < + impl.index("cf.cf_feature_version = PY_MINOR_VERSION;") < + impl.index("cf.cf_flags = PyCF_SOURCE_IS_UTF8;") < + impl.index("str = _Py_SourceAsString")): + raise SystemExit("symtablemodule must explicitly initialize compiler flags and preserve assignment order") +if impl.index("const char *str;") > impl.index("cf.cf_flags = 0;"): + raise SystemExit("symtablemodule str local must be declared before statements") + +module_start = source.index("static struct PyModuleDef symtablemodule = {") +module_end = source.index("\n};", module_start) + 3 +expected = """static struct PyModuleDef symtablemodule = { + PyModuleDef_HEAD_INIT, + "_symtable", + NULL, + 0, + symtable_methods, + symtable_slots, +};""" +if source[module_start:module_end] != expected: + raise SystemExit("symtablemodule must use C89-compatible positional PyModuleDef fields") +print("VC6 symtable wrapper, flags, and module definition are C89-compatible") +PY_SYMTABLE_VC6_TEST +then + pass "symtablemodule generated and handwritten code are VC6-compatible" +else + bad "symtablemodule generated and handwritten code are VC6-compatible" +fi + +# VC6 first C89 blockers in _threadmodule.c: hoist locals, retain point of use. +if python3 - "$REPO_ROOT/cpython/Modules/_threadmodule.c" <<'PY_THREAD_VC6_TEST' +from pathlib import Path +import sys +source = Path(sys.argv[1]).read_text() + +def body(signature): + start = source.rindex(signature) + opening = source.index("\n{\n", start) + closing = source.index("\n}\n", opening) + 3 + return source[opening + 1:closing] + +lock_dealloc = body("lock_dealloc(lockobject *self)") +if not lock_dealloc.startswith("""{ + PyTypeObject *tp; + + PyObject_GC_UnTrack(self); +""") or "tp = Py_TYPE(self);" not in lock_dealloc: + raise SystemExit("lock_dealloc must hoist tp and assign it after lock cleanup") +acquire_timed = body("acquire_timed(PyThread_type_lock lock, _PyTime_t timeout)") +if not acquire_timed.startswith("""{ + _PyTime_t endtime = 0; + PyLockStatus r; +"""): + raise SystemExit("acquire_timed must declare r before timeout statements") +acquire = body("lock_PyThread_acquire_lock(lockobject *self, PyObject *args, PyObject *kwds)") +if not acquire.startswith("""{ + _PyTime_t timeout; + PyLockStatus r; +""") or "r = acquire_timed(self->lock_lock, timeout);" not in acquire: + raise SystemExit("lock acquire must hoist r and assign it after argument parsing") +def initializer(symbol): + start = source.index("static PyType_Spec " + symbol + " = {") + end = source.index("\n};", start) + 3 + return source[start:end] + +expected_specs = { + "lock_type_spec": """static PyType_Spec lock_type_spec = { + "_thread.lock", + sizeof(lockobject), + 0, + (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE), + lock_type_slots, +};""", + "rlock_type_spec": """static PyType_Spec rlock_type_spec = { + "_thread.RLock", + sizeof(rlockobject), + 0, + (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), + rlock_type_slots, +};""", + "local_dummy_type_spec": """static PyType_Spec local_dummy_type_spec = { + "_thread._localdummy", + sizeof(localdummyobject), + 0, + (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_IMMUTABLETYPE), + local_dummy_type_slots, +};""", + "local_type_spec": """static PyType_Spec local_type_spec = { + "_thread._local", + sizeof(localobject), + 0, + (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + local_type_slots, +};""", +} +for symbol, expected in expected_specs.items(): + if initializer(symbol) != expected: + raise SystemExit(symbol + " must use declared-order positional PyType_Spec fields") + +desc_start = source.index("static PyStructSequence_Desc ExceptHookArgs_desc = {") +desc_end = source.index("\n};", desc_start) + 3 +expected_desc = """static PyStructSequence_Desc ExceptHookArgs_desc = { + "_thread._ExceptHookArgs", + ExceptHookArgs__doc__, + ExceptHookArgs_fields, + 4 +};""" +if source[desc_start:desc_end] != expected_desc: + raise SystemExit("ExceptHookArgs_desc must use declared-order positional fields") + +module_start = source.index("static struct PyModuleDef thread_module = {") +module_end = source.index("\n};", module_start) + 3 +expected_module = """static struct PyModuleDef thread_module = { + PyModuleDef_HEAD_INIT, + "_thread", + thread_doc, + sizeof(thread_module_state), + thread_methods, + thread_module_slots, + thread_module_traverse, + thread_module_clear, + thread_module_free, +};""" +if source[module_start:module_end] != expected_module: + raise SystemExit("thread_module must use declared-order positional PyModuleDef fields") +for name, expected_prefix, after_marker in ( + ("rlock_dealloc(rlockobject *self)", + """{ + PyTypeObject *tp; + + PyObject_GC_UnTrack(self); +""", "PyThread_free_lock(self->rlock_lock);"), + ("localdummy_dealloc(localdummyobject *self)", + """{ + PyTypeObject *tp; + + if (self->weakreflist != NULL)""", "PyObject_ClearWeakRefs((PyObject *) self);"), + ("local_dealloc(localobject *self)", + """{ + PyTypeObject *tp; + + /* Weakrefs must be invalidated right now""", "Py_XDECREF(self->key);"), +): + current = body(name) + if not current.startswith(expected_prefix) or not ( + current.index(after_marker) < current.index("tp = Py_TYPE(self);") + ): + raise SystemExit(name + " must hoist tp but preserve assignment after cleanup") + +create_dummy = body("_local_create_dummy(localobject *self, thread_module_state *state)") +if " int r;" not in create_dummy[:250] or not ( + create_dummy.index("wr = PyWeakref_NewRef") < + create_dummy.index("r = PyDict_SetItem(self->dummies, wr, ldict);") +): + raise SystemExit("_local_create_dummy must hoist r and assign it after weakref creation") + +local_new = body("local_new(PyTypeObject *type, PyObject *args, PyObject *kw)") +if not local_new.startswith("""{ + static PyMethodDef wr_callback_def = { + "_localdummy_destroyed", (PyCFunction) _localdummy_destroyed, METH_O + }; + + PyObject *module; + thread_module_state *state; + localobject *self; + PyObject *wr; +"""): + raise SystemExit("local_new must hoist module/state/self/wr declarations before statements") +for earlier, later in ( + ("if (type->tp_init == PyBaseObject_Type.tp_init)", "module = PyType_GetModuleByDef"), + ("module = PyType_GetModuleByDef", "state = get_thread_state(module);"), + ("state = get_thread_state(module);", "self = (localobject *)type->tp_alloc(type, 0);"), + ("self->dummies = PyDict_New();", "wr = PyWeakref_NewRef((PyObject *) self, NULL);"), +): + if local_new.index(earlier) >= local_new.index(later): + raise SystemExit("local_new must preserve control flow and original assignment order") + +local_clear = body("local_clear(localobject *self)") +key_block = local_clear[local_clear.index("if (self->key) {"):] +if not (key_block.index("PyThreadState *tstate;") < key_block.index("HEAD_LOCK(runtime);") and + key_block.index("HEAD_LOCK(runtime);") < key_block.index("tstate = PyInterpreterState_ThreadHead(interp);") < key_block.index("HEAD_UNLOCK(runtime);")): + raise SystemExit("local_clear must hoist tstate and keep thread-head lookup under the runtime lock") + +ldict = body("_ldict(localobject *self, thread_module_state *state)") +if not ldict.startswith("""{ + PyObject *tdict; + PyObject *ldict; + PyObject *dummy; + + tdict = PyThreadState_GetDict(); +"""): + raise SystemExit("_ldict must hoist locals and retain the thread-dict lookup point") +if not (ldict.index("if (tdict == NULL)") < ldict.index("dummy = PyDict_GetItemWithError")): + raise SystemExit("_ldict must not access the dummy until the thread dictionary is validated") + +setattro = body("local_setattro(localobject *self, PyObject *name, PyObject *v)") +if not setattro.startswith("""{ + PyObject *module; + thread_module_state *state; + PyObject *ldict; + int r; + + module = PyType_GetModuleByDef""") or "r = PyObject_RichCompareBool" not in setattro: + raise SystemExit("local_setattro must hoist r while preserving module/dict/compare order") +getattro = body("local_getattro(localobject *self, PyObject *name)") +if not getattro.startswith("""{ + PyObject *module; + thread_module_state *state; + PyObject *ldict; + int r; + PyObject *value; + + module = PyType_GetModuleByDef""") or not ( + getattro.index("r = PyObject_RichCompareBool") < + getattro.index("value = PyDict_GetItemWithError") +): + raise SystemExit("local_getattro must hoist locals and preserve comparison-before-lookup order") + +destroyed = body("_localdummy_destroyed(PyObject *localweakref, PyObject *dummyweakref)") +if not destroyed.startswith("""{ + PyObject *obj; + localobject *self; + + assert(PyWeakref_CheckRef(localweakref)); + obj = PyWeakref_GET_OBJECT(localweakref); +""") or not ( + destroyed.index("if (obj == Py_None)") < destroyed.index("self = (localobject *)Py_NewRef(obj);") +): + raise SystemExit("_localdummy_destroyed must hoist obj/self and preserve the None check") + +thread_run = body("thread_run(void *boot_raw)") +if not thread_run.startswith("""{ + struct bootstate *boot; + PyThreadState *tstate; + PyObject *res; + + boot = (struct bootstate *) boot_raw; + tstate = boot->tstate; +""") or not ( + thread_run.index("_PyThreadState_SetCurrent(tstate);") < + thread_run.index("res = PyObject_Call(boot->func, boot->args, boot->kwargs);") +): + raise SystemExit("thread_run must hoist locals and keep the call after current-thread setup") + +start_thread = body("thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs)") +if not start_thread.startswith("""{ + PyObject *func, *args, *kwargs = NULL; + PyInterpreterState *interp; + struct bootstate *boot; + unsigned long ident; +""") or not ( + start_thread.index("if (!PyArg_UnpackTuple") < + start_thread.index("interp = _PyInterpreterState_GET();") < + start_thread.index("boot = PyMem_RawMalloc(sizeof(struct bootstate));") < + start_thread.index("ident = PyThread_start_new_thread") +): + raise SystemExit("start_new_thread must preserve validation/allocation/thread-start order") + +hook_file = body("thread_excepthook_file(PyObject *file, PyObject *exc_type, PyObject *exc_value,") +if not hook_file.startswith("""{ + PyObject *name; + PyObject *res; +""") or not ( + hook_file.index("name = NULL;") < hook_file.index("if (thread != Py_None)") and + hook_file.index("res = PyObject_CallMethodNoArgs") < hook_file.index("Py_DECREF(res);") +): + raise SystemExit("thread_excepthook_file must hoist locals without changing lookup/flush order") + +hook = body("thread_excepthook(PyObject *module, PyObject *args)") +if not hook.startswith("""{ + thread_module_state *state; + PyObject *exc_type; + PyObject *exc_value; + PyObject *exc_tb; + PyObject *thread; + PyThreadState *tstate; + PyObject *file; + int res; + + state = get_thread_state(module); +""") or not ( + hook.index("exc_type = PyStructSequence_GET_ITEM(args, 0);") < + hook.index("if (exc_type == PyExc_SystemExit)") < + hook.index("exc_value = PyStructSequence_GET_ITEM(args, 1);") < + hook.index("tstate = _PyThreadState_GET();") < + hook.index("res = thread_excepthook_file") +): + raise SystemExit("thread_excepthook must hoist locals and retain filtering/lookup/dispatch order") + +module_exec = body("thread_module_exec(PyObject *module)") +if not module_exec.startswith("""{ + thread_module_state *state; + PyObject *d; + PyTypeObject *rlock_type; + double timeout_max; + double time_max; + + state = get_thread_state(module); + d = PyModule_GetDict(module); +""") or any(declaration in module_exec for declaration in ( + "PyTypeObject *rlock_type =", "double timeout_max =", "double time_max =")) or not ( + module_exec.index("rlock_type = (PyTypeObject *)PyType_FromSpec") < + module_exec.index("timeout_max = (double)PY_TIMEOUT_MAX * 1e-6;") < + module_exec.index("time_max = _PyTime_AsSecondsDouble(_PyTime_MAX);") +): + raise SystemExit("thread_module_exec must hoist locals and preserve type/module initialization order") + +time_header = (Path(sys.argv[1]).parents[1] / "Include/cpython/pytime.h").read_text() +if """#if defined(_MSC_VER) && _MSC_VER < 1300 +# define _PyTime_MIN PY_LLONG_MIN +# define _PyTime_MAX PY_LLONG_MAX +#else +# define _PyTime_MIN INT64_MIN +# define _PyTime_MAX INT64_MAX +#endif""" not in time_header: + raise SystemExit("pytime limits must use PY_LLONG bounds under VC6 without changing modern targets") + +tracemalloc_source = (Path(sys.argv[1]).parents[1] / "Modules/_tracemalloc.c").read_text() + +def tracemalloc_body(signature): + start = tracemalloc_source.rindex(signature) + opening = tracemalloc_source.index("\n{\n", start) + closing = tracemalloc_source.index("\n}\n", opening) + 3 + return tracemalloc_source[opening + 1:closing] + +compare_traceback = tracemalloc_body("hashtable_compare_traceback(const void *key1, const void *key2)") +if not compare_traceback.startswith("""{ + const traceback_t *traceback1 = (const traceback_t *)key1; + const traceback_t *traceback2 = (const traceback_t *)key2; + int i; +""") or "for (int i =" in compare_traceback or "for (i=0;" not in compare_traceback: + raise SystemExit("tracemalloc traceback comparison loop must declare its counter before statements") + +get_frame = tracemalloc_body("tracemalloc_get_frame(_PyInterpreterFrame *pyframe, frame_t *frame)") +if not get_frame.startswith("""{ + int lineno; + PyObject *filename; + _Py_hashtable_entry_t *entry; + + frame->filename = &_Py_STR(anon_unknown); +""") or not ( + get_frame.index("lineno = _PyInterpreterFrame_GetLine(pyframe);") < + get_frame.index("filename = pyframe->f_code->co_filename;") < + get_frame.index("entry = _Py_hashtable_get_entry(tracemalloc_filenames, filename);") +) or "PyObject *filename =" in get_frame or "_Py_hashtable_entry_t *entry;" in get_frame[get_frame.index("frame->filename"):]: + raise SystemExit("tracemalloc frame capture must hoist locals but preserve assignment points") + +traceback_frames = tracemalloc_body("traceback_get_frames(traceback_t *traceback)") +if not traceback_frames.startswith("""{ + PyThreadState *tstate; + _PyInterpreterFrame *pyframe; + + tstate = PyGILState_GetThisThreadState(); +""") or not ( + traceback_frames.index("if (tstate == NULL)") < + traceback_frames.index("pyframe = tstate->cframe->current_frame;") +) or "_PyInterpreterFrame *pyframe =" in traceback_frames: + raise SystemExit("tracemalloc frame walk must hoist pyframe and preserve the null-state guard") + +remove_trace = tracemalloc_body("tracemalloc_remove_trace(unsigned int domain, uintptr_t ptr)") +if not remove_trace.startswith("""{ + _Py_hashtable_t *traces; + trace_t *trace; + + assert(_Py_tracemalloc_config.tracing); +""") or not ( + remove_trace.index("traces = tracemalloc_get_traces_table(domain);") < + remove_trace.index("trace = _Py_hashtable_steal(traces, TO_PTR(ptr));") +) or "_Py_hashtable_t *traces =" in remove_trace or "trace_t *trace =" in remove_trace: + raise SystemExit("tracemalloc removal must hoist table/trace locals and preserve lookup order") + +add_trace = tracemalloc_body("tracemalloc_add_trace(unsigned int domain, uintptr_t ptr,") +if not add_trace.startswith("""{ + traceback_t *traceback; + _Py_hashtable_t *traces; + trace_t *trace; + int res; + + assert(_Py_tracemalloc_config.tracing); +""") or not ( + add_trace.index("traceback = traceback_new();") < + add_trace.index("traces = tracemalloc_get_traces_table(domain);") < + add_trace.index("trace = _Py_hashtable_get(traces, TO_PTR(ptr));") < + add_trace.index("res = _Py_hashtable_set(traces, TO_PTR(ptr), trace);") +) or "traceback_t *traceback =" in add_trace or "int res =" in add_trace: + raise SystemExit("tracemalloc insertion must hoist locals and preserve traceback/table/insert order") + +pyport_header = (Path(sys.argv[1]).parents[1] / "Include/pyport.h").read_text() +if """#if defined(_MSC_VER) && _MSC_VER < 1300 && !defined(__func__) +# define __func__ "" +#endif""" not in pyport_header: + raise SystemExit("pyport must provide the VC6 __func__ fallback used by Py_FatalError") + +traceback_object = tracemalloc_body("traceback_to_pyobject(traceback_t *traceback, _Py_hashtable_t *intern_table)") +if not traceback_object.startswith("""{ + PyObject *frames; + int i; +""") or "for (int i=" in traceback_object or "for (i=0;" not in traceback_object: + raise SystemExit("tracemalloc tuple conversion must hoist the frame-loop counter") + +copy_traces = tracemalloc_body("tracemalloc_copy_traces(_Py_hashtable_t *traces)") +if not copy_traces.startswith("""{ + _Py_hashtable_t *traces2; + int err; + + traces2 = tracemalloc_create_traces_table(); +""") or not ( + copy_traces.index("if (traces2 == NULL)") < + copy_traces.index("err = _Py_hashtable_foreach(traces,") +) or "int err =" in copy_traces: + raise SystemExit("tracemalloc table copy must hoist err and preserve allocation-before-copy") + +copy_domains = tracemalloc_body("tracemalloc_copy_domains(_Py_hashtable_t *domains)") +if not copy_domains.startswith("""{ + _Py_hashtable_t *domains2; + int err; + + domains2 = tracemalloc_create_domains_table(); +""") or not ( + copy_domains.index("if (domains2 == NULL)") < + copy_domains.index("err = _Py_hashtable_foreach(domains,") +) or "int err =" in copy_domains: + raise SystemExit("tracemalloc domain copy must hoist err and preserve allocation-before-copy") + +fill_traces = tracemalloc_body("tracemalloc_get_traces_fill(_Py_hashtable_t *traces,") +if not fill_traces.startswith("""{ + get_traces_t *get_traces; + const trace_t *trace; + PyObject *tuple; + int res; + + get_traces = user_data; +""") or not ( + fill_traces.index("tuple = trace_to_pyobject") < + fill_traces.index("res = PyList_Append(get_traces->list, tuple);") +) or "int res =" in fill_traces: + raise SystemExit("tracemalloc trace-list fill must hoist locals without moving reference operations") + +get_traces_impl = tracemalloc_body("_tracemalloc__get_traces_impl(PyObject *module)") +if not get_traces_impl.startswith("""{ + get_traces_t get_traces; + int err; +""") or "int err =" in get_traces_impl: + raise SystemExit("tracemalloc get-traces must declare err before statements for VC6") +if not ( + get_traces_impl.index("set_reentrant(1);") < + get_traces_impl.index("err = _Py_hashtable_foreach(get_traces.traces,") < + get_traces_impl.index("if (!err)") < + get_traces_impl.index("err = _Py_hashtable_foreach(get_traces.domains,") < + get_traces_impl.index("set_reentrant(0);") < + get_traces_impl.index("if (err)") +): + raise SystemExit("tracemalloc get-traces must preserve traversal and reentrancy order") + +get_traceback = tracemalloc_body("tracemalloc_get_traceback(unsigned int domain, uintptr_t ptr)") +assert get_traceback == """{ + trace_t *trace; + _Py_hashtable_t *traces; + + if (!_Py_tracemalloc_config.tracing) + return NULL; + + TABLES_LOCK(); + traces = tracemalloc_get_traces_table(domain); + if (traces) { + trace = _Py_hashtable_get(traces, TO_PTR(ptr)); + } + else { + trace = NULL; + } + TABLES_UNLOCK(); + + if (!trace) { + return NULL; + } + + return trace->traceback; +} +""", "tracemalloc_get_traceback must hoist only trace/traces and preserve guarded, locked lookup order" + +print("_thread and tracemalloc VC6/C89 regression checks passed") +PY_THREAD_VC6_TEST +then + pass "_thread lock paths pass initial VC6 C89 checks" +else + bad "_thread lock paths pass initial VC6 C89 checks" +fi + +# Keep this NewReference slice limited to C89 declaration placement, with +# assignments, control flow, and table locking preserved exactly. +if python3 - "$REPO_ROOT/cpython/Modules/_tracemalloc.c" <<'PY_NEWREFERENCE_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("_PyTraceMalloc_NewReference(PyObject *op)\n{\n") +opening = source.index("\n{\n", start) + 1 +end = source.index("\n}\n", opening) + 3 +function = source[opening:end] +declarations = """{ + uintptr_t ptr; + PyTypeObject *type; + int res; + trace_t *trace; + +""" +assert function.startswith(declarations), ( + "_PyTraceMalloc_NewReference must hoist ptr/type/res/trace into the initial C89 declaration group" +) +assert function[len(declarations):] == """ assert(PyGILState_Check()); + + if (!_Py_tracemalloc_config.tracing) { + /* tracemalloc is not tracing: do nothing */ + return -1; + } + + type = Py_TYPE(op); + if (PyType_IS_GC(type)) { + ptr = (uintptr_t)((char *)op - sizeof(PyGC_Head)); + } + else { + ptr = (uintptr_t)op; + } + + res = -1; + + TABLES_LOCK(); + trace = _Py_hashtable_get(tracemalloc_traces, TO_PTR(ptr)); + if (trace != NULL) { + /* update the traceback of the memory block */ + traceback_t *traceback = traceback_new(); + if (traceback != NULL) { + trace->traceback = traceback; + res = 0; + } + } + /* else: cannot track the object, its memory block size is unknown */ + TABLES_UNLOCK(); + + return res; +} +""", "_PyTraceMalloc_NewReference must preserve assignment points, control flow, and TABLES_LOCK/UNLOCK ordering" +PY_NEWREFERENCE_TEST +then + pass "_PyTraceMalloc_NewReference preserves C89 declarations, assignments, and table locking" +else + bad "_PyTraceMalloc_NewReference preserves C89 declarations, assignments, and table locking" +fi + +# Only the first timemodule clock slice: hoist ticks/ns without moving work. +if python3 - "$REPO_ROOT/cpython/Modules/timemodule.c" <<'PY_TIME_CLOCK_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("_PyTime_GetClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)\n") +opening = source.index("{\n", start) +end = source.index("\n}\n", opening) + 3 +function = source[opening:end] +assert function == """{ + static int initialized = 0; + clock_t ticks; + _PyTime_t ns; + + if (!initialized) { + initialized = 1; + + /* Make sure that _PyTime_MulDiv(ticks, SEC_TO_NS, CLOCKS_PER_SEC) + above cannot overflow */ + if ((_PyTime_t)CLOCKS_PER_SEC > _PyTime_MAX / SEC_TO_NS) { + PyErr_SetString(PyExc_OverflowError, + "CLOCKS_PER_SEC is too large"); + return -1; + } + } + + if (info) { + info->implementation = "clock()"; + info->resolution = 1.0 / (double)CLOCKS_PER_SEC; + info->monotonic = 1; + info->adjustable = 0; + } + + ticks = clock(); + if (ticks == (clock_t)-1) { + PyErr_SetString(PyExc_RuntimeError, + "the processor time used is not available " + "or its value cannot be represented"); + return -1; + } + ns = _PyTime_MulDiv(ticks, SEC_TO_NS, (_PyTime_t)CLOCKS_PER_SEC); + *tp = _PyTime_FromNanoseconds(ns); + return 0; +} +""", "_PyTime_GetClockWithInfo must hoist only ticks/ns and preserve initialization, info updates, clock/error checks, and conversion order" +PY_TIME_CLOCK_TEST +then + pass "timemodule clock ticks/ns use C89 declarations with unchanged execution order" +else + bad "timemodule clock ticks/ns use C89 declarations with unchanged execution order" +fi + +# Only the first timemodule state-declaration slice; compare complete bodies +# to preserve all assignment points, error paths, and platform branches. +if python3 - "$REPO_ROOT/cpython/Modules/timemodule.c" <<'PY_TIME_STATE_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +expected = { + 'time_gmtime': r"""{ + time_t when; + struct tm buf; + time_module_state *state; + + if (!parse_time_t_args(args, "|O:gmtime", &when)) + return NULL; + + errno = 0; + if (_PyTime_gmtime(when, &buf) != 0) + return NULL; + + state = get_time_state(module); +#ifdef HAVE_STRUCT_TM_TM_ZONE + return tmtotuple(state, &buf); +#else + return tmtotuple(state, &buf, "UTC", 0); +#endif +} +""", + 'time_localtime': r"""{ + time_t when; + struct tm buf; + time_module_state *state; + + if (!parse_time_t_args(args, "|O:localtime", &when)) + return NULL; + if (_PyTime_localtime(when, &buf) != 0) + return NULL; + + state = get_time_state(module); +#ifdef HAVE_STRUCT_TM_TM_ZONE + return tmtotuple(state, &buf); +#else + { + struct tm local = buf; + char zone[100]; + time_t gmtoff; + strftime(zone, sizeof(zone), "%Z", &buf); + gmtoff = timegm(&buf) - when; + return tmtotuple(state, &local, zone, gmtoff); + } +#endif +} +""", + 'time_strftime': r"""{ + PyObject *tup = NULL; + struct tm buf; + const time_char *fmt; +#ifdef HAVE_WCSFTIME + wchar_t *format; +#else + PyObject *format; +#endif + PyObject *format_arg; + size_t fmtlen, buflen; + time_char *outbuf = NULL; + size_t i; + PyObject *ret = NULL; + time_module_state *state; + + memset((void *) &buf, '\0', sizeof(buf)); + + /* Will always expect a unicode string to be passed as format. + Given that there's no str type anymore in py3k this seems safe. + */ + if (!PyArg_ParseTuple(args, "U|O:strftime", &format_arg, &tup)) + return NULL; + + state = get_time_state(module); + if (tup == NULL) { + time_t tt = time(NULL); + if (_PyTime_localtime(tt, &buf) != 0) + return NULL; + } + else if (!gettmarg(state, tup, &buf, + "iiiiiiiii;strftime(): illegal time tuple argument") || + !checktm(&buf)) + { + return NULL; + } + +#if defined(_MSC_VER) || (defined(__sun) && defined(__SVR4)) || defined(_AIX) || defined(__VXWORKS__) + if (buf.tm_year + 1900 < 1 || 9999 < buf.tm_year + 1900) { + PyErr_SetString(PyExc_ValueError, + "strftime() requires year in [1; 9999]"); + return NULL; + } +#endif + + /* Normalize tm_isdst just in case someone foolishly implements %Z + based on the assumption that tm_isdst falls within the range of + [-1, 1] */ + if (buf.tm_isdst < -1) + buf.tm_isdst = -1; + else if (buf.tm_isdst > 1) + buf.tm_isdst = 1; + +#ifdef HAVE_WCSFTIME + format = PyUnicode_AsWideCharString(format_arg, NULL); + if (format == NULL) + return NULL; + fmt = format; +#else + /* Convert the unicode string to an ascii one */ + format = PyUnicode_EncodeLocale(format_arg, "surrogateescape"); + if (format == NULL) + return NULL; + fmt = PyBytes_AS_STRING(format); +#endif + +#if defined(MS_WINDOWS) && !defined(HAVE_WCSFTIME) + /* check that the format string contains only valid directives */ + for (outbuf = strchr(fmt, '%'); + outbuf != NULL; + outbuf = strchr(outbuf+2, '%')) + { + if (outbuf[1] == '#') + ++outbuf; /* not documented by python, */ + if (outbuf[1] == '\0') + break; + if ((outbuf[1] == 'y') && buf.tm_year < 0) { + PyErr_SetString(PyExc_ValueError, + "format %y requires year >= 1900 on Windows"); + Py_DECREF(format); + return NULL; + } + } +#elif (defined(_AIX) || (defined(__sun) && defined(__SVR4))) && defined(HAVE_WCSFTIME) + for (outbuf = wcschr(fmt, '%'); + outbuf != NULL; + outbuf = wcschr(outbuf+2, '%')) + { + if (outbuf[1] == L'\0') + break; + /* Issue #19634: On AIX, wcsftime("y", (1899, 1, 1, 0, 0, 0, 0, 0, 0)) + returns "0/" instead of "99" */ + if (outbuf[1] == L'y' && buf.tm_year < 0) { + PyErr_SetString(PyExc_ValueError, + "format %y requires year >= 1900 on AIX"); + PyMem_Free(format); + return NULL; + } + } +#endif + + fmtlen = time_strlen(fmt); + + /* I hate these functions that presume you know how big the output + * will be ahead of time... + */ + for (i = 1024; ; i += i) { + outbuf = (time_char *)PyMem_Malloc(i*sizeof(time_char)); + if (outbuf == NULL) { + PyErr_NoMemory(); + break; + } +#if defined _MSC_VER && _MSC_VER >= 1400 && defined(__STDC_SECURE_LIB__) + errno = 0; +#endif + _Py_BEGIN_SUPPRESS_IPH + buflen = format_time(outbuf, i, fmt, &buf); + _Py_END_SUPPRESS_IPH +#if defined _MSC_VER && _MSC_VER >= 1400 && defined(__STDC_SECURE_LIB__) + /* VisualStudio .NET 2005 does this properly */ + if (buflen == 0 && errno == EINVAL) { + PyErr_SetString(PyExc_ValueError, "Invalid format string"); + PyMem_Free(outbuf); + break; + } +#endif + if (buflen > 0 || i >= 256 * fmtlen) { + /* If the buffer is 256 times as long as the format, + it's probably not failing for lack of room! + More likely, the format yields an empty result, + e.g. an empty format, or %Z when the timezone + is unknown. */ +#ifdef HAVE_WCSFTIME + ret = PyUnicode_FromWideChar(outbuf, buflen); +#else + ret = PyUnicode_DecodeLocaleAndSize(outbuf, buflen, "surrogateescape"); +#endif + PyMem_Free(outbuf); + break; + } + PyMem_Free(outbuf); + } +#ifdef HAVE_WCSFTIME + PyMem_Free(format); +#else + Py_DECREF(format); +#endif + return ret; +} +""", + 'time_asctime': r"""{ + PyObject *tup = NULL; + struct tm buf; + time_module_state *state; + + if (!PyArg_UnpackTuple(args, "asctime", 0, 1, &tup)) + return NULL; + + state = get_time_state(module); + if (tup == NULL) { + time_t tt = time(NULL); + if (_PyTime_localtime(tt, &buf) != 0) + return NULL; + } + else if (!gettmarg(state, tup, &buf, + "iiiiiiiii;asctime(): illegal time tuple argument") || + !checktm(&buf)) + { + return NULL; + } + return _asctime(&buf); +} +""", +} +failed = [] +for name, body in expected.items(): + start = source.index(name + "(PyObject *module, PyObject *args)\n") + opening = source.index("{\n", start) + end = source.index("\n}\n", opening) + 3 + if source[opening:end] != body: + failed.append(name) +assert not failed, ( + "state must use the opening C89 declaration group with the exact body and " + "original get_time_state assignment point preserved: " + ", ".join(failed) +) +PY_TIME_STATE_TEST +then + pass "timemodule four state declarations preserve exact bodies and evaluation order" +else + bad "timemodule four state declarations preserve exact bodies and evaluation order" +fi + +# Windows timezone locals in init_timezone must be in the C89 declaration group. +if python3 - "$REPO_ROOT/cpython/Modules/timemodule.c" <<'PY_TIMEZONE_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.rindex("init_timezone(PyObject *m)") +opening = source.index("{\n", start) +end = source.index("\n}\n", opening) + 3 +function = source[opening:end] +expected_prefix = """{ +#ifdef HAVE_DECL_TZNAME + PyObject *otz0, *otz1; +#ifdef MS_WINDOWS + TIME_ZONE_INFORMATION tzinfo = {0}; +#endif +#endif + + assert(!PyErr_Occurred());""" +if not function.startswith(expected_prefix): + raise SystemExit("init_timezone must hoist tzname/timezone locals before its first statement") +if " PyObject *otz0, *otz1;" in function[function.index("assert(!PyErr_Occurred());"):]: + raise SystemExit("init_timezone must not redeclare otz locals after executable statements") +if " TIME_ZONE_INFORMATION tzinfo = {0};" in function[function.index("assert(!PyErr_Occurred());"):]: + raise SystemExit("init_timezone must not declare tzinfo in the late Windows branch") +if not ( + function.index("tzset();") < + function.index('PyModule_AddIntConstant(m, "daylight", _Py_daylight);') < + function.index("GetTimeZoneInformation(&tzinfo);") < + function.index("otz0 = PyUnicode_FromWideChar(tzinfo.StandardName, -1);") < + function.index("otz1 = PyUnicode_FromWideChar(tzinfo.DaylightName, -1);") +): + raise SystemExit("init_timezone must preserve timezone query and Unicode conversion order") +PY_TIMEZONE_TEST +then + pass "timemodule init_timezone hoists Windows locals and preserves ordering" +else + bad "timemodule init_timezone hoists Windows locals and preserves ordering" +fi + +# The VC6 module definition must avoid C99 designated initializers. +if python3 - "$REPO_ROOT/cpython/Modules/timemodule.c" <<'PY_TIMEMODULE_DEF_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("static struct PyModuleDef timemodule = {") +body = source[start:].split("{", 1)[1].split("};", 1)[0] +fields = [field.strip() for field in body.split(",") if field.strip()] +expected = [ + "PyModuleDef_HEAD_INIT", + '"time"', + "module_doc", + "sizeof(time_module_state)", + "time_methods", + "time_slots", + "time_module_traverse", + "time_module_clear", + "time_module_free", +] +if fields != expected: + raise SystemExit("time module definition must use the CPython field order with positional initializers") +PY_TIMEMODULE_DEF_TEST +then + pass "timemodule definition uses VC6-compatible positional fields" +else + bad "timemodule definition uses VC6-compatible positional fields" +fi + +# Windows pysleep: hoist only declarations, preserving every execution point. +if python3 - "$REPO_ROOT/cpython/Modules/timemodule.c" <<'PY_TIME_SLEEP_TEST' +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +function_start = source.index("pysleep(_PyTime_t timeout)\n{\n") +function_open = source.index("{\n", function_start) +declaration_prefix = """{ +#ifdef MS_WINDOWS + _PyTime_t timeout_100ns; + LARGE_INTEGER relative_timeout; + HANDLE timer; + HANDLE sigint_event; + HANDLE events[2]; + DWORD rc; +#endif + assert(timeout >= 0); + +""" +assert source.startswith(declaration_prefix, function_open), ( + "Windows pysleep locals must precede the first statement at function scope" +) +start = source.index("#else // MS_WINDOWS\n", function_start) + len("#else // MS_WINDOWS\n") +end = source.index("#endif\n}", start) +branch = source[start:end] +assert branch == """ timeout_100ns = _PyTime_As100Nanoseconds(timeout, + _PyTime_ROUND_CEILING); + + // Maintain Windows Sleep() semantics for time.sleep(0) + if (timeout_100ns == 0) { + Py_BEGIN_ALLOW_THREADS + // A value of zero causes the thread to relinquish the remainder of its + // time slice to any other thread that is ready to run. If there are no + // other threads ready to run, the function returns immediately, and + // the thread continues execution. + Sleep(0); + Py_END_ALLOW_THREADS + return 0; + } + + // No need to check for integer overflow, both types are signed + assert(sizeof(relative_timeout) == sizeof(timeout_100ns)); + // SetWaitableTimer(): a negative due time indicates relative time + relative_timeout.QuadPart = -timeout_100ns; + + timer = CreateWaitableTimerExW(NULL, NULL, timer_flags, + TIMER_ALL_ACCESS); + if (timer == NULL) { + PyErr_SetFromWindowsErr(0); + return -1; + } + + if (!SetWaitableTimerEx(timer, &relative_timeout, + 0, // no period; the timer is signaled once + NULL, NULL, // no completion routine + NULL, // no wake context; do not resume from suspend + 0)) // no tolerable delay for timer coalescing + { + PyErr_SetFromWindowsErr(0); + goto error; + } + + // Only the main thread can be interrupted by SIGINT. + // Signal handlers are only executed in the main thread. + if (_PyOS_IsMainThread()) { + sigint_event = _PyOS_SigintEvent(); + + while (1) { + // Check for pending SIGINT signal before resetting the event + if (PyErr_CheckSignals()) { + goto error; + } + ResetEvent(sigint_event); + + events[0] = timer; + events[1] = sigint_event; + + Py_BEGIN_ALLOW_THREADS + rc = WaitForMultipleObjects(Py_ARRAY_LENGTH(events), events, + // bWaitAll + FALSE, + // No wait timeout + INFINITE); + Py_END_ALLOW_THREADS + + if (rc == WAIT_FAILED) { + PyErr_SetFromWindowsErr(0); + goto error; + } + + if (rc == WAIT_OBJECT_0) { + // Timer signaled: we are done + break; + } + + assert(rc == (WAIT_OBJECT_0 + 1)); + // The sleep was interrupted by SIGINT: restart sleeping + } + } + else { + Py_BEGIN_ALLOW_THREADS + rc = WaitForSingleObject(timer, INFINITE); + Py_END_ALLOW_THREADS + + if (rc == WAIT_FAILED) { + PyErr_SetFromWindowsErr(0); + goto error; + } + + assert(rc == WAIT_OBJECT_0); + // Timer signaled: we are done + } + + CloseHandle(timer); + return 0; + +error: + CloseHandle(timer); + return -1; +""", "Windows pysleep must preserve assignment points, wait branches, signal handling, and cleanup with one shared rc declaration" +PY_TIME_SLEEP_TEST +then + pass "timemodule Windows pysleep preserves C89 declarations and exact execution order" +else + bad "timemodule Windows pysleep preserves C89 declarations and exact execution order" +fi + +# _copy_raw_string keeps its early error return before copying, but declares locals in C89 order. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_COPY_RAW_STRING_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.rindex("_copy_raw_string(PyObject *strobj)") +opening = source.index("{\n", start) +end = source.index("\n}\n", opening) + 2 +function = source[opening:end] +expected = """{ + const char *str = PyUnicode_AsUTF8(strobj); + char *copied; + if (str == NULL) { + return NULL; + } + copied = PyMem_Malloc(strlen(str)+1); + if (copied == NULL) { + PyErr_NoMemory(); + return NULL; + } + strcpy(copied, str); + return copied; +}""" +if function != expected: + raise SystemExit("_copy_raw_string must hoist copied and preserve its null/error/copy order") +PY_COPY_RAW_STRING_TEST +then + pass "_copy_raw_string preserves error and copy order with C89 declarations" +else + bad "_copy_raw_string preserves error and copy order with C89 declarations" +fi + +# _sharednsitem_apply hoists all locals and preserves allocation/error order. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_SHAREDNSITEM_APPLY_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_sharednsitem_apply(struct _sharednsitem *item, PyObject *ns)") +opening = source.index("{\n", start) +end = source.index("\n}\n", opening) + 2 +function = source[opening:end] +expected = """{ + PyObject *name; + PyObject *value; + int res; + + name = PyUnicode_FromString(item->name); + if (name == NULL) { + return -1; + } + value = _PyCrossInterpreterData_NewObject(&item->data); + if (value == NULL) { + Py_DECREF(name); + return -1; + } + res = PyDict_SetItem(ns, name, value); + Py_DECREF(name); + Py_DECREF(value); + return res; +}""" +if function != expected: + raise SystemExit("_sharednsitem_apply must hoist locals and preserve error/cleanup order") +PY_SHAREDNSITEM_APPLY_TEST +then + pass "_sharednsitem_apply preserves ordering with C89 declarations" +else + bad "_sharednsitem_apply preserves ordering with C89 declarations" +fi + +# _sharedns_free uses a C89-compatible loop declaration without changing cleanup order. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_SHAREDNS_FREE_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_sharedns_free(_sharedns *shared)") +opening = source.index("{\n", start) +end = source.index("\n}\n", opening) + 2 +function = source[opening:end] +expected = """{ + Py_ssize_t i; + for (i=0; i < shared->len; i++) { + _sharednsitem_clear(&shared->items[i]); + } + PyMem_Free(shared->items); + PyMem_Free(shared); +}""" +if function != expected: + raise SystemExit("_sharedns_free must hoist loop index and retain cleanup order") +PY_SHAREDNS_FREE_TEST +then + pass "_sharedns_free uses C89 loop declarations and preserves cleanup order" +else + bad "_sharedns_free uses C89 loop declarations and preserves cleanup order" +fi + +# _get_shared_ns declares locals before statements and preserves dictionary iteration/error behavior. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_GET_SHARED_NS_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_get_shared_ns(PyObject *shareable)") +opening = source.index("{\n", start) +end = source.index("\n}\n", opening) + 2 +function = source[opening:end] +expected = """{ + Py_ssize_t len; + _sharedns *shared; + Py_ssize_t pos; + Py_ssize_t i; + + if (shareable == NULL || shareable == Py_None) { + return NULL; + } + len = PyDict_Size(shareable); + if (len == 0) { + return NULL; + } + + shared = _sharedns_new(len); + if (shared == NULL) { + return NULL; + } + pos = 0; + for (i=0; i < len; i++) { + PyObject *key, *value; + if (PyDict_Next(shareable, &pos, &key, &value) == 0) { + break; + } + if (_sharednsitem_init(&shared->items[i], key, value) != 0) { + break; + } + } + if (PyErr_Occurred()) { + _sharedns_free(shared); + return NULL; + } + return shared; +}""" +if function != expected: + raise SystemExit("_get_shared_ns must preserve null/error/iteration order with C89 declarations") +PY_GET_SHARED_NS_TEST +then + pass "_get_shared_ns preserves behavior with C89 declarations" +else + bad "_get_shared_ns preserves behavior with C89 declarations" +fi + +# _sharedns_apply moves its loop index out of the C99 for declaration. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_SHAREDNS_APPLY_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_sharedns_apply(_sharedns *shared, PyObject *ns)") +opening = source.index("{\n", start) +end = source.index("\n}\n", opening) + 2 +function = source[opening:end] +expected = """{ + Py_ssize_t i; + for (i=0; i < shared->len; i++) { + if (_sharednsitem_apply(&shared->items[i], ns) != 0) { + return -1; + } + } + return 0; +}""" +if function != expected: + raise SystemExit("_sharedns_apply must hoist its loop index and preserve iteration order") +PY_SHAREDNS_APPLY_TEST +then + pass "_sharedns_apply uses C89 loop declarations" +else + bad "_sharedns_apply uses C89 loop declarations" +fi + +# _sharedexception_bind hoists only outer locals and keeps all error/cleanup points ordered. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_SHAREDEXCEPTION_BIND_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_sharedexception_bind(PyObject *exctype, PyObject *exc, PyObject *tb)") +opening = source.index("{\n", start) +end = source.index("\n}\n", opening) + 2 +function = source[opening:end] +expected = """{ + char *failure; + _sharedexception *err; + PyObject *name; + assert(exctype != NULL); + failure = NULL; + + err = _sharedexception_new(); + if (err == NULL) { + goto finally; + } + + name = PyUnicode_FromFormat("%S", exctype); + if (name == NULL) { + failure = "unable to format exception type name"; + goto finally; + } + err->name = _copy_raw_string(name); + Py_DECREF(name); + if (err->name == NULL) { + if (PyErr_ExceptionMatches(PyExc_MemoryError)) { + failure = "out of memory copying exception type name"; + } else { + failure = "unable to encode and copy exception type name"; + } + goto finally; + } + + if (exc != NULL) { + PyObject *msg = PyUnicode_FromFormat("%S", exc); + if (msg == NULL) { + failure = "unable to format exception message"; + goto finally; + } + err->msg = _copy_raw_string(msg); + Py_DECREF(msg); + if (err->msg == NULL) { + if (PyErr_ExceptionMatches(PyExc_MemoryError)) { + failure = "out of memory copying exception message"; + } else { + failure = "unable to encode and copy exception message"; + } + goto finally; + } + } + +finally: + if (failure != NULL) { + PyErr_Clear(); + if (err->name != NULL) { + PyMem_Free(err->name); + err->name = NULL; + } + err->msg = failure; + } + return err; +}""" +if function != expected: + raise SystemExit("_sharedexception_bind must preserve failure/cleanup order with C89 declarations") +PY_SHAREDEXCEPTION_BIND_TEST +then + pass "_sharedexception_bind preserves error flow with C89 declarations" +else + bad "_sharedexception_bind preserves error flow with C89 declarations" +fi + +# _channel_close_interpreter keeps lock acquisition before result initialization. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_CHANNEL_CLOSE_INTERPRETER_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_channel_close_interpreter(_PyChannelState *chan, int64_t interp, int end)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +function = source[opening:end] +expected = """{ + int res; + PyThread_acquire_lock(chan->mutex, WAIT_LOCK); + + res = -1; + if (!chan->open) { + PyErr_SetString(ChannelClosedError, "channel already closed"); + goto done; + } + + if (_channelends_close_interpreter(chan->ends, interp, end) != 0) { + goto done; + } + chan->open = _channelends_is_open(chan->ends); + + res = 0; +done: + PyThread_release_lock(chan->mutex); + return res; +}""" +if function != expected: + raise SystemExit("_channel_close_interpreter must preserve lock/result/error order with C89 declarations") +PY_CHANNEL_CLOSE_INTERPRETER_TEST +then + pass "_channel_close_interpreter preserves lock and error order" +else + bad "_channel_close_interpreter preserves lock and error order" +fi + +# Channel lookup/add helpers hoist locals without changing lock or list operations. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_CHANNELS_HELPERS_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +checks = { + "_channels_lookup(_channels *channels, int64_t id, PyThread_type_lock *pmutex)": """{ + _PyChannelState *chan; + _channelref *ref; + chan = NULL; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + if (pmutex != NULL) { + *pmutex = NULL; + } + + ref = _channelref_find(channels->head, id, NULL); + if (ref == NULL) { + PyErr_Format(ChannelNotFoundError, "channel %" PRId64 " not found", id); + goto done; + } + if (ref->chan == NULL || !ref->chan->open) { + PyErr_Format(ChannelClosedError, "channel %" PRId64 " closed", id); + goto done; + } + + if (pmutex != NULL) { + // The mutex will be closed by the caller. + *pmutex = channels->mutex; + } + + chan = ref->chan; +done: + if (pmutex == NULL || *pmutex == NULL) { + PyThread_release_lock(channels->mutex); + } + return chan; +}""", + "_channels_add(_channels *channels, _PyChannelState *chan)": """{ + int64_t cid; + int64_t id; + _channelref *ref; + cid = -1; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + + // Create a new ref. + id = _channels_next_id(channels); + if (id < 0) { + goto done; + } + ref = _channelref_new(id, chan); + if (ref == NULL) { + goto done; + } + + // Add it to the list. + // We assume that the channel is a new one (not already in the list). + ref->next = channels->head; + channels->head = ref; + channels->numopen += 1; + + cid = id; +done: + PyThread_release_lock(channels->mutex); + return cid; +}""", +} +for signature, expected in checks.items(): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + if source[opening:end] != expected: + raise SystemExit("channel helper must hoist locals and preserve lock/error/list operation order: " + signature) +PY_CHANNELS_HELPERS_TEST +then + pass "_channels_lookup/_channels_add preserve lock order with C89 declarations" +else + bad "_channels_lookup/_channels_add preserve lock order with C89 declarations" +fi + +# _channels_close declares ref before lock/error statements and keeps lookup after pchan initialization. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_CHANNELS_CLOSE_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_channels_close(_channels *channels, int64_t cid, _PyChannelState **pchan,") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +function = source[opening:end] +expected_prefix = """{ + int res = -1; + _channelref *ref; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + if (pchan != NULL) { + *pchan = NULL; + } + + ref = _channelref_find(channels->head, cid, NULL); +""" +if not function.startswith(expected_prefix): + raise SystemExit("_channels_close must hoist ref while preserving lock and lookup order") +PY_CHANNELS_CLOSE_TEST +then + pass "_channels_close hoists ref and preserves lock/lookup order" +else + bad "_channels_close hoists ref and preserves lock/lookup order" +fi + +# Channel reference operations hoist locals while retaining locking and cleanup order. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_CHANNELS_REF_OPS_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +checks = { + "_channels_remove(_channels *channels, int64_t id, _PyChannelState **pchan)": """{ + int res = -1; + _channelref *prev; + _channelref *ref; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + + if (pchan != NULL) { + *pchan = NULL; + } + + prev = NULL; + ref = _channelref_find(channels->head, id, &prev); + if (ref == NULL) { + PyErr_Format(ChannelNotFoundError, "channel %" PRId64 " not found", id); + goto done; + } + + _channels_remove_ref(channels, ref, prev, pchan); + + res = 0; +done: + PyThread_release_lock(channels->mutex); + return res; +}""", + "_channels_add_id_object(_channels *channels, int64_t id)": """{ + int res = -1; + _channelref *ref; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + + ref = _channelref_find(channels->head, id, NULL); + if (ref == NULL) { + PyErr_Format(ChannelNotFoundError, "channel %" PRId64 " not found", id); + goto done; + } + ref->objcount += 1; + + res = 0; +done: + PyThread_release_lock(channels->mutex); + return res; +}""", + "_channels_drop_id_object(_channels *channels, int64_t id)": """{ + _channelref *prev; + _channelref *ref; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + + prev = NULL; + ref = _channelref_find(channels->head, id, &prev); + if (ref == NULL) { + // Already destroyed. + goto done; + } + ref->objcount -= 1; + + // Destroy if no longer used. + if (ref->objcount == 0) { + _PyChannelState *chan = NULL; + _channels_remove_ref(channels, ref, prev, &chan); + if (chan != NULL) { + _channel_free(chan); + } + } + +done: + PyThread_release_lock(channels->mutex); +}""", + "_channels_list_all(_channels *channels, int64_t *count)": """{ + int64_t *cids = NULL; + int64_t *ids; + _channelref *ref; + int64_t i; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + ids = PyMem_NEW(int64_t, (Py_ssize_t)(channels->numopen)); + if (ids == NULL) { + goto done; + } + ref = channels->head; + for (i=0; ref != NULL; ref = ref->next, i++) { + ids[i] = ref->id; + } + *count = channels->numopen; + + cids = ids; +done: + PyThread_release_lock(channels->mutex); + return cids; +}""", +} +for signature, expected in checks.items(): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + if source[opening:end] != expected: + raise SystemExit("channel reference helper must preserve locks, result and cleanup order: " + signature) +PY_CHANNELS_REF_OPS_TEST +then + pass "channel reference helpers use C89 declarations and preserve order" +else + bad "channel reference helpers use C89 declarations and preserve order" +fi + +# Channel closing/creation helpers hoist locals and keep assignments after their guards. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_CHANNEL_CLOSING_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +checks = { + "_channel_set_closing(struct _channelref *ref, PyThread_type_lock mutex)": """{ + struct _channel *chan; + int res; + chan = ref->chan; + if (chan == NULL) { + // already closed + return 0; + } + res = -1; + PyThread_acquire_lock(chan->mutex, WAIT_LOCK); + if (chan->closing != NULL) { + PyErr_SetString(ChannelClosedError, "channel closed"); + goto done; + } + chan->closing = PyMem_NEW(struct _channel_closing, 1); + if (chan->closing == NULL) { + goto done; + } + chan->closing->ref = ref; + + res = 0; +done: + PyThread_release_lock(chan->mutex); + return res; +}""", + "_channel_finish_closing(struct _channel *chan)": """{ + struct _channel_closing *closing; + _channelref *ref; + closing = chan->closing; + if (closing == NULL) { + return; + } + ref = closing->ref; + _channel_clear_closing(chan); + // Do the things that would have been done in _channels_close(). + ref->chan = NULL; + _channel_free(chan); +}""", + "_channel_create(_channels *channels)": """{ + _PyChannelState *chan; + int64_t id; + chan = _channel_new(); + if (chan == NULL) { + return -1; + } + id = _channels_add(channels, chan); + if (id < 0) { + _channel_free(chan); + return -1; + } + return id; +}""", +} +for signature, expected in checks.items(): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + if source[opening:end] != expected: + raise SystemExit("channel closing helper must preserve error/assignment/cleanup order: " + signature) +PY_CHANNEL_CLOSING_TEST +then + pass "channel closing/creation helpers use C89 declaration order" +else + bad "channel closing/creation helpers use C89 declaration order" +fi + +# _channel_send preserves acquired-lock and cross-interpreter cleanup order with C89 locals. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_CHANNEL_SEND_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_channel_send(_channels *channels, int64_t id, PyObject *obj)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +function = source[opening:end] +expected = """{ + PyInterpreterState *interp; + PyThread_type_lock mutex; + _PyChannelState *chan; + _PyCrossInterpreterData *data; + int res; + interp = _get_current(); + if (interp == NULL) { + return -1; + } + + // Look up the channel. + mutex = NULL; + chan = _channels_lookup(channels, id, &mutex); + if (chan == NULL) { + return -1; + } + // Past this point we are responsible for releasing the mutex. + + if (chan->closing != NULL) { + PyErr_Format(ChannelClosedError, "channel %" PRId64 " closed", id); + PyThread_release_lock(mutex); + return -1; + } + + // Convert the object to cross-interpreter data. + data = PyMem_NEW(_PyCrossInterpreterData, 1); + if (data == NULL) { + PyThread_release_lock(mutex); + return -1; + } + if (_PyObject_GetCrossInterpreterData(obj, data) != 0) { + PyThread_release_lock(mutex); + PyMem_Free(data); + return -1; + } + + // Add the data to the channel. + res = _channel_add(chan, PyInterpreterState_GetID(interp), data); + PyThread_release_lock(mutex); + if (res != 0) { + _PyCrossInterpreterData_Release(data); + PyMem_Free(data); + return -1; + } + + return 0; +}""" +if function != expected: + raise SystemExit("_channel_send must preserve lock/data/error order with C89 declarations") +PY_CHANNEL_SEND_TEST +then + pass "_channel_send preserves lock/data cleanup order" +else + bad "_channel_send preserves lock/data cleanup order" +fi + +# _channel_recv hoists locals while preserving lock, null, cleanup and result order. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_CHANNEL_RECV_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_channel_recv(_channels *channels, int64_t id)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +function = source[opening:end] +expected = """{ + PyInterpreterState *interp; + PyThread_type_lock mutex; + _PyChannelState *chan; + _PyCrossInterpreterData *data; + PyObject *obj; + interp = _get_current(); + if (interp == NULL) { + return NULL; + } + + // Look up the channel. + mutex = NULL; + chan = _channels_lookup(channels, id, &mutex); + if (chan == NULL) { + return NULL; + } + // Past this point we are responsible for releasing the mutex. + + // Pop off the next item from the channel. + data = _channel_next(chan, PyInterpreterState_GetID(interp)); + PyThread_release_lock(mutex); + if (data == NULL) { + return NULL; + } + + // Convert the data back to an object. + obj = _PyCrossInterpreterData_NewObject(data); + _PyCrossInterpreterData_Release(data); + PyMem_Free(data); + if (obj == NULL) { + return NULL; + } + + return obj; +}""" +if function != expected: + raise SystemExit("_channel_recv must preserve lock/null/cleanup/result order with C89 declarations") +PY_CHANNEL_RECV_TEST +then + pass "_channel_recv preserves lock/null/cleanup/result order with C89 declarations" +else + bad "_channel_recv preserves lock/null/cleanup/result order with C89 declarations" +fi + +# _channel_recv/drop/association hoist declarations without changing resource cleanup. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_CHANNEL_RECV_DROP_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +checks = { + "_channel_recv(_channels *channels, int64_t id)": """{ + PyInterpreterState *interp; + PyThread_type_lock mutex; + _PyChannelState *chan; + _PyCrossInterpreterData *data; + PyObject *obj; + interp = _get_current(); + if (interp == NULL) { + return NULL; + } + + // Look up the channel. + mutex = NULL; + chan = _channels_lookup(channels, id, &mutex); + if (chan == NULL) { + return NULL; + } + // Past this point we are responsible for releasing the mutex. + + // Pop off the next item from the channel. + data = _channel_next(chan, PyInterpreterState_GetID(interp)); + PyThread_release_lock(mutex); + if (data == NULL) { + return NULL; + } + + // Convert the data back to an object. + obj = _PyCrossInterpreterData_NewObject(data); + _PyCrossInterpreterData_Release(data); + PyMem_Free(data); + if (obj == NULL) { + return NULL; + } + + return obj; +}""", + "_channel_drop(_channels *channels, int64_t id, int send, int recv)": """{ + PyInterpreterState *interp; + PyThread_type_lock mutex; + _PyChannelState *chan; + int res; + interp = _get_current(); + if (interp == NULL) { + return -1; + } + + // Look up the channel. + mutex = NULL; + chan = _channels_lookup(channels, id, &mutex); + if (chan == NULL) { + return -1; + } + // Past this point we are responsible for releasing the mutex. + + // Close one or both of the two ends. + res = _channel_close_interpreter(chan, PyInterpreterState_GetID(interp), send-recv); + PyThread_release_lock(mutex); + return res; +}""", + "_channel_is_associated(_channels *channels, int64_t cid, int64_t interp,": """{ + _PyChannelState *chan; + _channelend *end; + chan = _channels_lookup(channels, cid, NULL); + if (chan == NULL) { + return -1; + } else if (send && chan->closing != NULL) { + PyErr_Format(ChannelClosedError, "channel %" PRId64 " closed", cid); + return -1; + } + + end = _channelend_find(send ? chan->ends->send : chan->ends->recv, + interp, NULL); + + return (end != NULL && end->open); +}""", +} +for signature, expected in checks.items(): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + if source[opening:end] != expected: + raise SystemExit("channel recv/drop/association helper changed declaration or cleanup order: " + signature) +PY_CHANNEL_RECV_DROP_TEST +then + pass "_channel_recv/drop/association preserve cleanup order with C89 declarations" +else + bad "_channel_recv/drop/association preserve cleanup order with C89 declarations" +fi + +# channelid_new declares end at function entry and assigns it after argument validation. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_CHANNELID_NEW_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("channelid_new(PyTypeObject *cls, PyObject *args, PyObject *kwds)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +expected = """{ + static char *kwlist[] = {"id", "send", "recv", "force", "_resolve", NULL}; + int64_t cid; + int send = -1; + int recv = -1; + int force = 0; + int resolve = 0; + int end; + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "O&|$pppp:ChannelID.__new__", kwlist, + channel_id_converter, &cid, &send, &recv, &force, &resolve)) + return NULL; + + // Handle "send" and "recv". + if (send == 0 && recv == 0) { + PyErr_SetString(PyExc_ValueError, + "'send' and 'recv' cannot both be False"); + return NULL; + } + + end = 0; + if (send == 1) { + if (recv == 0 || recv == -1) { + end = CHANNEL_SEND; + } + } + else if (recv == 1) { + end = CHANNEL_RECV; + } + + return (PyObject *)newchannelid(cls, cid, end, _global_channels(), + force, resolve); +}""" +if source[opening:end] != expected: + raise SystemExit("channelid_new must preserve parse/validation/endpoint selection order") +PY_CHANNELID_NEW_TEST +then + pass "channelid_new preserves parse and endpoint selection order with C89 declarations" +else + bad "channelid_new preserves parse and endpoint selection order with C89 declarations" +fi + +# ChannelID hash/comparison use C89 declarations and VC6's 64-bit type spelling. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_CHANNELID_COMPARE_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +checks = { + "channelid_hash(PyObject *self)": """{ + channelid *cid = (channelid *)self; + PyObject *id = PyLong_FromLongLong(cid->id); + Py_hash_t hash; + if (id == NULL) { + return -1; + } + hash = PyObject_Hash(id); + Py_DECREF(id); + return hash; +}""", + "channelid_richcompare(PyObject *self, PyObject *other, int op)": """{ + channelid *cid; + int equal; + if (op != Py_EQ && op != Py_NE) { + Py_RETURN_NOTIMPLEMENTED; + } + + if (!PyObject_TypeCheck(self, &ChannelIDtype)) { + Py_RETURN_NOTIMPLEMENTED; + } + + cid = (channelid *)self; + if (PyObject_TypeCheck(other, &ChannelIDtype)) { + channelid *othercid = (channelid *)other; + equal = (cid->end == othercid->end) && (cid->id == othercid->id); + } + else if (PyLong_Check(other)) { + int overflow; + PY_LONG_LONG othercid; + /* Fast path */ + othercid = PyLong_AsLongLongAndOverflow(other, &overflow); + if (othercid == -1 && PyErr_Occurred()) { + return NULL; + } + equal = !overflow && (othercid >= 0) && (cid->id == othercid); + } + else if (PyNumber_Check(other)) { + PyObject *pyid; + PyObject *res; + pyid = PyLong_FromLongLong(cid->id); + if (pyid == NULL) { + return NULL; + } + res = PyObject_RichCompare(pyid, other, op); + Py_DECREF(pyid); + return res; + } + else { + Py_RETURN_NOTIMPLEMENTED; + } + + if ((op == Py_EQ && equal) || (op == Py_NE && !equal)) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +}""", +} +for signature, expected in checks.items(): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + if source[opening:end] != expected: + raise SystemExit("ChannelID comparison helper changed error/evaluation order or VC6 type spelling: " + signature) +PY_CHANNELID_COMPARE_TEST +then + pass "ChannelID hash/comparison preserve behavior with VC6-compatible declarations" +else + bad "ChannelID hash/comparison preserve behavior with VC6-compatible declarations" +fi + +# ChannelID reconstruction helpers hoist locals while keeping import/refcount timing. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_CHANNELID_RECONSTRUCT_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +checks = { + "_channel_from_cid(PyObject *cid, int end)": """{ + PyObject *highlevel = PyImport_ImportModule("interpreters"); + const char *clsname; + PyObject *cls; + PyObject *chan; + if (highlevel == NULL) { + PyErr_Clear(); + highlevel = PyImport_ImportModule("test.support.interpreters"); + if (highlevel == NULL) { + return NULL; + } + } + clsname = (end == CHANNEL_RECV) ? "RecvChannel" : + "SendChannel"; + cls = PyObject_GetAttrString(highlevel, clsname); + Py_DECREF(highlevel); + if (cls == NULL) { + return NULL; + } + chan = PyObject_CallFunctionObjArgs(cls, cid, NULL); + Py_DECREF(cls); + if (chan == NULL) { + return NULL; + } + return chan; +}""", + "_channelid_from_xid(_PyCrossInterpreterData *data)": """{ + struct _channelid_xid *xid = (struct _channelid_xid *)data->data; + PyObject *cid; + PyObject *chan; + // Note that we do not preserve the "resolve" flag. + cid = (PyObject *)newchannelid(&ChannelIDtype, xid->id, xid->end, + _global_channels(), 0, 0); + if (xid->end == 0) { + return cid; + } + if (!xid->resolve) { + return cid; + } + + /* Try returning a high-level channel end but fall back to the ID. */ + chan = _channel_from_cid(cid, xid->end); + if (chan == NULL) { + PyErr_Clear(); + return cid; + } + Py_DECREF(cid); + return chan; +}""", +} +for signature, expected in checks.items(): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + if source[opening:end] != expected: + raise SystemExit("ChannelID reconstruction must preserve import/refcount/resolve behavior: " + signature) +PY_CHANNELID_RECONSTRUCT_TEST +then + pass "ChannelID reconstruction preserves import/refcount behavior with C89 declarations" +else + bad "ChannelID reconstruction preserves import/refcount behavior with C89 declarations" +fi + +# _is_running fetches current_frame only after its assertions while declaring locals in C89 order. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_IS_RUNNING_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_is_running(PyInterpreterState *interp)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +expected = """{ + PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); + _PyInterpreterFrame *frame; + if (PyThreadState_Next(tstate) != NULL) { + PyErr_SetString(PyExc_RuntimeError, + "interpreter has more than one thread"); + return -1; + } + + assert(!PyErr_Occurred()); + frame = tstate->cframe->current_frame; + if (frame == NULL) { + return 0; + } + return 1; +}""" +if source[opening:end] != expected: + raise SystemExit("_is_running must retain thread check, assert, and frame lookup order") +PY_IS_RUNNING_TEST +then + pass "_is_running preserves validation order with C89 declarations" +else + bad "_is_running preserves validation order with C89 declarations" +fi + +# _run_script hoists locals across labels while keeping all initialization at original control-flow points. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_RUN_SCRIPT_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_run_script(PyInterpreterState *interp, const char *codestr,") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +expected = """{ + PyObject *exctype = NULL; + PyObject *excval = NULL; + PyObject *tb = NULL; + PyObject *ns; + PyObject *result; + _sharedexception *sharedexc; + + PyObject *main_mod = _PyInterpreterState_GetMainModule(interp); + if (main_mod == NULL) { + goto error; + } + ns = PyModule_GetDict(main_mod); // borrowed + Py_DECREF(main_mod); + if (ns == NULL) { + goto error; + } + Py_INCREF(ns); + + // Apply the cross-interpreter data. + if (shared != NULL) { + if (_sharedns_apply(shared, ns) != 0) { + Py_DECREF(ns); + goto error; + } + } + + // Run the string (see PyRun_SimpleStringFlags). + result = PyRun_StringFlags(codestr, Py_file_input, ns, ns, NULL); + Py_DECREF(ns); + if (result == NULL) { + goto error; + } + else { + Py_DECREF(result); // We throw away the result. + } + + *exc = NULL; + return 0; + +error: + PyErr_Fetch(&exctype, &excval, &tb); + + sharedexc = _sharedexception_bind(exctype, excval, tb); + Py_XDECREF(exctype); + Py_XDECREF(excval); + Py_XDECREF(tb); + if (sharedexc == NULL) { + fprintf(stderr, "RunFailedError: script raised an uncaught exception"); + PyErr_Clear(); + sharedexc = NULL; + } + else { + assert(!PyErr_Occurred()); + } + *exc = sharedexc; + return -1; +}""" +if source[opening:end] != expected: + raise SystemExit("_run_script must preserve error-label, namespace, and result handling order") +PY_RUN_SCRIPT_TEST +then + pass "_run_script preserves error handling with C89 declarations" +else + bad "_run_script preserves error handling with C89 declarations" +fi + +# _run_script_in_interpreter keeps interpreter switching/exception propagation order with C89 locals. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_RUN_SCRIPT_INTERP_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_run_script_in_interpreter(PyInterpreterState *interp, const char *codestr,") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +expected = """{ + _sharedns *shared; + PyThreadState *save_tstate; + PyThreadState *tstate; + _sharedexception *exc; + int result; + if (_ensure_not_running(interp) < 0) { + return -1; + } + + shared = _get_shared_ns(shareables); + if (shared == NULL && PyErr_Occurred()) { + return -1; + } + + // Switch to interpreter. + save_tstate = NULL; + if (interp != PyInterpreterState_Get()) { + // XXX Using the "head" thread isn't strictly correct. + tstate = PyInterpreterState_ThreadHead(interp); + // XXX Possible GILState issues? + save_tstate = PyThreadState_Swap(tstate); + } + + // Run the script. + exc = NULL; + result = _run_script(interp, codestr, shared, &exc); + + // Switch back. + if (save_tstate != NULL) { + PyThreadState_Swap(save_tstate); + } + + // Propagate any exception out to the caller. + if (exc != NULL) { + _sharedexception_apply(exc, RunFailedError); + _sharedexception_free(exc); + } + else if (result != 0) { + // We were unable to allocate a shared exception. + PyErr_NoMemory(); + } + + if (shared != NULL) { + _sharedns_free(shared); + } + + return result; +}""" +if source[opening:end] != expected: + raise SystemExit("_run_script_in_interpreter must preserve switch/exception/shared-state order") +PY_RUN_SCRIPT_INTERP_TEST +then + pass "_run_script_in_interpreter preserves behavior with C89 declarations" +else + bad "_run_script_in_interpreter preserves behavior with C89 declarations" +fi + +# Interpreter lifecycle entry points hoist locals without changing thread-state swap/cleanup order. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_INTERP_LIFECYCLE_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +checks = { + "interp_create(PyObject *self, PyObject *args, PyObject *kwds)": """{ + + static char *kwlist[] = {"isolated", NULL}; + int isolated = 1; + PyThreadState *save_tstate; + PyThreadState *tstate; + PyInterpreterState *interp; + PyObject *idobj; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|$i:create", kwlist, + &isolated)) { + return NULL; + } + + // Create and initialize the new interpreter. + save_tstate = _PyThreadState_GET(); + // XXX Possible GILState issues? + tstate = _Py_NewInterpreter(isolated); + PyThreadState_Swap(save_tstate); + if (tstate == NULL) { + /* Since no new thread state was created, there is no exception to + propagate; raise a fresh one after swapping in the old thread + state. */ + PyErr_SetString(PyExc_RuntimeError, "interpreter creation failed"); + return NULL; + } + interp = PyThreadState_GetInterpreter(tstate); + idobj = _PyInterpreterState_GetIDObject(interp); + if (idobj == NULL) { + // XXX Possible GILState issues? + save_tstate = PyThreadState_Swap(tstate); + Py_EndInterpreter(tstate); + PyThreadState_Swap(save_tstate); + return NULL; + } + _PyInterpreterState_RequireIDRef(interp, 1); + return idobj; +}""", + "interp_destroy(PyObject *self, PyObject *args, PyObject *kwds)": """{ + static char *kwlist[] = {"id", NULL}; + PyObject *id; + PyInterpreterState *interp; + PyInterpreterState *current; + PyThreadState *tstate; + PyThreadState *save_tstate; + // XXX Use "L" for id? + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "O:destroy", kwlist, &id)) { + return NULL; + } + + // Look up the interpreter. + interp = _PyInterpreterID_LookUp(id); + if (interp == NULL) { + return NULL; + } + + // Ensure we don't try to destroy the current interpreter. + current = _get_current(); + if (current == NULL) { + return NULL; + } + if (interp == current) { + PyErr_SetString(PyExc_RuntimeError, + "cannot destroy the current interpreter"); + return NULL; + } + + // Ensure the interpreter isn't running. + /* XXX We *could* support destroying a running interpreter but + aren't going to worry about it for now. */ + if (_ensure_not_running(interp) < 0) { + return NULL; + } + + // Destroy the interpreter. + tstate = PyInterpreterState_ThreadHead(interp); + // XXX Possible GILState issues? + save_tstate = PyThreadState_Swap(tstate); + Py_EndInterpreter(tstate); + PyThreadState_Swap(save_tstate); + + Py_RETURN_NONE; +}""", + "interp_list_all(PyObject *self, PyObject *Py_UNUSED(ignored))": """{ + PyObject *ids, *id; + PyInterpreterState *interp; + int res; + + ids = PyList_New(0); + if (ids == NULL) { + return NULL; + } + + interp = PyInterpreterState_Head(); + while (interp != NULL) { + id = _PyInterpreterState_GetIDObject(interp); + if (id == NULL) { + Py_DECREF(ids); + return NULL; + } + // insert at front of list + res = PyList_Insert(ids, 0, id); + Py_DECREF(id); + if (res < 0) { + Py_DECREF(ids); + return NULL; + } + + interp = PyInterpreterState_Next(interp); + } + + return ids; +}""", +} +for signature, expected in checks.items(): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + if source[opening:end] != expected: + raise SystemExit("interpreter lifecycle helper changed thread-state/list cleanup order: " + signature) +PY_INTERP_LIFECYCLE_TEST +then + pass "interpreter lifecycle entry points preserve swap/order with C89 declarations" +else + bad "interpreter lifecycle entry points preserve swap/order with C89 declarations" +fi + +# interp_run_string keeps parse, lookup, and UTF-8 extraction order with C89 declarations. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_INTERP_RUN_STRING_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("interp_run_string(PyObject *self, PyObject *args, PyObject *kwds)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +expected = """{ + static char *kwlist[] = {"id", "script", "shared", NULL}; + PyObject *id, *code; + PyObject *shared = NULL; + PyInterpreterState *interp; + Py_ssize_t size; + const char *codestr; + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "OU|O:run_string", kwlist, + &id, &code, &shared)) { + return NULL; + } + + // Look up the interpreter. + interp = _PyInterpreterID_LookUp(id); + if (interp == NULL) { + return NULL; + } + + // Extract code. + codestr = PyUnicode_AsUTF8AndSize(code, &size); + if (codestr == NULL) { + return NULL; + } + if (strlen(codestr) != (size_t)size) { + PyErr_SetString(PyExc_ValueError, + "source code string cannot contain null bytes"); + return NULL; + } + + // Run the code in the interpreter. + if (_run_script_in_interpreter(interp, codestr, shared) != 0) { + return NULL; + } + Py_RETURN_NONE; +}""" +if source[opening:end] != expected: + raise SystemExit("interp_run_string must preserve parse/lookup/UTF-8 validation order") +PY_INTERP_RUN_STRING_TEST +then + pass "interp_run_string preserves lookup/validation order with C89 declarations" +else + bad "interp_run_string preserves lookup/validation order with C89 declarations" +fi + +# Remaining interpreter/channel list entry points hoist C89 locals without shifting checks or cleanup. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_LIST_ENTRYPOINTS_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +checks = { + "interp_is_running(PyObject *self, PyObject *args, PyObject *kwds)": """{ + static char *kwlist[] = {"id", NULL}; + PyObject *id; + PyInterpreterState *interp; + int is_running; + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "O:is_running", kwlist, &id)) { + return NULL; + } + + interp = _PyInterpreterID_LookUp(id); + if (interp == NULL) { + return NULL; + } + is_running = _is_running(interp); + if (is_running < 0) { + return NULL; + } + if (is_running) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +}""", + "channel_create(PyObject *self, PyObject *Py_UNUSED(ignored))": """{ + int64_t cid = _channel_create(&_globals.channels); + PyObject *id; + if (cid < 0) { + return NULL; + } + id = (PyObject *)newchannelid(&ChannelIDtype, cid, 0, + &_globals.channels, 0, 0); + if (id == NULL) { + if (_channel_destroy(&_globals.channels, cid) != 0) { + // XXX issue a warning? + } + return NULL; + } + assert(((channelid *)id)->channels != NULL); + return id; +}""", + "channel_list_all(PyObject *self, PyObject *Py_UNUSED(ignored))": """{ + int64_t count = 0; + int64_t *cids = _channels_list_all(&_globals.channels, &count); + PyObject *ids; + int64_t *cur; + int64_t i; + if (cids == NULL) { + if (count == 0) { + return PyList_New(0); + } + return NULL; + } + ids = PyList_New((Py_ssize_t)count); + if (ids == NULL) { + goto finally; + } + cur = cids; + for (i=0; i < count; cur++, i++) { + PyObject *id = (PyObject *)newchannelid(&ChannelIDtype, *cur, 0, + &_globals.channels, 0, 0); + if (id == NULL) { + Py_DECREF(ids); + ids = NULL; + break; + } + PyList_SET_ITEM(ids, i, id); + } + +finally: + PyMem_Free(cids); + return ids; +}""", + "channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds)": """{ + static char *kwlist[] = {"cid", "send", NULL}; + int64_t cid; /* Channel ID */ + int send = 0; /* Send or receive end? */ + int64_t id; + PyObject *ids, *id_obj; + PyInterpreterState *interp; + int res; + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "O&$p:channel_list_interpreters", + kwlist, channel_id_converter, &cid, &send)) { + return NULL; + } + + ids = PyList_New(0); + if (ids == NULL) { + goto except; + } + + interp = PyInterpreterState_Head(); + while (interp != NULL) { + id = PyInterpreterState_GetID(interp); + assert(id >= 0); + res = _channel_is_associated(&_globals.channels, cid, id, send); + if (res < 0) { + goto except; + } + if (res) { + id_obj = _PyInterpreterState_GetIDObject(interp); + if (id_obj == NULL) { + goto except; + } + res = PyList_Insert(ids, 0, id_obj); + Py_DECREF(id_obj); + if (res < 0) { + goto except; + } + } + interp = PyInterpreterState_Next(interp); + } + + goto finally; + +except: + Py_XDECREF(ids); + ids = NULL; + +finally: + return ids; +}""", +} +for signature, expected in checks.items(): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + if source[opening:end] != expected: + raise SystemExit("interpreter/channel list helper changed checks, ownership, or cleanup order: " + signature) +PY_LIST_ENTRYPOINTS_TEST +then + pass "interpreter/channel list entry points preserve behavior with C89 declarations" +else + bad "interpreter/channel list entry points preserve behavior with C89 declarations" +fi + +# Channel receive/module initialization hoist locals while retaining default/refcount and init order. +if python3 - "$REPO_ROOT/cpython/Modules/_xxsubinterpretersmodule.c" <<'PY_CHANNEL_RECV_INIT_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +checks = { + "channel_recv(PyObject *self, PyObject *args, PyObject *kwds)": """{ + static char *kwlist[] = {"cid", "default", NULL}; + int64_t cid; + PyObject *dflt = NULL; + PyObject *obj; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O:channel_recv", kwlist, + channel_id_converter, &cid, &dflt)) { + return NULL; + } + Py_XINCREF(dflt); + + obj = _channel_recv(&_globals.channels, cid); + if (obj != NULL) { + Py_XDECREF(dflt); + return obj; + } else if (PyErr_Occurred()) { + Py_XDECREF(dflt); + return NULL; + } else if (dflt != NULL) { + return dflt; + } else { + PyErr_Format(ChannelEmptyError, "channel %" PRId64 " is empty", cid); + return NULL; + } +}""", + "PyInit__xxsubinterpreters(void)": """{ + PyObject *module; + PyObject *ns; + if (_init_globals() != 0) { + return NULL; + } + + /* Initialize types */ + if (PyType_Ready(&ChannelIDtype) != 0) { + return NULL; + } + + /* Create the module */ + module = PyModule_Create(&interpretersmodule); + if (module == NULL) { + return NULL; + } + + /* Add exception types */ + ns = PyModule_GetDict(module); // borrowed + if (interp_exceptions_init(ns) != 0) { + return NULL; + } + if (channel_exceptions_init(ns) != 0) { + return NULL; + } + + /* Add other types */ + Py_INCREF(&ChannelIDtype); + if (PyDict_SetItemString(ns, "ChannelID", (PyObject *)&ChannelIDtype) != 0) { + return NULL; + } + Py_INCREF(&_PyInterpreterID_Type); + if (PyDict_SetItemString(ns, "InterpreterID", (PyObject *)&_PyInterpreterID_Type) != 0) { + return NULL; + } + + if (_PyCrossInterpreterData_RegisterClass(&ChannelIDtype, _channelid_shared)) { + return NULL; + } + + return module; +}""", +} +for signature, expected in checks.items(): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + if source[opening:end] != expected: + raise SystemExit("channel receive/module init changed refcount, exception, or registration order: " + signature) +PY_CHANNEL_RECV_INIT_TEST +then + pass "channel receive/module initialization preserve behavior with C89 declarations" +else + bad "channel receive/module initialization preserve behavior with C89 declarations" +fi + +# fileio offset types use VC6's established 64-bit alias; Clinic local is declared before statements. +if python3 - "$REPO_ROOT/cpython/Modules/_io/_iomodule.h" "$REPO_ROOT/cpython/Modules/_io/clinic/fileio.c.h" <<'PY_FILEIO_VC6_TEST' +import re +import sys +from pathlib import Path +header = Path(sys.argv[1]).read_text() +clinic = Path(sys.argv[2]).read_text() +if "typedef PY_LONG_LONG Py_off_t;" not in header or "# define PY_OFF_T_COMPAT PY_LONG_LONG" not in header: + raise SystemExit("fileio offset typedef/compat alias must use PY_LONG_LONG for VC6") +if "# define PY_OFF_T_MAX PY_LLONG_MAX" not in header or "# define PY_OFF_T_MIN PY_LLONG_MIN" not in header: + raise SystemExit("Windows file offset bounds must use the VC6-compatible PY_LLONG aliases") +win_start = header.index("/* Windows uses long long for offsets */") +win_end = header.index("\n#else\n", win_start) +windows = header[win_start:win_end] +if re.search(r"typedef\s+long\s+long\s+Py_off_t|#\s*define\s+PY_OFF_T_COMPAT\s+long\s+long", windows): + raise SystemExit("raw long long must not remain in Windows offset type declarations") +start = clinic.index("_io_FileIO___init__(PyObject *self, PyObject *args, PyObject *kwargs)") +end = clinic.index("exit:", start) +body = clinic[start:end] +if body.count("Py_ssize_t mode_length;") != 1: + raise SystemExit("FileIO mode_length must have exactly one declaration") +if not (body.index("Py_ssize_t mode_length;") < body.index("fastargs =") < body.index("if (fastargs[1])") < body.index("mode = PyUnicode_AsUTF8AndSize")): + raise SystemExit("FileIO mode length must remain assigned only after optional-mode validation") +PY_FILEIO_VC6_TEST +then + pass "fileio offset and Clinic declaration invariants pass" +else + bad "fileio offset and Clinic declaration invariants pass" +fi + +# resize_buffer keeps its allocation-size read after assertions with a C89 local declaration. +if python3 - "$REPO_ROOT/cpython/Modules/_io/bytesio.c" <<'PY_BYTESIO_RESIZE_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("resize_buffer(bytesio *self, size_t size)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +expected = """{ + size_t alloc; + assert(self->buf != NULL); + assert(self->exports == 0); + + /* Here, unsigned types are used to avoid dealing with signed integer + overflow, which is undefined in C. */ + alloc = PyBytes_GET_SIZE(self->buf); + + /* For simplicity, stay in the range of the signed type. Anyway, Python + doesn't allow strings to be longer than this. */ + if (size > PY_SSIZE_T_MAX) + goto overflow; + + if (size < alloc / 2) { + /* Major downsize; resize down to exact size. */ + alloc = size + 1; + } + else if (size < alloc) { + /* Within allocated size; quick exit */ + return 0; + } + else if (size <= alloc * 1.125) { + /* Moderate upsize; overallocate similar to list_resize() */ + alloc = size + (size >> 3) + (size < 9 ? 3 : 6); + } + else { + /* Major upsize; resize up to exact size */ + alloc = size + 1; + } + + if (alloc > ((size_t)-1) / sizeof(char)) + goto overflow; + + if (SHARED_BUF(self)) { + if (unshare_buffer(self, alloc) < 0) + return -1; + } + else { + if (_PyBytes_Resize(&self->buf, alloc) < 0) + return -1; + } + + return 0; + + overflow: + PyErr_SetString(PyExc_OverflowError, + "new buffer size too large"); + return -1; +}""" +if source[opening:end] != expected: + raise SystemExit("resize_buffer must preserve overflow/resize paths and allocation read timing") +PY_BYTESIO_RESIZE_TEST +then + pass "resize_buffer preserves allocation/overflow behavior with C89 declarations" +else + bad "resize_buffer preserves allocation/overflow behavior with C89 declarations" +fi + +# write_bytes keeps the buffer acquire, length read, position assertion, and cleanup order with C89 locals. +if python3 - "$REPO_ROOT/cpython/Modules/_io/bytesio.c" <<'PY_BYTESIO_WRITE_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("write_bytes(bytesio *self, PyObject *b)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +expected = """{ + Py_buffer buf; + Py_ssize_t len; + size_t endpos; + if (check_closed(self)) { + return -1; + } + if (check_exports(self)) { + return -1; + } + + if (PyObject_GetBuffer(b, &buf, PyBUF_CONTIG_RO) < 0) { + return -1; + } + len = buf.len; + if (len == 0) { + goto done; + } + + assert(self->pos >= 0); + endpos = (size_t)self->pos + len; + if (endpos > (size_t)PyBytes_GET_SIZE(self->buf)) { + if (resize_buffer(self, endpos) < 0) { + len = -1; + goto done; + } + } + else if (SHARED_BUF(self)) { + if (unshare_buffer(self, Py_MAX(endpos, (size_t)self->string_size)) < 0) { + len = -1; + goto done; + } + } + + if (self->pos > self->string_size) { + /* In case of overseek, pad with null bytes the buffer region between + the end of stream and the current position. + + 0 lo string_size hi + | |<---used--->|<----------available----------->| + | | <--to pad-->|<---to write---> | + 0 buf position + */ + memset(PyBytes_AS_STRING(self->buf) + self->string_size, '\\0', + (self->pos - self->string_size) * sizeof(char)); + } + + /* Copy the data to the internal buffer, overwriting some of the existing + data if self->pos < self->string_size. */ + memcpy(PyBytes_AS_STRING(self->buf) + self->pos, buf.buf, len); + self->pos = endpos; + + /* Set the new length of the internal string if it has changed. */ + if ((size_t)self->string_size < endpos) { + self->string_size = endpos; + } + + done: + PyBuffer_Release(&buf); + return len; +}""" +if source[opening:end] != expected: + raise SystemExit("write_bytes must preserve acquisition, position, resizing, and buffer release order") +PY_BYTESIO_WRITE_TEST +then + pass "write_bytes preserves buffer lifetime and resize order with C89 declarations" +else + bad "write_bytes preserves buffer lifetime and resize order with C89 declarations" +fi + +# IncrementalNewlineDecoder's 64-bit flags use CPython's VC6-compatible integer spelling. +if python3 - "$REPO_ROOT/cpython/Modules/_io/textio.c" <<'PY_TEXTIO_FLAGS_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +for signature in ( + "_io_IncrementalNewlineDecoder_getstate_impl(nldecoder_object *self)", + "_io_IncrementalNewlineDecoder_setstate(nldecoder_object *self,", +): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + body = source[opening:end] + if "unsigned PY_LONG_LONG flag;" not in body or "unsigned long long flag;" in body: + raise SystemExit("IncrementalNewlineDecoder flag must retain unsigned 64-bit width through PY_LONG_LONG") +PY_TEXTIO_FLAGS_TEST +then + pass "textio decoder flags use VC6-compatible unsigned 64-bit types" +else + bad "textio decoder flags use VC6-compatible unsigned 64-bit types" +fi + +# TextIOWrapper encoder/error helpers hoist locals without moving codec calls or early returns. +if python3 - "$REPO_ROOT/cpython/Modules/_io/textio.c" <<'PY_TEXTIO_ERROR_HELPERS_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +checks = { + "_textiowrapper_fix_encoder_state(textio *self)": """{ + PyObject *cookieObj; + PyObject *res; + int cmp; + if (!self->seekable || !self->encoder) { + return 0; + } + + self->encoding_start_of_stream = 1; + + cookieObj = PyObject_CallMethodNoArgs( + self->buffer, &_Py_ID(tell)); + if (cookieObj == NULL) { + return -1; + } + + cmp = PyObject_RichCompareBool(cookieObj, _PyLong_GetZero(), Py_EQ); + Py_DECREF(cookieObj); + if (cmp < 0) { + return -1; + } + + if (cmp == 0) { + self->encoding_start_of_stream = 0; + res = PyObject_CallMethodOneArg( + self->encoder, &_Py_ID(setstate), _PyLong_GetZero()); + if (res == NULL) { + return -1; + } + Py_DECREF(res); + } + + return 0; +}""", + "io_check_errors(PyObject *errors)": """{ + PyInterpreterState *interp; + Py_ssize_t name_length; + const char *name; + PyObject *handler; + assert(errors != NULL && errors != Py_None); + + interp = _PyInterpreterState_GET(); +#ifndef Py_DEBUG + /* In release mode, only check in development mode (-X dev) */ + if (!_PyInterpreterState_GetConfig(interp)->dev_mode) { + return 0; + } +#else + /* Always check in debug mode */ +#endif + + /* Avoid calling PyCodec_LookupError() before the codec registry is ready: + before_PyUnicode_InitEncodings() is called. */ + if (!interp->unicode.fs_codec.encoding) { + return 0; + } + + name = PyUnicode_AsUTF8AndSize(errors, &name_length); + if (name == NULL) { + return -1; + } + if (strlen(name) != (size_t)name_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character in errors"); + return -1; + } + handler = PyCodec_LookupError(name); + if (handler != NULL) { + Py_DECREF(handler); + return 0; + } + return -1; +}""", +} +for signature, expected in checks.items(): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + if source[opening:end] != expected: + raise SystemExit("textio helper changed codec/error lookup or reference-handling order: " + signature) +PY_TEXTIO_ERROR_HELPERS_TEST +then + pass "textio encoder/error helpers preserve lookup and return order with C89 locals" +else + bad "textio encoder/error helpers preserve lookup and return order with C89 locals" +fi + +# TextIOWrapper init keeps errors UTF-8 extraction after validation while declaring its locals in C89 order. +if python3 - "$REPO_ROOT/cpython/Modules/_io/textio.c" <<'PY_TEXTIO_INIT_ERRORS_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_io_TextIOWrapper___init___impl(textio *self, PyObject *buffer,") +opening = source.index("{", start) +end = source.index("\n}\n", opening) + 2 +body = source[opening:end] +for declaration in ("Py_ssize_t errors_len;", "const char *errors_str;"): + if body.count(declaration) != 1 or body.index(declaration) > body.index("self->ok = 0;"): + raise SystemExit("TextIOWrapper init locals must be declared before executable statements") +if "const char *errors_str = PyUnicode_AsUTF8AndSize" in body: + raise SystemExit("TextIOWrapper errors conversion must remain assigned after validation") +markers = ("if (errors == Py_None)", "else if (io_check_errors(errors))", "errors_str = PyUnicode_AsUTF8AndSize(errors, &errors_len);") +positions = [body.index(marker) for marker in markers] +if positions != sorted(positions): + raise SystemExit("TextIOWrapper errors conversion must remain after errors validation") +PY_TEXTIO_INIT_ERRORS_TEST +then + pass "TextIOWrapper init keeps errors conversion after validation with C89 locals" +else + bad "TextIOWrapper init keeps errors conversion after validation with C89 locals" +fi + +# textiowrapper_change_encoding hoists codec locals while preserving reference cleanup on every failure. +if python3 - "$REPO_ROOT/cpython/Modules/_io/textio.c" <<'PY_TEXTIO_CHANGE_ENCODING_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("textiowrapper_change_encoding(textio *self, PyObject *encoding,") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +expected = """{ + const char *c_encoding; + const char *c_errors; + PyObject *codec_info; + /* Use existing settings where new settings are not specified */ + if (encoding == Py_None && errors == Py_None && !newline_changed) { + return 0; // no change + } + + if (encoding == Py_None) { + encoding = self->encoding; + if (errors == Py_None) { + errors = self->errors; + } + Py_INCREF(encoding); + } + else { + if (_PyUnicode_EqualToASCIIString(encoding, "locale")) { + encoding = _Py_GetLocaleEncodingObject(); + if (encoding == NULL) { + return -1; + } + } else { + Py_INCREF(encoding); + } + if (errors == Py_None) { + errors = &_Py_ID(strict); + } + } + Py_INCREF(errors); + + c_encoding = PyUnicode_AsUTF8(encoding); + if (c_encoding == NULL) { + Py_DECREF(encoding); + Py_DECREF(errors); + return -1; + } + c_errors = PyUnicode_AsUTF8(errors); + if (c_errors == NULL) { + Py_DECREF(encoding); + Py_DECREF(errors); + return -1; + } + + // Create new encoder & decoder + codec_info = _PyCodec_LookupTextEncoding( + c_encoding, "codecs.open()"); + if (codec_info == NULL) { + Py_DECREF(encoding); + Py_DECREF(errors); + return -1; + } + if (_textiowrapper_set_decoder(self, codec_info, c_errors) != 0 || + _textiowrapper_set_encoder(self, codec_info, c_errors) != 0) { + Py_DECREF(codec_info); + Py_DECREF(encoding); + Py_DECREF(errors); + return -1; + } + Py_DECREF(codec_info); + + Py_SETREF(self->encoding, encoding); + Py_SETREF(self->errors, errors); + + return _textiowrapper_fix_encoder_state(self); +}""" +if source[opening:end] != expected: + raise SystemExit("textiowrapper_change_encoding must preserve codec calls and refcount cleanup order") +PY_TEXTIO_CHANGE_ENCODING_TEST +then + pass "textiowrapper_change_encoding preserves codec/refcount behavior with C89 locals" +else + bad "textiowrapper_change_encoding preserves codec/refcount behavior with C89 locals" +fi + +# TextIOWrapper reconfigure hoists its flush result without changing validation or ownership order. +if python3 - "$REPO_ROOT/cpython/Modules/_io/textio.c" <<'PY_TEXTIO_RECONFIGURE_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_io_TextIOWrapper_reconfigure_impl(textio *self, PyObject *encoding,") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +body = source[opening:end] +if body.count("PyObject *res;") != 1 or body.index("PyObject *res;") > body.index("if (encoding"): + raise SystemExit("reconfigure flush result must be declared before executable validation") +if "PyObject *res = PyObject_CallMethodNoArgs" in body: + raise SystemExit("reconfigure flush must remain assigned after validation") +markers = ( + "write_through = convert_optional_bool(", + "if (write_through < 0)", + "res = PyObject_CallMethodNoArgs((PyObject *)self, &_Py_ID(flush));", + "Py_DECREF(res);", + "self->b2cratio = 0;", +) +positions = [body.index(marker) for marker in markers] +if positions != sorted(positions): + raise SystemExit("reconfigure must validate optional flags before flush and release the result before state update") +PY_TEXTIO_RECONFIGURE_TEST +then + pass "TextIOWrapper reconfigure preserves validation/flush order with C89 locals" +else + bad "TextIOWrapper reconfigure preserves validation/flush order with C89 locals" +fi + +# _textiowrapper_writeflush keeps pending-buffer ownership and write retry order with C89 locals. +if python3 - "$REPO_ROOT/cpython/Modules/_io/textio.c" <<'PY_TEXTIO_WRITEFLUSH_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_textiowrapper_writeflush(textio *self)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +expected = """{ + PyObject *pending; + PyObject *b; + char *buf; + Py_ssize_t pos; + Py_ssize_t i; + PyObject *ret; + if (self->pending_bytes == NULL) + return 0; + + pending = self->pending_bytes; + + if (PyBytes_Check(pending)) { + b = pending; + Py_INCREF(b); + } + else if (PyUnicode_Check(pending)) { + assert(PyUnicode_IS_ASCII(pending)); + assert(PyUnicode_GET_LENGTH(pending) == self->pending_bytes_count); + b = PyBytes_FromStringAndSize( + PyUnicode_DATA(pending), PyUnicode_GET_LENGTH(pending)); + if (b == NULL) { + return -1; + } + } + else { + assert(PyList_Check(pending)); + b = PyBytes_FromStringAndSize(NULL, self->pending_bytes_count); + if (b == NULL) { + return -1; + } + + buf = PyBytes_AsString(b); + pos = 0; + + for (i = 0; i < PyList_GET_SIZE(pending); i++) { + PyObject *obj = PyList_GET_ITEM(pending, i); + char *src; + Py_ssize_t len; + if (PyUnicode_Check(obj)) { + assert(PyUnicode_IS_ASCII(obj)); + src = PyUnicode_DATA(obj); + len = PyUnicode_GET_LENGTH(obj); + } + else { + assert(PyBytes_Check(obj)); + if (PyBytes_AsStringAndSize(obj, &src, &len) < 0) { + Py_DECREF(b); + return -1; + } + } + memcpy(buf + pos, src, len); + pos += len; + } + assert(pos == self->pending_bytes_count); + } + + self->pending_bytes_count = 0; + self->pending_bytes = NULL; + Py_DECREF(pending); + + do { + ret = PyObject_CallMethodOneArg(self->buffer, &_Py_ID(write), b); + } while (ret == NULL && _PyIO_trap_eintr()); + Py_DECREF(b); + // NOTE: We cleared buffer but we don't know how many bytes are actually written + // when an error occurred. + if (ret == NULL) + return -1; + Py_DECREF(ret); + return 0; +}""" +if source[opening:end] != expected: + raise SystemExit("writeflush must preserve buffer ownership, list flattening, retry, and error order") +PY_TEXTIO_WRITEFLUSH_TEST +then + pass "textio writeflush preserves ownership/retry behavior with C89 locals" +else + bad "textio writeflush preserves ownership/retry behavior with C89 locals" +fi + +# TextIOWrapper.write declares byte-size and list-append locals before validation; assignment/refcount order stays fixed. +if python3 - "$REPO_ROOT/cpython/Modules/_io/textio.c" <<'PY_TEXTIO_WRITE_IMPL_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_io_TextIOWrapper_write_impl(textio *self, PyObject *text)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +body = source[opening:end] +for declaration in ("Py_ssize_t bytes_len;", "int list_ret;"): + if body.count(declaration) != 1 or body.index(declaration) > body.index("if (PyUnicode_READY(text)"): + raise SystemExit("write locals must be declared before executable statements") +if "Py_ssize_t bytes_len =" in body or "int ret = PyList_Append" in body: + raise SystemExit("write locals must not be redeclared after statements") +markers = ( + "if (b != text && !PyBytes_Check(b))", + "if (b == text)", + "bytes_len = PyBytes_GET_SIZE(b);", + "list_ret = PyList_Append(self->pending_bytes, b);", +) +positions = [body.index(marker) for marker in markers] +append_pos = positions[-1] +decref_pos = body.index("Py_DECREF(b);", append_pos) +append_error_pos = body.index("if (list_ret < 0)", decref_pos) +positions.extend((decref_pos, append_error_pos)) +if positions != sorted(positions): + raise SystemExit("write must preserve encoded-value validation, length, append and decref order") +PY_TEXTIO_WRITE_IMPL_TEST +then + pass "TextIOWrapper write preserves validation/append ownership with C89 locals" +else + bad "TextIOWrapper write preserves validation/append ownership with C89 locals" +fi + +# textiowrapper_read_chunk preserves snapshot construction and failure cleanup with a C89 declaration. +if python3 - "$REPO_ROOT/cpython/Modules/_io/textio.c" <<'PY_TEXTIO_READ_CHUNK_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +body = source[opening:end] +if body.count("PyObject *snapshot;") != 1 or body.index("PyObject *snapshot;") > body.index("if (self->decoder == NULL)"): + raise SystemExit("read_chunk snapshot must be declared before executable statements") +if "PyObject *snapshot = Py_BuildValue" in body: + raise SystemExit("read_chunk snapshot must remain constructed at its original point") +markers = ( + "PyBytes_Concat(&next_input, input_chunk);", + "if (next_input == NULL)", + "snapshot = Py_BuildValue(\"NN\", dec_flags, next_input);", + "if (snapshot == NULL)", + "Py_XSETREF(self->snapshot, snapshot);", +) +positions = [body.index(marker) for marker in markers[:4]] +dec_flags_pos = body.index("dec_flags = NULL;", positions[-1]) +snapshot_set_pos = body.index(markers[4], dec_flags_pos) +positions.extend((dec_flags_pos, snapshot_set_pos)) +if positions != sorted(positions): + raise SystemExit("read_chunk must preserve snapshot failure handling and reference ownership") +PY_TEXTIO_READ_CHUNK_TEST +then + pass "textio read_chunk preserves snapshot/error ownership with C89 locals" +else + bad "textio read_chunk preserves snapshot/error ownership with C89 locals" +fi + +# TextIOWrapper.seek declares the zero singleton up front but evaluates it after seekability validation. +if python3 - "$REPO_ROOT/cpython/Modules/_io/textio.c" <<'PY_TEXTIO_SEEK_ZERO_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +body = source[opening:end] +if body.count("PyObject *zero;") != 1 or body.index("PyObject *zero;") > body.index("CHECK_ATTACHED(self);"): + raise SystemExit("seek zero singleton must be declared before executable statements") +if "PyObject *zero = _PyLong_GetZero()" in body: + raise SystemExit("seek zero singleton lookup must remain after seekability check") +markers = ( + "if (!self->seekable)", + "zero = _PyLong_GetZero(); // borrowed reference", + "switch (whence)", + "PyObject_RichCompareBool(cookieObj, zero, Py_EQ)", +) +positions = [body.index(marker) for marker in markers] +if positions != sorted(positions): + raise SystemExit("seek must preserve validation and zero lookup timing") +PY_TEXTIO_SEEK_ZERO_TEST +then + pass "TextIOWrapper seek preserves validation/singleton timing with C89 local" +else + bad "TextIOWrapper seek preserves validation/singleton timing with C89 local" +fi + +# _PyIO_get_console_type hoists locals while keeping path conversion, handle lookup and cleanup at their original points. +if python3 - "$REPO_ROOT/cpython/Modules/_io/winconsoleio.c" <<'PY_WINCONSOLE_TYPE_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_PyIO_get_console_type(PyObject *path_or_fd)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +body = source[opening:end] +first_statement = body.index("fd = PyLong_AsLong(path_or_fd);") +for declaration in ( + "int fd;", "HANDLE handle;", "PyObject *decoded;", "wchar_t *decoded_wstr;", + "char m;", "DWORD length;", "wchar_t name_buf[MAX_PATH];", "wchar_t *pname_buf;", +): + if body.count(declaration) != 1 or body.index(declaration) > first_statement: + raise SystemExit("console type locals must be declared before executable statements") +for old in ("int fd = PyLong_AsLong", "HANDLE handle =", "char m =", "wchar_t name_buf[MAX_PATH], *pname_buf = name_buf"): + if old in body: + raise SystemExit("console type locals must not be declared after executable statements") +markers = ( + "fd = PyLong_AsLong(path_or_fd);", + "PyErr_Clear();", + "handle = _Py_get_osfhandle_noraise(fd);", + "if (!PyUnicode_FSDecoder(path_or_fd, &decoded))", + "decoded_wstr = PyUnicode_AsWideCharString(decoded, NULL);", + "m = '\\0';", + "length = GetFullPathNameW(decoded_wstr, MAX_PATH, pname_buf, NULL);", + "PyMem_Free(decoded_wstr);", +) +positions = [body.index(marker) for marker in markers] +if positions != sorted(positions): + raise SystemExit("console type lookup and cleanup order must remain unchanged") +PY_WINCONSOLE_TYPE_TEST +then + pass "console type detection preserves control/cleanup order with C89 locals" +else + bad "console type detection preserves control/cleanup order with C89 locals" +fi + +# Console byte-buffer helpers keep loop bounds/order while replacing C99 loop declarations. +if python3 - "$REPO_ROOT/cpython/Modules/_io/winconsoleio.c" <<'PY_WINCONSOLE_BUFFER_LOOPS_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +def body_for(signature): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + return source[opening:end] +buflen = body_for("_buflen(winconsoleio *self)") +if buflen.count("DWORD i;") != 1 or buflen.index("DWORD i;") > buflen.index("for (i = 0;"): + raise SystemExit("_buflen loop index must be declared before the loop") +if "for (DWORD i =" in buflen: + raise SystemExit("_buflen must use a C89 loop declaration") +copyfrombuf = body_for("_copyfrombuf(winconsoleio *self, char *buf, DWORD len)") +if copyfrombuf.count("int i;") != 1 or copyfrombuf.index("int i;") > copyfrombuf.index("while (self->buf[0]"): + raise SystemExit("_copyfrombuf loop index must be declared before executable statements") +if "for (int i =" in copyfrombuf: + raise SystemExit("_copyfrombuf must use a C89 loop declaration") +markers = ("while (self->buf[0] && len--)", "for (i = 1;", "self->buf[SMALLBUF - 1] = 0;") +positions = [copyfrombuf.index(marker) for marker in markers] +if positions != sorted(positions): + raise SystemExit("_copyfrombuf must preserve byte shift order") +PY_WINCONSOLE_BUFFER_LOOPS_TEST +then + pass "console buffer helpers preserve loop behavior with C89 declarations" +else + bad "console buffer helpers preserve loop behavior with C89 declarations" +fi + +# read_console_w keeps the allow-threads boundaries and call/error order while hoisting C89 locals. +if python3 - "$REPO_ROOT/cpython/Modules/_io/winconsoleio.c" <<'PY_WINCONSOLE_READ_W_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +body = source[opening:end] +for declaration in ("DWORD off;", "HANDLE hInterruptEvent;"): + if body.count(declaration) != 1 or body.index(declaration) > body.index("wchar_t *buf ="): + raise SystemExit("read_console_w function locals must precede executable statements") +loop = body.index("while (off < maxlen) {") +first_loop_statement = body.index("n = (DWORD)-1;", loop) +for declaration in ("DWORD n;", "DWORD len;", "BOOL res;"): + if body.count(declaration) != 1 or body.index(declaration) < loop or body.index(declaration) > first_loop_statement: + raise SystemExit("read_console_w loop locals must be declared at the start of the loop body") +for old in ("DWORD off = 0;", "DWORD n = (DWORD)-1;", "DWORD len = min(", "BOOL res = ReadConsoleW", "HANDLE hInterruptEvent ="): + if body.count(old) > 1: + raise SystemExit("read_console_w must not redeclare C89 locals") +markers = ( + "Py_BEGIN_ALLOW_THREADS", + "off = 0;", + "while (off < maxlen)", + "res = ReadConsoleW(handle, &buf[off], len, &n, NULL);", + "hInterruptEvent = _PyOS_SigintEvent();", + "Py_END_ALLOW_THREADS", +) +positions = [body.index(marker) for marker in markers] +if positions != sorted(positions): + raise SystemExit("read_console_w must preserve thread-release and console-read ordering") +PY_WINCONSOLE_READ_W_TEST +then + pass "read_console_w preserves allow-threads/call order with C89 locals" +else + bad "read_console_w preserves allow-threads/call order with C89 locals" +fi + +# readinto hoists only declarations, retaining all evaluation/control/cleanup order. +if python3 - "$REPO_ROOT/cpython/Modules/_io/winconsoleio.c" <<'PY_WINCONSOLE_READINTO_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("readinto(winconsoleio *self, char *buf, Py_ssize_t len)") +opening = source.index("{", start) +end = source.index("\n}\n", opening) + 2 +body = source[opening:end] +expected = r'''{ + HANDLE handle; + DWORD wlen; + DWORD read_len; + DWORD n; + wchar_t *wbuf; + int err; + DWORD u8n; + + if (self->fd == -1) { + err_closed(); + return -1; + } + if (!self->readable) { + err_mode("reading"); + return -1; + } + if (len == 0) + return 0; + if (len > BUFMAX) { + PyErr_Format(PyExc_ValueError, "cannot read more than %d bytes", BUFMAX); + return -1; + } + + handle = _Py_get_osfhandle(self->fd); + if (handle == INVALID_HANDLE_VALUE) + return -1; + + /* Each character may take up to 4 bytes in the final buffer. + This is highly conservative, but necessary to avoid + failure for any given Unicode input (e.g. \U0010ffff). + If the caller requests fewer than 4 bytes, we buffer one + character. + */ + wlen = (DWORD)(len / 4); + if (wlen == 0) { + wlen = 1; + } + + read_len = _copyfrombuf(self, buf, (DWORD)len); + if (read_len) { + buf = &buf[read_len]; + len -= read_len; + wlen -= 1; + } + if (len == read_len || wlen == 0) + return read_len; + + wbuf = read_console_w(handle, wlen, &n); + if (wbuf == NULL) + return -1; + if (n == 0) { + PyMem_Free(wbuf); + return read_len; + } + + err = 0; + u8n = 0; + + Py_BEGIN_ALLOW_THREADS + if (len < 4) { + if (WideCharToMultiByte(CP_UTF8, 0, wbuf, n, + self->buf, sizeof(self->buf) / sizeof(self->buf[0]), + NULL, NULL)) + u8n = _copyfrombuf(self, buf, (DWORD)len); + } else { + u8n = WideCharToMultiByte(CP_UTF8, 0, wbuf, n, + buf, (DWORD)len, NULL, NULL); + } + + if (u8n) { + read_len += u8n; + u8n = 0; + } else { + err = GetLastError(); + if (err == ERROR_INSUFFICIENT_BUFFER) { + /* Calculate the needed buffer for a more useful error, as this + means our "/ 4" logic above is insufficient for some input. + */ + u8n = WideCharToMultiByte(CP_UTF8, 0, wbuf, n, + NULL, 0, NULL, NULL); + } + } + Py_END_ALLOW_THREADS + + PyMem_Free(wbuf); + + if (u8n) { + PyErr_Format(PyExc_SystemError, + "Buffer had room for %zd bytes but %u bytes required", + len, u8n); + return -1; + } + if (err) { + PyErr_SetFromWindowsErr(err); + return -1; + } + + return read_len; +}''' +if body != expected: + raise SystemExit("readinto must hoist C89 locals while preserving evaluation/control/cleanup order") +PY_WINCONSOLE_READINTO_TEST +then + pass "readinto preserves evaluation/control/cleanup order with C89 locals" +else + bad "readinto preserves evaluation/control/cleanup order with C89 locals" +fi + +# read_console_w detects UTF-16 high surrogates by code-unit range, without Vista-only C3_HIGHSURROGATE. +if python3 - "$REPO_ROOT/cpython/Modules/_io/winconsoleio.c" <<'PY_WINCONSOLE_SURROGATE_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +body = source[opening:end] +condition = "buf[*readlen - 1] >= 0xD800 &&\n buf[*readlen - 1] <= 0xDBFF" +if condition not in body: + raise SystemExit("read_console_w must detect the full UTF-16 high-surrogate range") +if "C3_HIGHSURROGATE" in body or "GetStringTypeW(CT_CTYPE3" in body: + raise SystemExit("read_console_w must not depend on the Vista-only high-surrogate type flag") +condition_pos = body.index(condition) +block_threads_pos = body.index("Py_BLOCK_THREADS", condition_pos) +realloc_pos = body.index("PyMem_Realloc(buf, maxlen * sizeof(wchar_t))", block_threads_pos) +unblock_threads_pos = body.index("Py_UNBLOCK_THREADS", realloc_pos) +if not condition_pos < realloc_pos < unblock_threads_pos: + raise SystemExit("surrogate-triggered buffer growth must retain its thread/resize ordering") +PY_WINCONSOLE_SURROGATE_TEST +then + pass "console read preserves surrogate-boundary growth on legacy Windows" +else + bad "console read preserves surrogate-boundary growth on legacy Windows" +fi + +# readall buffer reallocation and generated console-init mode parsing retain their evaluation points. +if python3 - "$REPO_ROOT/cpython/Modules/_io/winconsoleio.c" "$REPO_ROOT/cpython/Modules/_io/clinic/winconsoleio.c.h" <<'PY_WINCONSOLE_READALL_CLINIC_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +clinic = Path(sys.argv[2]).read_text() +start = source.index("_io__WindowsConsoleIO_readall_impl(winconsoleio *self)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +body = source[opening:end] +loop = body[body.index("while (1) {"):] +first_loop_statement = loop.index("if (len >= (Py_ssize_t)bufsize)") +if loop.count("wchar_t *tmp;") != 1 or loop.index("wchar_t *tmp;") > first_loop_statement: + raise SystemExit("readall temporary buffer pointer must be declared before loop statements") +if "wchar_t *tmp = PyMem_Realloc" in loop: + raise SystemExit("readall reallocation must remain at its original point") +markers = ("bufsize = newsize;", "tmp = PyMem_Realloc(buf,", "if (tmp == NULL)", "buf = tmp;") +positions = [loop.index(marker) for marker in markers] +if positions != sorted(positions): + raise SystemExit("readall must preserve buffer resize and failure order") +start = clinic.index("_io__WindowsConsoleIO___init__(PyObject *self") +opening = clinic.index("{", start) +end = clinic.index(chr(10) + "}" + chr(10), opening) + 2 +wrapper = clinic[opening:end] +if wrapper.count("Py_ssize_t mode_length;") != 1 or wrapper.index("Py_ssize_t mode_length;") > wrapper.index("fastargs ="): + raise SystemExit("generated mode_length local must be in the wrapper declaration group") +if "Py_ssize_t mode_length;\n mode =" in wrapper: + raise SystemExit("generated mode_length must not remain declared after validation") +if wrapper.index("if (!PyUnicode_Check(fastargs[1]))") > wrapper.index("mode = PyUnicode_AsUTF8AndSize"): + raise SystemExit("generated mode validation must remain before UTF-8 conversion") +PY_WINCONSOLE_READALL_CLINIC_TEST +then + pass "console readall/Clinic locals preserve buffer and validation order" +else + bad "console readall/Clinic locals preserve buffer and validation order" +fi + +# _io_text_encoding assigns preconfig only after warning handling while declaring it in C89 order. +if python3 - "$REPO_ROOT/cpython/Modules/_io/_iomodule.c" <<'PY_IOMODULE_PRECONFIG_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_io_text_encoding_impl(PyObject *module, PyObject *encoding, int stacklevel)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +body = source[opening:end] +first_statement = body.index("if (encoding == NULL || encoding == Py_None)") +if body.count("const PyPreConfig *preconfig;") != 1 or body.index("const PyPreConfig *preconfig;") > first_statement: + raise SystemExit("preconfig must be declared before executable statements") +if "const PyPreConfig *preconfig =" in body: + raise SystemExit("preconfig lookup must not be initialized early") +markers = ( + "PyErr_WarnEx(PyExc_EncodingWarning,", + "preconfig = &_PyRuntime.preconfig;", + "if (preconfig->utf8_mode)", +) +positions = [body.index(marker) for marker in markers] +if positions != sorted(positions): + raise SystemExit("preconfig lookup must remain after warning handling") +PY_IOMODULE_PRECONFIG_TEST +then + pass "io encoding selection preserves warning/preconfig order with C89 local" +else + bad "io encoding selection preserves warning/preconfig order with C89 local" +fi + +# _io cleanup/init loops and open Clinic mode length use C89 declarations without shifting work. +if python3 - "$REPO_ROOT/cpython/Modules/_io/_iomodule.c" "$REPO_ROOT/cpython/Modules/_io/clinic/_iomodule.c.h" <<'PY_IOMODULE_C89_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +clinic = Path(sys.argv[2]).read_text() +start = source.index("_PyIO_Fini(void)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +fini = source[opening:end] +if "Py_ssize_t i;" not in fini or "for (i = Py_ARRAY_LENGTH(static_types) - 1;" not in fini: + raise SystemExit("_PyIO_Fini loop index must use C89 declaration order") +if "for (Py_ssize_t i=" in fini: + raise SystemExit("_PyIO_Fini must not declare its index in the for initializer") +start = source.index("PyInit__io(void)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +init = source[opening:end] +if init.index("size_t i;") > init.index("PyObject *m = PyModule_Create"): + raise SystemExit("PyInit__io loop index must be in the declaration group") +if "for (i = 0; i < Py_ARRAY_LENGTH(static_types); i++)" not in init: + raise SystemExit("PyInit__io must keep the static-type iteration range") +if "for (size_t i=" in init: + raise SystemExit("PyInit__io must not declare its index in the for initializer") +start = clinic.index("_io_open(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)") +opening = clinic.index("{", start) +end = clinic.index(chr(10) + "}" + chr(10), opening) + 2 +wrapper = clinic[opening:end] +if wrapper.count("Py_ssize_t mode_length;") != 1 or wrapper.index("Py_ssize_t mode_length;") > wrapper.index("args = _PyArg_UnpackKeywords"): + raise SystemExit("open Clinic mode_length must be declared before wrapper statements") +if wrapper.index("if (!PyUnicode_Check(args[1]))") > wrapper.index("mode = PyUnicode_AsUTF8AndSize(args[1], &mode_length)"): + raise SystemExit("open mode validation must remain before UTF-8 conversion") +PY_IOMODULE_C89_TEST +then + pass "io module loops/open wrapper preserve C89 behavior" +else + bad "io module loops/open wrapper preserve C89 behavior" +fi + +# CJK codec helpers preserve reference cleanup and module field order with VC6-compatible declarations. +if python3 - "$REPO_ROOT/cpython/Modules/cjkcodecs/cjkcodecs.h" <<'PY_CJKCODECS_VC6_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +def body_for(signature): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + return source[opening:end] +getcodecmod = body_for("getmultibytecodec(void)") +if getcodecmod.count("PyObject *mod;") != 1 or getcodecmod.count("PyObject *cofunc;") != 1: + raise SystemExit("getmultibytecodec locals must be declared in C89 order") +if getcodecmod.index("PyObject *cofunc;") > getcodecmod.index("if (mod == NULL)"): + raise SystemExit("getmultibytecodec result local must be declared before executable statements") +if "PyObject *cofunc = PyObject_GetAttrString" in getcodecmod: + raise SystemExit("getmultibytecodec attribute lookup must remain after import/error check") +markers = ("mod = PyImport_ImportModuleNoBlock", "if (mod == NULL)", "cofunc = PyObject_GetAttrString", "Py_DECREF(mod);", "return cofunc;") +positions = [getcodecmod.index(marker) for marker in markers] +if positions != sorted(positions): + raise SystemExit("getmultibytecodec must preserve import/refcount order") +register = body_for("register_maps(PyObject *module)") +if register.count("PyObject *capsule;") != 1 or register.index("PyObject *capsule;") > register.index("for (h = mapping_list;"): + raise SystemExit("register_maps capsule local must be declared before iteration") +if "PyObject *capsule = PyCapsule_New" in register: + raise SystemExit("register_maps capsule creation must remain in the mapping loop") +if not (register.index("strcpy(mhname + sizeof(\"__map_\") - 1, h->charset);") < register.index("capsule = PyCapsule_New((void *)h,") < register.index("if (capsule == NULL)") < register.index("PyModule_AddObject(module, mhname, capsule)")): + raise SystemExit("register_maps must preserve capsule creation/error/add ownership order") +module_start = source.index("static struct PyModuleDef _cjk_module =") +module_end = source.index("};", module_start) + 2 +module_def = source[module_start:module_end] +if any(field in module_def for field in (".m_name =", ".m_size =", ".m_methods =", ".m_slots =")): + raise SystemExit("CJK module definition must not use designated initializers") +normalized_module = " ".join(module_def.replace("\\\n", " ").split()) +expected = "static struct PyModuleDef _cjk_module = { PyModuleDef_HEAD_INIT, \"_codecs_\"#loc, NULL, 0, _cjk_methods, _cjk_slots, NULL, NULL, NULL, };" +if normalized_module != expected: + raise SystemExit("CJK module definition must follow PyModuleDef field order") +PY_CJKCODECS_VC6_TEST +then + pass "CJK codecs preserve reference and module initialization behavior" +else + bad "CJK codecs preserve reference and module initialization behavior" +fi + +# multibytecodec_dealloc keeps GC/type deallocation order with a C89 local. +if python3 - "$REPO_ROOT/cpython/Modules/cjkcodecs/multibytecodec.c" <<'PY_MULTIBYTECODEC_DEALLOC_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("multibytecodec_dealloc(MultibyteCodecObject *self)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +body = source[opening:end] +if body.count("PyTypeObject *tp;") != 1 or body.index("PyTypeObject *tp;") > body.index("PyObject_GC_UnTrack(self);"): + raise SystemExit("dealloc type pointer must be declared before executable statements") +if "PyTypeObject *tp = Py_TYPE(self);" in body: + raise SystemExit("dealloc type lookup must remain after GC untracking") +markers = ("PyObject_GC_UnTrack(self);", "tp = Py_TYPE(self);", "tp->tp_free(self);", "Py_DECREF(tp);") +positions = [body.index(marker) for marker in markers] +if positions != sorted(positions): + raise SystemExit("dealloc must preserve GC untrack, free, and decref ordering") +PY_MULTIBYTECODEC_DEALLOC_TEST +then + pass "multibyte codec deallocation preserves order with C89 local" +else + bad "multibyte codec deallocation preserves order with C89 local" +fi + +# CJK dynamic type specifications use the verified PyType_Spec field order (name, size, itemsize, flags, slots). +if python3 - "$REPO_ROOT/cpython/Modules/cjkcodecs/multibytecodec.c" <<'PY_MULTIBYTECODEC_SPECS_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +specs = ( + ("multibytecodec_spec", "MultibyteCodec", "MultibyteCodecObject", "multibytecodec_slots", False), + ("encoder_spec", "MultibyteIncrementalEncoder", "MultibyteIncrementalEncoderObject", "encoder_slots", True), + ("decoder_spec", "MultibyteIncrementalDecoder", "MultibyteIncrementalDecoderObject", "decoder_slots", True), + ("reader_spec", "MultibyteStreamReader", "MultibyteStreamReaderObject", "reader_slots", True), + ("writer_spec", "MultibyteStreamWriter", "MultibyteStreamWriterObject", "writer_slots", True), +) +for name, typename, object_type, slots, base_type in specs: + start = source.index("static PyType_Spec " + name + " = {") + end = source.index("};", start) + 2 + spec = source[start:end] + if any(field in spec for field in (".name =", ".basicsize =", ".itemsize =", ".flags =", ".slots =")): + raise SystemExit("PyType_Spec designated fields must be positional: " + name) + expected = ('MODULE_NAME ".' + typename + '"', "sizeof(" + object_type + ")", "0", "Py_TPFLAGS_BASETYPE" if base_type else "Py_TPFLAGS_DISALLOW_INSTANTIATION", slots) + positions = [spec.index(field) for field in expected] + if positions != sorted(positions): + raise SystemExit("PyType_Spec field order/itemsize changed: " + name) +PY_MULTIBYTECODEC_SPECS_TEST +then + pass "multibytecodec PyType_Spec field ordering is verified" +else + bad "multibytecodec PyType_Spec field ordering is verified" +fi + +# Multibyte codec module-state locals and module definition preserve initialization order/field layout. +if python3 - "$REPO_ROOT/cpython/Modules/cjkcodecs/multibytecodec.c" <<'PY_MULTIBYTECODEC_STATE_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +def body_for(signature): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + return source[opening:end] +cases = ( + ("mbiencoder_new(PyTypeObject *type", "if (!PyArg_ParseTupleAndKeywords", "codec = PyObject_GetAttrString", "state = _multibyte_codec_find_state_by_type(type);", "if (!MultibyteCodec_Check(state, codec))"), + ("mbidecoder_new(PyTypeObject *type", "if (!PyArg_ParseTupleAndKeywords", "codec = PyObject_GetAttrString", "state = _multibyte_codec_find_state_by_type(type);", "if (!MultibyteCodec_Check(state, codec))"), + ("mbstreamreader_new(PyTypeObject *type", "if (!PyArg_ParseTupleAndKeywords", "codec = PyObject_GetAttrString", "state = _multibyte_codec_find_state_by_type(type);", "if (!MultibyteCodec_Check(state, codec))"), + ("mbstreamwriter_new(PyTypeObject *type", "if (!PyArg_ParseTupleAndKeywords", "codec = PyObject_GetAttrString", "state = _multibyte_codec_find_state_by_type(type);", "if (!MultibyteCodec_Check(state, codec))"), + ("_multibytecodec_MultibyteStreamWriter_write_impl(", "state = PyType_GetModuleState(cls);", "assert(state != NULL);", "if (mbstreamwriter_iwrite(self, strobj, state->str_write))"), + ("_multibytecodec_MultibyteStreamWriter_writelines_impl(", "if (!PySequence_Check(lines))", "state = PyType_GetModuleState(cls);", "assert(state != NULL);", "for (i = 0;"), + ("_multibytecodec_MultibyteStreamWriter_reset_impl(", "if (!self->pending)", "pwrt = multibytecodec_encode", "assert(PyBytes_Check(pwrt));", "state = PyType_GetModuleState(cls);"), + ("_multibytecodec___create_codec(PyObject *module, PyObject *arg)", "if (!PyCapsule_IsValid(arg,", "codec = PyCapsule_GetPointer", "codec->codecinit", "state = _multibytecodec_get_state(module);"), +) +for signature, *ordered in cases: + body = body_for(signature) + if body.count("_multibytecodec_state *state;") != 1: + raise SystemExit("module state local must be declared once at function scope: " + signature) + first_statement = body.index(ordered[0]) + if body.index("_multibytecodec_state *state;") > first_statement: + raise SystemExit("module state local must precede executable statements: " + signature) + if "_multibytecodec_state *state =" in body: + raise SystemExit("module state lookup must remain at its original execution point: " + signature) + positions = [body.index(marker) for marker in ordered] + if positions != sorted(positions): + raise SystemExit("module state lookup/order changed: " + signature) +module_start = source.index("static struct PyModuleDef _multibytecodecmodule =") +module_end = source.index("};", module_start) + 2 +module = source[module_start:module_end] +if any(field in module for field in (".m_base =", ".m_name =", ".m_doc =", ".m_size =", ".m_methods =", ".m_slots =", ".m_traverse =", ".m_clear =", ".m_free =")): + raise SystemExit("module definition must not use designated initializers") +normalized = " ".join(module.replace("\\\n", " ").split()) +expected = "static struct PyModuleDef _multibytecodecmodule = { PyModuleDef_HEAD_INIT, \"_multibytecodec\", NULL, sizeof(_multibytecodec_state), _multibytecodec_methods, _multibytecodec_slots, _multibytecodec_traverse, _multibytecodec_clear, _multibytecodec_free, };" +if normalized != expected: + raise SystemExit("module definition fields must match PyModuleDef order") +PY_MULTIBYTECODEC_STATE_TEST +then + pass "multibyte codec state/module initialization ordering passes" +else + bad "multibyte codec state/module initialization ordering passes" +fi + +# overlapped_dealloc keeps cancellation, buffer cleanup and type release order with a C89 local. +if python3 - "$REPO_ROOT/cpython/Modules/_winapi.c" <<'PY_WINAPI_OVERLAPPED_DEALLOC_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("overlapped_dealloc(OverlappedObject *self)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +body = source[opening:end] +if body.count("PyTypeObject *tp;") != 1 or body.index("PyTypeObject *tp;") > body.index("PyObject_GC_UnTrack(self);"): + raise SystemExit("overlapped type local must be declared before executable statements") +if "PyTypeObject *tp = Py_TYPE(self);" in body: + raise SystemExit("overlapped type lookup must remain after buffer cleanup") +markers = ("PyBuffer_Release(&self->write_buffer);", "Py_CLEAR(self->read_buffer);", "tp = Py_TYPE(self);", "tp->tp_free(self);", "Py_DECREF(tp);") +positions = [body.index(marker) for marker in markers] +if positions != sorted(positions): + raise SystemExit("overlapped deallocation must preserve buffer/type release order") +PY_WINAPI_OVERLAPPED_DEALLOC_TEST +then + pass "overlapped deallocation preserves cleanup order with C89 local" +else + bad "overlapped deallocation preserves cleanup order with C89 local" +fi + +# _winapi OVERLAPPED PyType_Spec uses C89-compatible positional fields. +if python3 - "$REPO_ROOT/cpython/Modules/_winapi.c" <<'PY_WINAPI_OVERLAPPED_SPEC_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("static PyType_Spec winapi_overlapped_type_spec = {") +end = source.index("};", start) + 2 +spec = source[start:end] +fields = (".name =", ".basicsize =", ".flags =", ".slots =") +if any(field in spec for field in fields): + raise SystemExit("OVERLAPPED PyType_Spec must not use designated initializers") +ordered = ("\"_winapi.Overlapped\"", "sizeof(OverlappedObject)", "0", "Py_TPFLAGS_DEFAULT", "winapi_overlapped_type_slots") +positions = [spec.index(field) for field in ordered] +if positions != sorted(positions): + raise SystemExit("OVERLAPPED PyType_Spec must follow name,size,itemsize,flags,slots order") +PY_WINAPI_OVERLAPPED_SPEC_TEST +then + pass "OVERLAPPED type specification uses positional C89 fields" +else + bad "OVERLAPPED type specification uses positional C89 fields" +fi + +# _winapi supplies exact legacy-SDK spellings without asserting Win9x API support. +if python3 - "$REPO_ROOT/cpython/Modules/_winapi.c" <<'PY_WINAPI_LEGACY_SDK_CONSTANTS_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +expected = """#if defined(_MSC_VER) && _MSC_VER < 1300 +# ifndef INVALID_FILE_ATTRIBUTES +# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) +# endif +# ifndef FSCTL_SET_REPARSE_POINT +# define FSCTL_SET_REPARSE_POINT 0x000900A4 +# endif +# ifndef LOCALE_NAME_INVARIANT +# define LOCALE_NAME_INVARIANT L"" +# endif +# ifndef LOCALE_NAME_MAX_LENGTH +# define LOCALE_NAME_MAX_LENGTH 85 +# endif +# ifndef LOCALE_NAME_SYSTEM_DEFAULT +# define LOCALE_NAME_SYSTEM_DEFAULT L"!sys-default-locale" +# endif +# ifndef LOCALE_NAME_USER_DEFAULT +# define LOCALE_NAME_USER_DEFAULT NULL +# endif +# ifndef LCMAP_SORTHANDLE +# define LCMAP_SORTHANDLE 0x20000000 +# endif +# ifndef LCMAP_HASH +# define LCMAP_HASH 0x00040000 +# endif +# ifndef LCMAP_TITLECASE +# define LCMAP_TITLECASE 0x00000300 +# endif +# ifndef FILE_FLAG_FIRST_PIPE_INSTANCE +# define FILE_FLAG_FIRST_PIPE_INSTANCE 0x00080000 +# endif +# ifndef FILE_MAP_EXECUTE +# define FILE_MAP_EXECUTE 0x0020 +# endif +# ifndef SEC_LARGE_PAGES +# define SEC_LARGE_PAGES 0x80000000 +# endif +# ifndef SEC_WRITECOMBINE +# define SEC_WRITECOMBINE 0x40000000 +# endif +# ifndef ABOVE_NORMAL_PRIORITY_CLASS +# define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000 +# endif +# ifndef BELOW_NORMAL_PRIORITY_CLASS +# define BELOW_NORMAL_PRIORITY_CLASS 0x00004000 +# endif +# ifndef CREATE_BREAKAWAY_FROM_JOB +# define CREATE_BREAKAWAY_FROM_JOB 0x01000000 +# endif +#endif""" +if expected not in source: + raise SystemExit("_winapi legacy SDK constants must retain exact documented values") +PY_WINAPI_LEGACY_SDK_CONSTANTS_TEST +then + pass "_winapi legacy SDK constants match documented values" +else + bad "_winapi legacy SDK constants match documented values" +fi + +# sort_environment_keys hoists C89 locals but keeps callable and argument initialization at the same points. +if python3 - "$REPO_ROOT/cpython/Modules/_winapi.c" <<'PY_WINAPI_SORT_ENVIRONMENT_KEYS_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("sort_environment_keys(PyObject *keys)") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +body = source[opening:end] +for declaration in ("PyObject *keyfunc;", "PyObject *kwnames;", "PyObject *args[2];", "PyObject *ret;"): + if body.count(declaration) != 1 or body.index(declaration) > body.index("keyfunc = PyCFunction_New"): + raise SystemExit("sort_environment_keys locals must be declared before executable statements") +markers = ("keyfunc = PyCFunction_New", "if (keyfunc == NULL)", "kwnames = Py_BuildValue", "if (kwnames == NULL)", "args[0] = keys;", "args[1] = keyfunc;", "ret = PyObject_VectorcallMethod", "if (ret == NULL)", "Py_DECREF(ret);") +positions = [body.index(marker) for marker in markers] +cleanup_positions = (body.rindex("Py_DECREF(keyfunc);"), body.rindex("Py_DECREF(kwnames);")) +if positions != sorted(positions) or not (positions[6] < cleanup_positions[0] < cleanup_positions[1] < positions[7]): + raise SystemExit("sort_environment_keys must preserve construction and cleanup order") +PY_WINAPI_SORT_ENVIRONMENT_KEYS_TEST +then + pass "sort_environment_keys preserves call and cleanup order with C89 locals" +else + bad "sort_environment_keys preserves call and cleanup order with C89 locals" +fi + +# _winapi environment helpers use C89 declarations without moving cleanup points. +if python3 - "$REPO_ROOT/cpython/Modules/_winapi.c" <<'PY_WINAPI_ENV_HELPERS_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +def body_for(signature): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + return source[opening:end] +compare = body_for("compare_string_ordinal(PyObject *str1") +for declaration in ("wchar_t *s1;", "wchar_t *s2;"): + if compare.count(declaration) != 1 or compare.index(declaration) > compare.index("s1 = PyUnicode_AsWideCharString"): + raise SystemExit("ordinal-comparison buffers must be declared before lookup") +order = (compare.index("s1 = PyUnicode_AsWideCharString"), compare.index("if (s1 == NULL)"), compare.index("s2 = PyUnicode_AsWideCharString"), compare.index("if (s2 == NULL)"), compare.index("CompareStringOrdinal"), compare.rindex("PyMem_Free(s1);"), compare.index("PyMem_Free(s2);")) +if order != tuple(sorted(order)): + raise SystemExit("ordinal-comparison lookup and cleanup order changed") +dedup = body_for("dedup_environment_keys(PyObject *keys)") +for declaration in ("PyObject *result;", "Py_ssize_t i;"): + if dedup.count(declaration) != 1 or dedup.index(declaration) > dedup.index("result = PyList_New(0)"): + raise SystemExit("dedup locals must be declared before list creation") +if "for (Py_ssize_t i =" in dedup: + raise SystemExit("dedup loop index must be declared in C89 order") +loop = dedup[dedup.index("for (i =") :] +for declaration in ("PyObject *key;", "PyObject *next_key;", "int compare_result;"): + if loop.count(declaration) != 1 or loop.index(declaration) > loop.index("key = PyList_GET_ITEM"): + raise SystemExit("dedup loop locals must be declared before loop statements") +if "PyObject *next_key =" in loop: + raise SystemExit("dedup next-key lookup must remain an assignment at its original point") +normalize = body_for("normalize_environment(PyObject *environment)") +for declaration in ("PyObject *keys;", "PyObject *normalized_keys;", "PyObject *result;", "PyObject *key;", "PyObject *value;", "int i;", "int ret;"): + if normalize.count(declaration) != 1 or normalize.index(declaration) > normalize.index("keys = PyMapping_Keys(environment)"): + raise SystemExit("normalization locals must be declared before mapping access") +if "for (int i =" in normalize or "PyObject *normalized_keys =" in normalize or "PyObject *result =" in normalize: + raise SystemExit("normalize_environment retains a late C99 declaration") +PY_WINAPI_ENV_HELPERS_TEST +then + pass "_winapi environment helpers satisfy C89 declaration placement" +else + bad "_winapi environment helpers satisfy C89 declaration placement" +fi + +# VC6 routes CreateProcess through STARTUPINFOW and rejects Vista-only attribute lists safely. +if python3 - "$REPO_ROOT/cpython/Modules/_winapi.c" <<'PY_WINAPI_LEGACY_CREATEPROCESS_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +marker = "/* Vista-only process/thread attribute APIs are unavailable in VC6. */" +comment = source.index(marker) +start = source.rfind("#if defined(_MSC_VER) && _MSC_VER < 1300", 0, comment) +modern = source.index("#else", comment) +legacy = source[start:modern] +if any(name in legacy for name in ("LPPROC_THREAD_ATTRIBUTE_LIST", "InitializeProcThreadAttributeList", "UpdateProcThreadAttribute", "DeleteProcThreadAttributeList", "STARTUPINFOEXW")): + raise SystemExit("VC6 branch must not reference Vista-only process attribute APIs") +if "PyErr_SetFromWindowsErr(ERROR_CALL_NOT_IMPLEMENTED)" not in legacy: + raise SystemExit("non-None startup attribute lists must fail explicitly on the legacy branch") +body_start = source.index("_winapi_CreateProcess_impl(") +opening = source.index("{", body_start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +body = source[opening:end] +startup_pair = chr(10).join(("#if defined(_MSC_VER) && _MSC_VER < 1300", " STARTUPINFOW si;", "#else", " STARTUPINFOEXW si;", "#endif")) +if startup_pair not in body or "STARTUPINFOW *si_base;" not in body: + raise SystemExit("CreateProcess must select the correct startup structure and base pointer") +call_start = body.index("Py_BEGIN_ALLOW_THREADS") +call_end = body.index("Py_END_ALLOW_THREADS", call_start) +call = body[call_start:call_end] +if "#if defined(_MSC_VER) && _MSC_VER < 1300" not in call or "EXTENDED_STARTUPINFO_PRESENT" not in call: + raise SystemExit("CreateProcess must use extended flags only in the modern branch") +PY_WINAPI_LEGACY_CREATEPROCESS_TEST +then + pass "VC6 CreateProcess avoids Vista-only startup attribute APIs" +else + bad "VC6 CreateProcess avoids Vista-only startup attribute APIs" +fi + +# _winapi_LCMapStringEx_impl keeps validation, allocation and error cleanup order with C89 locals. +if python3 - "$REPO_ROOT/cpython/Modules/_winapi.c" <<'PY_WINAPI_LCMAP_C89_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +start = source.index("_winapi_LCMapStringEx_impl(PyObject *module") +opening = source.index("{", start) +end = source.index(chr(10) + "}" + chr(10), opening) + 2 +body = source[opening:end] +first_statement = body.index("if (flags &") +for declaration in ("wchar_t *locale_;", "Py_ssize_t src_size;", "wchar_t *src_;", "int dest_size;", "wchar_t *dest;", "int nmapped;", "PyObject *ret;"): + if body.count(declaration) != 1 or body.index(declaration) > first_statement: + raise SystemExit("LCMapStringEx locals must be declared before validation") +markers = ("if (flags &", "locale_ = PyUnicode_AsWideCharString", "if (!locale_)", "src_ = PyUnicode_AsWideCharString", "if (!src_)", "if (src_size > INT_MAX)", "dest_size = LCMapStringEx", "dest = PyMem_NEW", "nmapped = LCMapStringEx", "ret = PyUnicode_FromWideChar") +positions = [body.index(marker) for marker in markers] +if positions != sorted(positions) or positions[-1] > body.rindex("PyMem_DEL(dest)"): + raise SystemExit("LCMapStringEx validation/allocation order changed") +PY_WINAPI_LCMAP_C89_TEST +then + pass "LCMapStringEx declaration order preserves validation and cleanup" +else + bad "LCMapStringEx declaration order preserves validation and cleanup" +fi + +# _winapi registry traversal and module setup use C89 locals and the PyModuleDef field order. +if python3 - "$REPO_ROOT/cpython/Modules/_winapi.c" <<'PY_WINAPI_REGISTRY_MODULE_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +def body_for(signature): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + return source[opening:end] +registry = body_for("_winapi__mimetypes_read_windows_registry_impl(") +for declaration in ("DWORD i;", "int j;"): + if registry.count(declaration) != 1 or registry.index(declaration) > registry.index("err = RegOpenKeyExW"): + raise SystemExit("registry traversal indices must be declared before registry calls") +if "for (DWORD i =" in registry or "for (int j =" in registry or registry.count("for (j = 0; j < entry; ++j)") != 2: + raise SystemExit("registry traversal loops must use the hoisted C89 indices") +if not (registry.index("Py_BLOCK_THREADS") < registry.index("for (j =") < registry.index("Py_UNBLOCK_THREADS") < registry.rindex("for (j =")): + raise SystemExit("registry callback thread-state boundaries changed") +exec_body = body_for("winapi_exec(PyObject *m)") +for declaration in ("WinApiState *st;", "PyObject *d;"): + if exec_body.count(declaration) != 1 or exec_body.index(declaration) > exec_body.index("st = winapi_get_state(m)"): + raise SystemExit("module exec locals must be declared before initialization") +if not (exec_body.index("PyModule_AddType(m, st->overlapped_type)") < exec_body.index("d = PyModule_GetDict(m)")): + raise SystemExit("module dictionary lookup must remain after type registration") +start = source.index("static struct PyModuleDef winapi_module = {") +end = source.index("};", start) + 2 +normalized = " ".join(source[start:end].replace(chr(92) + chr(10), " ").split()) +expected = "static struct PyModuleDef winapi_module = { PyModuleDef_HEAD_INIT, \"_winapi\", NULL, sizeof(WinApiState), winapi_functions, winapi_slots, winapi_traverse, winapi_clear, winapi_free, };" +if normalized != expected: + raise SystemExit("_winapi PyModuleDef must use the verified positional field order") +PY_WINAPI_REGISTRY_MODULE_TEST +then + pass "_winapi registry and module setup follow C89/layout rules" +else + bad "_winapi registry and module setup follow C89/layout rules" +fi + +# Core object access helpers hoist C89 method-table/result locals without moving slot calls. +if python3 - "$REPO_ROOT/cpython/Objects/abstract.c" <<'PY_ABSTRACT_ACCESSOR_C89_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +def body_for(signature): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + return source[opening:end] +checks = ( + ("PyObject_Size(PyObject *o)", ("PySequenceMethods *m;",), "if (o == NULL)", "m = Py_TYPE(o)->tp_as_sequence;", "if (m && m->sq_length)"), + ("PyObject_GetItem(PyObject *o, PyObject *key)", ("PyMappingMethods *m;", "PySequenceMethods *ms;"), "if (o == NULL || key == NULL)", "m = Py_TYPE(o)->tp_as_mapping;", "if (m && m->mp_subscript)", "ms = Py_TYPE(o)->tp_as_sequence;", "if (ms && ms->sq_item)"), + ("PyObject_SetItem(PyObject *o, PyObject *key, PyObject *value)", ("PyMappingMethods *m;",), "if (o == NULL || key == NULL || value == NULL)", "m = Py_TYPE(o)->tp_as_mapping;", "if (m && m->mp_ass_subscript)"), + ("PyObject_DelItem(PyObject *o, PyObject *key)", ("PyMappingMethods *m;",), "if (o == NULL || key == NULL)", "m = Py_TYPE(o)->tp_as_mapping;", "if (m && m->mp_ass_subscript)"), + ("PyObject_GetBuffer(PyObject *obj", ("int res;",), "if (pb == NULL", "res = (*pb->bf_getbuffer)", "assert(_Py_CheckSlotResult"), +) +for item in checks: + signature, declarations, *markers = item + body = body_for(signature) + first = markers[0] + for declaration in declarations: + if body.count(declaration) != 1 or body.index(declaration) > body.index(first): + raise SystemExit(signature + " local declarations must precede statements") + positions = [body.index(marker) for marker in markers] + if positions != sorted(positions): + raise SystemExit(signature + " null guards/slot calls changed order") +if "PyMappingMethods *m =" in body_for("PyObject_SetItem(PyObject *o, PyObject *key, PyObject *value)"): + raise SystemExit("PyObject_SetItem retains a late initialized method-table local") +PY_ABSTRACT_ACCESSOR_C89_TEST +then + pass "core object access helpers preserve slot order with C89 locals" +else + bad "core object access helpers preserve slot order with C89 locals" +fi + +# Numeric dispatch helpers hoist late method-table/slot locals without reordering dispatch. +if python3 - "$REPO_ROOT/cpython/Objects/abstract.c" <<'PY_ABSTRACT_NUMERIC_C89_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +def body_for(signature): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + return source[opening:end] +def require_before(body, declaration, marker): + if body.count(declaration) != 1 or body.index(declaration) > body.index(marker): + raise SystemExit(declaration + " must be a C89 local before " + marker) +b = body_for("PyNumber_Check(PyObject *o)") +require_before(b, "PyNumberMethods *nb;", "if (o == NULL)") +if not (b.index("if (o == NULL)") < b.index("nb = Py_TYPE(o)->tp_as_number;") < b.index("return nb &&")): + raise SystemExit("PyNumber_Check changed null/slot evaluation order") +b = body_for("binary_op1(PyObject *v, PyObject *w, const int op_slot") +for d in ("binaryfunc slotv;", "binaryfunc slotw;"): + require_before(b, d, "if (Py_TYPE(v)->tp_as_number") +if "binaryfunc slotw;" in b[b.index("if (Py_TYPE(v)->tp_as_number"):]: + raise SystemExit("binary_op1 retains a late slotw declaration") +b = body_for("ternary_op(PyObject *v,") +for d in ("PyNumberMethods *mv;", "PyNumberMethods *mw;", "PyNumberMethods *mz;", "ternaryfunc slotv;", "ternaryfunc slotw;"): + require_before(b, d, "mv = Py_TYPE(v)->tp_as_number;") +if not (b.index("mv = Py_TYPE(v)->tp_as_number;") < b.index("mw = Py_TYPE(w)->tp_as_number;") < b.index("if (mv != NULL)") < b.index("slotv = NB_TERNOP") < b.index("slotw = NB_TERNOP") < b.index("mz = Py_TYPE(z)->tp_as_number;")): + raise SystemExit("ternary_op changed slot lookup order") +b = body_for("PyNumber_Add(PyObject *v, PyObject *w)") +require_before(b, "PySequenceMethods *m;", "PyObject *result") +if not (b.index("PyObject *result = BINARY_OP1") < b.index("Py_DECREF(result);") < b.index("m = Py_TYPE(v)->tp_as_sequence;") < b.index("if (m && m->sq_concat)")): + raise SystemExit("PyNumber_Add changed binary-then-sequence fallback order") +b = body_for("sequence_repeat(ssizeargfunc repeatfunc") +require_before(b, "PyObject *res;", "if (_PyIndex_Check(n))") +if not (b.index("count = PyNumber_AsSsize_t") < b.index("res = (*repeatfunc)(seq, count);") < b.index("return res;")): + raise SystemExit("sequence_repeat changed conversion/call order") +for signature, slot in (("PyNumber_Negative(PyObject *o)", "nb_negative"), ("PyNumber_Positive(PyObject *o)", "nb_positive"), ("PyNumber_Invert(PyObject *o)", "nb_invert"), ("PyNumber_Absolute(PyObject *o)", "nb_absolute")): + b = body_for(signature) + require_before(b, "PyNumberMethods *m;", "if (o == NULL)") + if not (b.index("if (o == NULL)") < b.index("m = Py_TYPE(o)->tp_as_number;") < b.index("if (m && m->" + slot + ")")): + raise SystemExit(signature + " changed null/slot dispatch order") +b = body_for("_PyNumber_Index(PyObject *item)") +require_before(b, "PyObject *result;", "if (item == NULL)") +if not (b.index("if (!_PyIndex_Check(item))") < b.index("result = Py_TYPE(item)->tp_as_number->nb_index(item);") < b.index("assert(_Py_CheckSlotResult(item, \"__index__\"")): + raise SystemExit("_PyNumber_Index changed validation/call order") +b = body_for("PyNumber_AsSsize_t(PyObject *item, PyObject *err)") +require_before(b, "PyThreadState *tstate;", "Py_ssize_t result;") +if not (b.index("if (result != -1)") < b.index("tstate = _PyThreadState_GET();") < b.index("runerr = _PyErr_Occurred(tstate);")): + raise SystemExit("PyNumber_AsSsize_t changed overflow handling order") +b = body_for("PyNumber_Float(PyObject *o)") +for declaration in ("PyNumberMethods *m;", "double val;"): + require_before(b, declaration, "if (o == NULL)") +if not (b.index("if (o == NULL)") < b.index("if (PyFloat_CheckExact(o))") < b.index("m = Py_TYPE(o)->tp_as_number;") < b.index("if (m && m->nb_float)") < b.index("val = PyFloat_AS_DOUBLE(res);") < b.index("Py_DECREF(res);") < b.index("if (m && m->nb_index)") < b.index("val = PyLong_AsDouble(res);")): + raise SystemExit("PyNumber_Float changed slot/fallback conversion order") +b = body_for("PyNumber_ToBase(PyObject *n, int base)") +for declaration in ("PyObject *index;", "PyObject *res;"): + require_before(b, declaration, "if (!(base ==") +if not (b.index("if (!(base ==") < b.index("index = _PyNumber_Index(n);") < b.index("res = _PyLong_Format(index, base);") < b.index("Py_DECREF(index);") < b.index("return res;")): + raise SystemExit("PyNumber_ToBase changed validation/ownership order") +PY_ABSTRACT_NUMERIC_C89_TEST +then + pass "numeric dispatch helpers preserve slot order with C89 locals" +else + bad "numeric dispatch helpers preserve slot order with C89 locals" +fi + +# Sequence operation fallbacks retain null-check and slot-priority order after local hoisting. +if python3 - "$REPO_ROOT/cpython/Objects/abstract.c" <<'PY_ABSTRACT_SEQUENCE_C89_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +def body_for(signature): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + return source[opening:end] +checks = ( + ("PySequence_Size(PyObject *s)", "if (s == NULL)", "m = Py_TYPE(s)->tp_as_sequence;", "if (m && m->sq_length)"), + ("PySequence_Concat(PyObject *s, PyObject *o)", "if (s == NULL || o == NULL)", "m = Py_TYPE(s)->tp_as_sequence;", "if (m && m->sq_concat)"), + ("PySequence_Repeat(PyObject *o, Py_ssize_t count)", "if (o == NULL)", "m = Py_TYPE(o)->tp_as_sequence;", "if (m && m->sq_repeat)"), + ("PySequence_InPlaceConcat(PyObject *s, PyObject *o)", "if (s == NULL || o == NULL)", "m = Py_TYPE(s)->tp_as_sequence;", "if (m && m->sq_inplace_concat)"), + ("PySequence_InPlaceRepeat(PyObject *o, Py_ssize_t count)", "if (o == NULL)", "m = Py_TYPE(o)->tp_as_sequence;", "if (m && m->sq_inplace_repeat)"), +) +for signature, null_check, assignment, first_slot in checks: + body = body_for(signature) + if body.count("PySequenceMethods *m;") != 1 or body.index("PySequenceMethods *m;") > body.index(null_check): + raise SystemExit(signature + " must declare m at function entry") + if not (body.index(null_check) < body.index(assignment) < body.index(first_slot)): + raise SystemExit(signature + " changed null-check or slot-dispatch order") + if "PySequenceMethods *m =" in body: + raise SystemExit(signature + " retains a late initialized local") +more_checks = ( + ("PySequence_GetItem(PyObject *s, Py_ssize_t i)", ("PySequenceMethods *m;", "PyObject *res;"), "if (s == NULL)", "m = Py_TYPE(s)->tp_as_sequence;", "if (m && m->sq_item)", "res = m->sq_item(s, i);", "assert(_Py_CheckSlotResult(s, \"__getitem__\""), + ("PySequence_GetSlice(PyObject *s, Py_ssize_t i1, Py_ssize_t i2)", ("PyMappingMethods *mp;", "PyObject *slice;", "PyObject *res;"), "if (!s)", "mp = Py_TYPE(s)->tp_as_mapping;", "if (mp && mp->mp_subscript)", "slice = _PySlice_FromIndices(i1, i2);", "res = mp->mp_subscript(s, slice);", "Py_DECREF(slice);"), + ("PySequence_SetItem(PyObject *s, Py_ssize_t i, PyObject *o)", ("PySequenceMethods *m;", "int res;"), "if (s == NULL)", "m = Py_TYPE(s)->tp_as_sequence;", "if (m && m->sq_ass_item)", "res = m->sq_ass_item(s, i, o);", "assert(_Py_CheckSlotResult(s, \"__setitem__\""), + ("PySequence_DelItem(PyObject *s, Py_ssize_t i)", ("PySequenceMethods *m;", "int res;"), "if (s == NULL)", "m = Py_TYPE(s)->tp_as_sequence;", "if (m && m->sq_ass_item)", "res = m->sq_ass_item(s, i, (PyObject *)NULL);", "assert(_Py_CheckSlotResult(s, \"__delitem__\""), + ("PySequence_SetSlice(PyObject *s, Py_ssize_t i1, Py_ssize_t i2, PyObject *o)", ("PyMappingMethods *mp;", "PyObject *slice;", "int res;"), "if (s == NULL)", "mp = Py_TYPE(s)->tp_as_mapping;", "if (mp && mp->mp_ass_subscript)", "slice = _PySlice_FromIndices(i1, i2);", "res = mp->mp_ass_subscript(s, slice, o);", "Py_DECREF(slice);"), + ("PySequence_DelSlice(PyObject *s, Py_ssize_t i1, Py_ssize_t i2)", ("PyMappingMethods *mp;", "PyObject *slice;", "int res;"), "if (s == NULL)", "mp = Py_TYPE(s)->tp_as_mapping;", "if (mp && mp->mp_ass_subscript)", "slice = _PySlice_FromIndices(i1, i2);", "res = mp->mp_ass_subscript(s, slice, NULL);", "Py_DECREF(slice);"), +) +for item in more_checks: + signature, declarations, *markers = item + body = body_for(signature) + for declaration in declarations: + if body.count(declaration) != 1 or body.index(declaration) > body.index(markers[0]): + raise SystemExit(signature + " declarations must precede the null check") + positions = [body.index(marker) for marker in markers] + if positions != sorted(positions): + raise SystemExit(signature + " changed bounds/slot/ownership order") +PY_ABSTRACT_SEQUENCE_C89_TEST +then + pass "sequence fallbacks preserve null and slot order with C89 locals" +else + bad "sequence fallbacks preserve null and slot order with C89 locals" +fi + +# Sequence/mapping containment preserves the slot-first fast path and fallback order. +if python3 - "$REPO_ROOT/cpython/Objects/abstract.c" <<'PY_ABSTRACT_CONTAINS_MAPPING_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +def body_for(signature): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + return source[opening:end] +b = body_for("PySequence_Contains(PyObject *seq, PyObject *ob)") +if b.count("Py_ssize_t result;") != 1 or b.index("Py_ssize_t result;") > b.index("PySequenceMethods *sqm ="): + raise SystemExit("PySequence_Contains result must be declared at entry") +if not (b.index("if (sqm != NULL && sqm->sq_contains != NULL)") < b.index("int res = (*sqm->sq_contains)(seq, ob)") < b.index("result = _PySequence_IterSearch(seq, ob, PY_ITERSEARCH_CONTAINS);") < b.index("return Py_SAFE_DOWNCAST(result")): + raise SystemExit("PySequence_Contains changed slot/fallback order") +b = body_for("PyMapping_Size(PyObject *o)") +if b.count("PyMappingMethods *m;") != 1 or b.index("PyMappingMethods *m;") > b.index("if (o == NULL)"): + raise SystemExit("PyMapping_Size method table must be declared at entry") +if not (b.index("if (o == NULL)") < b.index("m = Py_TYPE(o)->tp_as_mapping;") < b.index("if (m && m->mp_length)") < b.index("if (Py_TYPE(o)->tp_as_sequence")): + raise SystemExit("PyMapping_Size changed null/mapping/sequence fallback order") +PY_ABSTRACT_CONTAINS_MAPPING_TEST +then + pass "sequence containment and mapping size preserve fallback order" +else + bad "sequence containment and mapping size preserve fallback order" +fi + +# Recursive isinstance/issubclass and async iteration hoist locals without changing recursion or ownership order. +if python3 - "$REPO_ROOT/cpython/Objects/abstract.c" <<'PY_ABSTRACT_RECURSIVE_C89_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +def body_for(signature): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + return source[opening:end] +for signature, entry_marker in (("object_recursive_isinstance(PyThreadState *tstate", "if (Py_IS_TYPE(inst, (PyTypeObject *)cls))"), ("object_issubclass(PyThreadState *tstate", "if (PyType_CheckExact(cls))")): + body = body_for(signature) + for declaration in ("Py_ssize_t n;", "Py_ssize_t i;", "int r;", "PyObject *item;", "PyObject *res;", "int ok;"): + if body.count(declaration) != 1 or body.index(declaration) > body.index(entry_marker): + raise SystemExit(signature + " locals must be declared at function entry") + if "for (Py_ssize_t i =" in body: + raise SystemExit(signature + " retains a C99 loop declaration") + if not (body.index("if (PyTuple_Check(cls))") < body.index("_Py_EnterRecursiveCallTstate") < body.index("n = PyTuple_GET_SIZE(cls);") < body.index("r = 0;") < body.index("for (i = 0;") < body.index("item = PyTuple_GET_ITEM(cls, i);") < body.index("_Py_LeaveRecursiveCallTstate(tstate);")): + raise SystemExit(signature + " changed recursion guard/tuple traversal order") +body = body_for("PyObject_GetAIter(PyObject *o)") +for declaration in ("PyTypeObject *t = Py_TYPE(o);", "unaryfunc f;", "PyObject *it;"): + if body.count(declaration) != 1 or body.index(declaration) > body.index("if (t->tp_as_async == NULL"): + raise SystemExit("PyObject_GetAIter declarations must precede the async-slot guard") +if not (body.index("f = t->tp_as_async->am_aiter;") < body.index("it = (*f)(o);") < body.index("if (it != NULL && !PyAIter_Check(it))") < body.index("return it;")): + raise SystemExit("PyObject_GetAIter changed call/validation order") +PY_ABSTRACT_RECURSIVE_C89_TEST +then + pass "recursive type checks and async iteration preserve C89 control flow" +else + bad "recursive type checks and async iteration preserve C89 control flow" +fi + +# bool_vectorcall and PyBool_Type remain C89/VC6 compatible without changing vectorcall slot placement. +if python3 - "$REPO_ROOT/cpython/Objects/boolobject.c" <<'PY_BOOLOBJECT_C89_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +def body_for(signature): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + return source[opening:end] +body = body_for("bool_vectorcall(PyObject *type") +if body.count("Py_ssize_t nargs;") != 1 or body.index("Py_ssize_t nargs;") > body.index("if (!_PyArg_NoKwnames"): + raise SystemExit("bool_vectorcall nargs must be declared before statements") +if not (body.index("if (!_PyArg_NoKwnames") < body.index("nargs = PyVectorcall_NARGS(nargsf);") < body.index("if (!_PyArg_CheckPositional")): + raise SystemExit("bool_vectorcall changed keyword/positional validation order") +type_start = source.index("PyTypeObject PyBool_Type = {") +type_end = source.index("\n};", type_start) + 3 +type_body = " ".join(source[type_start:type_end].split()) +expected = "bool_new, /* tp_new */ 0, /* tp_free */ 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 */ bool_vectorcall, /* tp_vectorcall */ }" +if ".tp_vectorcall =" in type_body or expected not in type_body: + raise SystemExit("PyBool_Type must use positional fields through tp_vectorcall") +PY_BOOLOBJECT_C89_TEST +then + pass "bool type preserves vectorcall field placement with C89 initializers" +else + bad "bool type preserves vectorcall field placement with C89 initializers" +fi + +# bytearray Clinic wrappers declare UTF-8 lengths before validation and convert at the same points. +if python3 - "$REPO_ROOT/cpython/Objects/clinic/bytearrayobject.c.h" <<'PY_BYTEARRAY_CLINIC_C89_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +def body_for(signature): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + return source[opening:end] +checks = (("bytearray___init__(PyObject *self, PyObject *args", "fastargs = _PyArg_UnpackKeywords", "fastargs[1]", "encoding_length", "encoding = PyUnicode_AsUTF8AndSize(fastargs[1], &encoding_length);", "fastargs[2]", "errors_length", "errors = PyUnicode_AsUTF8AndSize(fastargs[2], &errors_length);"), ("bytearray_decode(PyByteArrayObject *self", "args = _PyArg_UnpackKeywords", "args[0]", "encoding_length", "encoding = PyUnicode_AsUTF8AndSize(args[0], &encoding_length);", "args[1]", "errors_length", "errors = PyUnicode_AsUTF8AndSize(args[1], &errors_length);")) +for signature, first_call, encoding_check, encoding_len, encoding_call, errors_check, errors_len, errors_call in checks: + body = body_for(signature) + for declaration in ("Py_ssize_t " + encoding_len + ";", "Py_ssize_t " + errors_len + ";"): + if body.count(declaration) != 1 or body.index(declaration) > body.index(first_call): + raise SystemExit(signature + " length locals must precede statements") + positions = [body.index(x) for x in (first_call, encoding_check, encoding_call, errors_check, errors_call)] + if positions != sorted(positions): + raise SystemExit(signature + " changed argument validation/conversion order") +PY_BYTEARRAY_CLINIC_C89_TEST +then + pass "bytearray Clinic wrappers preserve UTF-8 validation order with C89 locals" +else + bad "bytearray Clinic wrappers preserve UTF-8 validation order with C89 locals" +fi + +# bytearray repeat and slice-assignment locals move to C89 declaration groups without moving operations. +if python3 - "$REPO_ROOT/cpython/Objects/bytearrayobject.c" <<'PY_BYTEARRAY_CORE_C89_TEST' +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +def body_for(signature): + start = source.index(signature) + opening = source.index("{", start) + end = source.index(chr(10) + "}" + chr(10), opening) + 2 + return source[opening:end] +checks = (("bytearray_repeat(PyByteArrayObject *self, Py_ssize_t count)", ("Py_ssize_t mysize;", "Py_ssize_t size;", "PyByteArrayObject *result;", "const char *buf;"), ("if (count < 0)", "mysize = Py_SIZE(self);", "if (count > 0 && mysize > PY_SSIZE_T_MAX / count)", "size = mysize * count;", "result = (PyByteArrayObject *)PyByteArray_FromStringAndSize(NULL, size);", "buf = PyByteArray_AS_STRING(self);")), ("bytearray_irepeat(PyByteArrayObject *self, Py_ssize_t count)", ("Py_ssize_t mysize;", "Py_ssize_t size;", "char *buf;"), ("if (count < 0)", "mysize = Py_SIZE(self);", "if (count > 0 && mysize > PY_SSIZE_T_MAX / count)", "size = mysize * count;", "if (PyByteArray_Resize((PyObject *)self, size) < 0)", "buf = PyByteArray_AS_STRING(self);"))) +for signature, declarations, markers in checks: + body = body_for(signature) + for declaration in declarations: + if body.count(declaration) != 1 or body.index(declaration) > body.index(markers[0]): + raise SystemExit(signature + " locals must precede statements") + positions = [body.index(marker) for marker in markers] + if positions != sorted(positions): + raise SystemExit(signature + " changed overflow/resize/copy order") +body = body_for("bytearray_ass_subscript(PyByteArrayObject *self") +if body.count("int ival;") != 1 or body.index("int ival;") > body.index("buf = PyByteArray_AS_STRING(self);"): + raise SystemExit("bytearray_ass_subscript value local must be declared at entry") +if not (body.index("Py_ssize_t i = PyNumber_AsSsize_t") < body.index("ival = -1;") < body.index("if (values && !_getbytevalue(values, &ival))")): + raise SystemExit("bytearray_ass_subscript changed index/value conversion order") +body = body_for("bytearray___init___impl(PyByteArrayObject *self") +start = body.index("if (PyList_CheckExact(arg) || PyTuple_CheckExact(arg)) {") +end = body.index("\n }\nslowpath:", start) + 6 +block = body[start:end] +for declaration in ("Py_ssize_t size;", "PyObject **items;", "char *s;", "Py_ssize_t i;"): + if block.count(declaration) != 1 or block.index(declaration) > block.index("size = PySequence_Fast_GET_SIZE(arg);"): + raise SystemExit("bytearray fast list locals must precede the resize") +if "for (Py_ssize_t i =" in block: + raise SystemExit("bytearray fast list retains a C99 loop declaration") +if not (block.index("size = PySequence_Fast_GET_SIZE(arg);") < block.index("PyByteArray_Resize((PyObject *)self, size)") < block.index("items = PySequence_Fast_ITEMS(arg);") < block.index("s = PyByteArray_AS_STRING(self);") < block.index("for (i = 0;") < block.index("int value;") < block.index("int rc;") < block.index("rc = _getbytevalue(items[i], &value);") < block.index("s[i] = value;")): + raise SystemExit("bytearray fast list changed resize/validation/copy order") +PY_BYTEARRAY_CORE_C89_TEST +then + pass "bytearray repeat/slice helpers preserve operation order with C89 locals" +else + bad "bytearray repeat/slice helpers preserve operation order with C89 locals" +fi + +# fastsearch debug logging uses fixed-arity macros compatible with the VC6 preprocessor. +if python3 - "$REPO_ROOT/cpython/Objects/stringlib/fastsearch.h" <<'PY_FASTSEARCH_VC6_MACRO_TEST' +import re +import sys +from pathlib import Path +source = Path(sys.argv[1]).read_text() +if "# define LOG(args) printf args" not in source or "# define LOG(args) ((void)0)" not in source: + raise SystemExit("fastsearch LOG must use fixed-arity debug/no-op definitions") +if re.search(r"^\s*#\s*define\s+LOG\s*\(\s*\.\.\.", source, re.MULTILINE) or "__VA_ARGS__" in source: + raise SystemExit("fastsearch must not use variadic macros") +for line in source.splitlines(): + if re.search(r"\bLOG\(", line) and not re.search(r"#\s*define\s+LOG\(", line) and "LOG((" not in line: + raise SystemExit("fastsearch LOG invocation must parenthesize its argument tuple") +body_start = source.index("STRINGLIB(_preprocess)(const STRINGLIB_CHAR *needle") +body_open = source.index("{", body_start) +body_end = source.index(chr(10) + "}" + chr(10), body_open) + 2 +body = source[body_open:body_end] +for declaration in ("Py_ssize_t i;", "STRINGLIB_CHAR last;", "Py_ssize_t not_found_shift;"): + if body.count(declaration) != 1 or body.index(declaration) > body.index("p->needle = needle;" ): + raise SystemExit("fastsearch preprocessing locals must be declared at function entry") +if "for (Py_ssize_t i =" in body: + raise SystemExit("fastsearch preprocessing retains C99 loop declarations") +if not (body.index("p->gap = len_needle;") < body.index("last = needle[len_needle - 1] & TABLE_MASK;") < body.index("for (i = len_needle - 2;") < body.index("not_found_shift = Py_MIN(len_needle, MAX_SHIFT);") < body.index("for (i = 0;") < body.index("for (i = len_needle - not_found_shift;")): + raise SystemExit("fastsearch preprocessing changed table construction order") +body_start = source.index("STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack") +body_open = source.index("{", body_start) +body_end = source.index(chr(10) + "}" + chr(10), body_open) + 2 +body = source[body_open:body_end] +for declaration in ("Py_ssize_t memory;", "Py_ssize_t i;", "Py_ssize_t shift;", "Py_ssize_t gap_jump_end;", "Py_ssize_t mem_jump;"): + if body.count(declaration) != 1 or body.index(declaration) > body.index("LOG((" ): + raise SystemExit("fastsearch two-way locals must precede logging and algorithm statements") +if "for (Py_ssize_t i =" in body: + raise SystemExit("fastsearch two-way search retains C99 loop declarations") +if not (body.index("if (p->is_periodic)") < body.index("memory = 0;") < body.index("for (;;)") < body.index("shift = table[") < body.index("window_last += shift;") < body.index("i = Py_MAX(cut, memory);") < body.index("if (needle[i] != window[i])")): + raise SystemExit("fastsearch periodic search changed memory/shift/comparison order") +if not (body.index("gap_jump_end = Py_MIN(len_needle, cut + gap);") < body.index("for (i = cut;") < body.index("for (i = gap_jump_end;") < body.index("for (i = 0; i < cut;")): + raise SystemExit("fastsearch nonperiodic mismatch order changed") +body_start = source.index("STRINGLIB(_two_way_find)(const STRINGLIB_CHAR *haystack") +body_open = source.index("{", body_start) +body_end = source.index(chr(10) + "}" + chr(10), body_open) + 2 +body = source[body_open:body_end] +if body.count("STRINGLIB(prework) p;") != 1 or body.index("STRINGLIB(prework) p;") > body.index("LOG((" ): + raise SystemExit("fastsearch find prework must be declared before logging") +body_start = source.index("STRINGLIB(_two_way_count)(const STRINGLIB_CHAR *haystack") +body_open = source.index("{", body_start) +body_end = source.index(chr(10) + "}" + chr(10), body_open) + 2 +body = source[body_open:body_end] +for declaration in ("STRINGLIB(prework) p;", "Py_ssize_t index;", "Py_ssize_t count;"): + if body.count(declaration) != 1 or body.index(declaration) > body.index("LOG((" ): + raise SystemExit("fastsearch count locals must be declared before logging") +if not (body.index("LOG((") < body.index("STRINGLIB(_preprocess)(needle, len_needle, &p);") < body.index("index = 0;") < body.index("count = 0;") < body.index("while (1)")): + raise SystemExit("fastsearch count initialization/preprocessing order changed") +PY_FASTSEARCH_VC6_MACRO_TEST +then + pass "fastsearch logging macros parse with the VC6 preprocessor" +else + bad "fastsearch logging macros parse with the VC6 preprocessor" +fi + +# Strip bounds use C89 entry declarations while preserving the executable body. +if python3 - "$REPO_ROOT/cpython/Objects/bytearrayobject.c" <<'PY_BYTEARRAY_STRIP_C89_TEST' +import re +import sys +from pathlib import Path + +source = Path(sys.argv[1]).read_text() +start = source.index("\n{", source.index("bytearray_strip_impl_helper(")) + 2 +end = source.index("\n}", start) +body = source[start:end] +opening, execution = body.split(" if (bytes == Py_None)", 1) +for name in ("left", "right"): + declaration = "Py_ssize_t " + name + ";" + if opening.count(declaration) != 1 or body.count(declaration) != 1: + raise SystemExit(name + " must be declared once in the opening group") +if re.search(r"\bPy_ssize_t\b", execution): + raise SystemExit("strip bounds retain late declarations") +expected = r""" if (bytes == Py_None) { + bytesptr = "\t\n\r\f\v "; + byteslen = 6; + } + else { + if (PyObject_GetBuffer(bytes, &vbytes, PyBUF_SIMPLE) != 0) + return NULL; + bytesptr = (const char*)vbytes.buf; + byteslen = vbytes.len; + } + myptr = PyByteArray_AS_STRING(self); + mysize = Py_SIZE(self); + + left = 0; + if (striptype != RIGHTSTRIP) { + while (left < mysize && memchr(bytesptr, (unsigned char)myptr[left], byteslen)) + left++; + } + right = mysize; + if (striptype != LEFTSTRIP) { + do { + right--; + } while (right >= left && memchr(bytesptr, (unsigned char)myptr[right], byteslen)); + right++; + } + if (bytes != Py_None) + PyBuffer_Release(&vbytes); + return PyByteArray_FromStringAndSize(myptr + left, right - left);""" +if " ".join((" if (bytes == Py_None)" + execution).split()) != " ".join(expected.split()): + raise SystemExit("strip initialization/control flow changed execution order") +PY_BYTEARRAY_STRIP_C89_TEST +then + pass "bytearray strip preserves execution order with C89 bound declarations" +else + bad "bytearray strip preserves execution order with C89 bound declarations" +fi + +if python3 "$REPO_ROOT/scripts/vc6-classobject-c89.test.py" "$REPO_ROOT/cpython/Objects/classobject.c"; then + pass "class method vectorcall preserves C89 declarations and cleanup ordering" +else + bad "class method vectorcall preserves C89 declarations and cleanup ordering" +fi + +if python3 "$REPO_ROOT/scripts/vc6-codeobject-c89.test.py" "$REPO_ROOT/cpython/Objects/codeobject.c"; then + pass "codeobject early C89 declarations preserve order" +else + bad "codeobject early C89 declarations preserve order" +fi + +if python3 "$REPO_ROOT/scripts/vc6-descrobject-classmethod-c89.test.py" "$REPO_ROOT/cpython/Objects/descrobject.c" "$REPO_ROOT/cpython/Objects/object.c"; then + pass "descrobject C89 dispatch and VC6 type initialization order" +else + bad "descrobject C89 dispatch and VC6 type initialization order" +fi + +if python3 "$REPO_ROOT/scripts/vc6-dict-consistency-c89.test.py" "$REPO_ROOT/cpython/Objects/stringlib/eq.h" "$REPO_ROOT/cpython/Objects/dictobject.c"; then + pass "dict consistency C89 declarations and logic order" +else + bad "dict consistency C89 declarations and logic order" +fi + +if python3 "$REPO_ROOT/scripts/vc6-call-first-c89.test.py" "$REPO_ROOT/cpython/Objects/call.c"; then + pass "call.c vectorcall C89 locals preserve dispatch order" +else + bad "call.c vectorcall C89 locals preserve dispatch order" +fi + +if python3 "$REPO_ROOT/scripts/vc6-bytes-core-c89.test.py" "$REPO_ROOT/cpython/Objects/bytesobject.c"; then + pass "bytes find/repeat C89 locals preserve execution order" +else + bad "bytes find/repeat C89 locals preserve execution order" +fi + +if python3 "$REPO_ROOT/scripts/vc6-stringlib-partition-c89.test.py" "$REPO_ROOT/cpython/Objects/stringlib/partition.h"; then + pass "stringlib partition uses C89 declarations and preserves ownership order" +else + bad "stringlib partition uses C89 declarations and preserves ownership order" +fi + +if python3 "$REPO_ROOT/scripts/vc6-bytes-format-c89.test.py" "$REPO_ROOT/cpython/Objects/bytesobject.c"; then + pass "bytes format debug pointer uses C89 declaration order" +else + bad "bytes format debug pointer uses C89 declaration order" +fi + +if python3 "$REPO_ROOT/scripts/vc6-bytes-clinic-c89.test.py" "$REPO_ROOT/cpython/Objects/clinic/bytesobject.c.h"; then + pass "bytes Clinic C89 declarations remain at wrapper entry" +else + bad "bytes Clinic C89 declarations remain at wrapper entry" +fi + +if python3 "$REPO_ROOT/scripts/vc6-bytearray-join-c89.test.py" "$REPO_ROOT/cpython/Objects/bytearrayobject.c"; then + pass "bytearray_join C89 declaration preserves export/join/unexport order" +else + bad "bytearray_join C89 declaration preserves export/join/unexport order" +fi + if [ "$fail" -ne 0 ]; then echo "FAILED" exit 1 diff --git a/scripts/vc6-stringlib-partition-c89.test.py b/scripts/vc6-stringlib-partition-c89.test.py new file mode 100644 index 0000000..6d7f9ce --- /dev/null +++ b/scripts/vc6-stringlib-partition-c89.test.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +"""Check C89 declaration placement in immutable stringlib partitions.""" +import sys +from pathlib import Path + +if len(sys.argv) != 2: + raise SystemExit("usage: vc6-stringlib-partition-c89.test.py partition.h") +source = Path(sys.argv[1]).read_text() +expected_orders = { + "partition": ( + "Py_INCREF(str_obj);", + "empty = (PyObject*)STRINGLIB_GET_EMPTY();", + "assert(empty != NULL);", + "Py_INCREF(empty);", + "PyTuple_SET_ITEM(out, 1, empty);", + "Py_INCREF(empty);", + "PyTuple_SET_ITEM(out, 2, empty);", + ), + "rpartition": ( + "empty = (PyObject*)STRINGLIB_GET_EMPTY();", + "assert(empty != NULL);", + "Py_INCREF(empty);", + "PyTuple_SET_ITEM(out, 0, empty);", + "Py_INCREF(empty);", + "PyTuple_SET_ITEM(out, 1, empty);", + "Py_INCREF(str_obj);", + "PyTuple_SET_ITEM(out, 2, (PyObject*) str_obj);", + ), +} +for name, order in expected_orders.items(): + signature = "STRINGLIB(" + name + ")(" + start = source.index(signature) + body_start = source.index("\n{\n", start) + 2 + body_end = source.index("\n}\n", body_start) + body = source[body_start:body_end] + opening, no_match = body.split(" if (pos < 0)", 1) + if "#if !STRINGLIB_MUTABLE\n PyObject *empty;\n#endif" not in opening: + raise SystemExit(name + ": empty must be declared in the C89 entry declaration group") + if body.count("PyObject *empty;") != 1 or "PyObject *empty =" in body: + raise SystemExit(name + ": empty must have one separate declaration") + branch_start = no_match.index("#else") + branch_end = no_match.index("#endif", branch_start) + branch = no_match[branch_start:branch_end] + cursor = 0 + for statement in order: + position = branch.find(statement, cursor) + if position < 0: + raise SystemExit(name + ": empty reference-count / tuple insertion order changed") + cursor = position + len(statement) +print("stringlib partition C89 declarations and ownership order: PASS")