diff --git a/src/python/PythonPycParser.test.ts b/src/python/PythonPycParser.test.ts index e06ef1a274..db176068f1 100644 --- a/src/python/PythonPycParser.test.ts +++ b/src/python/PythonPycParser.test.ts @@ -1668,6 +1668,47 @@ it("hashes shallow TYPE_REF code chains without using the JavaScript stack", () }); }); +it("hashes slice-wrapped TYPE_REF code chains without using the JavaScript stack", () => { + const links = 3000; + const none = Buffer.from([0x4e]); + const codes = Array.from({ length: links }, (_, index) => + marshalCode("py311", { + flagged: true, + name: "f", + consts: + index === 0 + ? [none] + : [ + Buffer.concat([ + Buffer.from([0x3a]), + marshalRef(index - 1), + none, + none, + ]), + ], + }), + ); + const bytes = Buffer.concat([ + pythonPycHeader(3627), + marshalCode("py311", { + constsObject: Buffer.concat([ + Buffer.from([0x28]), + u32(links + 1), + ...codes, + Buffer.from([0x3c]), + u32(1), + marshalRef(links - 1), + ]), + }), + ]); + const report = parseWith(bytes, { max_objects: 65_536 }); + expect(root(report).consts.at(-1)).toEqual({ kind: "set", length: 1 }); + expect(report.coverage).toMatchObject({ + status: "complete", + code_objects: links + 1, + }); +}); + it("rejects code CPython treats as corrupt instead of complete coverage", () => { const file = ( layout: "py37" | "py38" | "py311", diff --git a/src/python/PythonPycParser.ts b/src/python/PythonPycParser.ts index d6b3d8cf7c..9da9d01aeb 100644 --- a/src/python/PythonPycParser.ts +++ b/src/python/PythonPycParser.ts @@ -1413,7 +1413,9 @@ class MarshalReader { malformed("Code object is missing equality material.") ).constsTuple, ] - : value.kind === "tuple" || value.kind === "frozenset" + : value.kind === "tuple" || + value.kind === "frozenset" || + value.kind === "slice" ? value.items : []; gray.add(identity);