diff --git a/src/python/PythonPycBytecodeEquality.ts b/src/python/PythonPycBytecodeEquality.ts index 9936d501b8..a2eae7b73f 100644 --- a/src/python/PythonPycBytecodeEquality.ts +++ b/src/python/PythonPycBytecodeEquality.ts @@ -38,6 +38,8 @@ const inlineCacheProfiles = ( * 22e65eecaad3f5337862319687047afe9861e1ef (3563), and * 52cc4af6ae9002f11605f91b672746c127494efd (3564). Adjacent magics share a * profile only where their source changes do not alter cache widths or IDs. + * Final 3.13 magic 3571 matches the opcode IDs and cache widths in CPython + * v3.13.17's Lib/_opcode_metadata.py and Lib/opcode.py. */ const INLINE_CACHE_ENTRIES_BY_MAGIC: ReadonlyMap< number, @@ -391,7 +393,7 @@ const INLINE_CACHE_ENTRIES_BY_MAGIC: ReadonlyMap< ], ), ...inlineCacheProfiles( - [3569, 3570], + [3569, 3570, 3571], [ [5, 1], [39, 1], @@ -414,30 +416,6 @@ const INLINE_CACHE_ENTRIES_BY_MAGIC: ReadonlyMap< [117, 1], ], ), - ...inlineCacheProfiles( - [3571], - [ - [5, 1], - [38, 1], - [39, 3], - [44, 1], - [52, 3], - [57, 1], - [58, 1], - [71, 1], - [76, 1], - [81, 9], - [91, 4], - [93, 1], - [97, 1], - [98, 1], - [99, 1], - [100, 1], - [104, 1], - [108, 4], - [117, 1], - ], - ), ]); /** Preserve CPython's bytecode-size check and omit only cache code units. */ diff --git a/tests/acceptance/pythonPycCli.test.ts b/tests/acceptance/pythonPycCli.test.ts index af8f5587d7..37575e4a8c 100644 --- a/tests/acceptance/pythonPycCli.test.ts +++ b/tests/acceptance/pythonPycCli.test.ts @@ -114,6 +114,35 @@ cliTest( }, ); +cliTest( + "retains distinct Python 3.13 code objects after STORE_SLICE", + async ({ cli }) => { + // CPython 3.13.17 marshal output: a module with a frozenset of two + // compiler-produced functions differing only in the LOAD_FAST operand + // immediately after STORE_SLICE. CPython retains both through round-trip. + const pyc = Buffer.from( + "8w0NCgAAAAAAAAAAAAAAAOMAAAAAAAAAAAAAAAABAAAAAAAAAPMEAAAAlQBnACkCTj4CAAAA4wIAAAAAAAAAAAAAAAQAAAADAAAA8w4AAACVAFgQUwFTAiYAVQAkACkDTukBAAAA6QIAAACpACkC2gFh2gFicwIAAAAgINoKZml4dHVyZS5wedoBZnIKAAAAAQAAAHMQAAAAgADYDQ6AYYgBgEbYCwyASPMAAAAA4wIAAAAAAAAAAAAAAAQAAAADAAAA8w4AAACVAFgQUwFTAiYAVQEkACkDTnIEAAAAcgUAAAByBgAAACkCcgcAAAByCAAAAHMCAAAAICByCQAAAHIKAAAAcgoAAAABAAAAcxAAAACAANgNDoBhiAGARtgLDIBIcgsAAAByBgAAAHIGAAAAcgsAAAByCQAAANoIPG1vZHVsZT5yDgAAAAEAAABzCAAAAPADAQEB2AAEcgsAAAA=", + "base64", + ); + const root = await createTestTempDirectory("rea-python-pyc-store-slice-"); + const path = join(root, "code-set.pyc"); + await writeFile(path, pyc); + const result = await cli.run({ + arguments: ["inspect-python-pyc", path, "--json"], + }); + expect(result.exitCode, result.stderr).toBe(0); + const evidence = completePycEvidenceSchema.parse(result.json); + expect(evidence.normalized_result.code_objects).toHaveLength(3); + expect(evidence.normalized_result.code_objects[0]).toMatchObject({ + consts: [{ kind: "none" }, { kind: "frozenset", length: 2 }], + nested_code: [1, 2], + }); + expect(evidence.normalized_result.artifact.sha256).toBe( + createHash("sha256").update(pyc).digest("hex"), + ); + }, +); + cliTest( "uses magic-specific cache tables for synthetic pyc bytecode equality", async ({ cli }) => { @@ -126,7 +155,7 @@ cliTest( { magic: 3550, opcode: 25, cacheUnits: 1 }, { magic: 3553, opcode: 25, cacheUnits: 1 }, { magic: 3564, opcode: 58, cacheUnits: 1 }, - { magic: 3571, opcode: 81, cacheUnits: 9 }, + { magic: 3571, opcode: 82, cacheUnits: 9 }, ]; for (const { magic, opcode, cacheUnits } of cases) { const cache = Buffer.alloc(cacheUnits * 2);