port: advance VC6 frontier into frameobject
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Guard C89 local ordering in the float32 packing fallback."""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import NoReturn
|
||||
|
||||
|
||||
def fail(message: str) -> NoReturn:
|
||||
print(message, file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def extract_function(source: str, signature: str) -> str:
|
||||
start = source.find(signature)
|
||||
if start < 0:
|
||||
fail(f"missing function signature: {signature}")
|
||||
brace = source.find("{", start + len(signature))
|
||||
if brace < 0:
|
||||
fail("missing function body")
|
||||
depth = 0
|
||||
for end in range(brace, len(source)):
|
||||
if source[end] == "{":
|
||||
depth += 1
|
||||
elif source[end] == "}":
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
return source[brace + 1:end]
|
||||
fail("unterminated function body")
|
||||
|
||||
|
||||
source = Path(sys.argv[1]).read_text(encoding="utf-8")
|
||||
body = extract_function(source, "PyFloat_Pack4(double x, char *data, int le)")
|
||||
expected = """ else {
|
||||
unsigned char s[sizeof(float)];
|
||||
float y = (float)x;
|
||||
int i, incr = 1;"""
|
||||
if expected not in body:
|
||||
fail("float32 fallback locals must be declared at the start of its block")
|
||||
copy = body.find("memcpy(s, &y, sizeof(float));")
|
||||
infinity_check = body.find("if (Py_IS_INFINITY(y) && !Py_IS_INFINITY(x))")
|
||||
pack_loop = body.find("for (i = 0; i < 4; i++)")
|
||||
if min(copy, infinity_check, pack_loop) < 0 or not infinity_check < copy < pack_loop:
|
||||
fail("float32 fallback must preserve overflow-check/copy/packing order")
|
||||
|
||||
dealloc = extract_function(source, "_PyFloat_ExactDealloc(PyObject *obj)")
|
||||
if " PyFloatObject *op;" not in dealloc or " PyFloatObject *op =" in dealloc:
|
||||
fail("float exact deallocator op must be declared without initializer at entry")
|
||||
if " struct _Py_float_state *state;" not in dealloc or " struct _Py_float_state *state =" in dealloc:
|
||||
fail("float exact deallocator state must be declared without initializer at entry")
|
||||
if not dealloc.find("assert(PyFloat_CheckExact(obj));") < dealloc.find("op = (PyFloatObject *)obj;") < dealloc.find("state = get_float_state();"):
|
||||
fail("float exact deallocator must preserve assert/op/state initialization order")
|
||||
|
||||
as_double = extract_function(source, "PyFloat_AsDouble(PyObject *op)")
|
||||
if " PyObject *res;" not in as_double or " double val;" not in as_double:
|
||||
fail("PyFloat_AsDouble temporaries must be declared at function entry")
|
||||
if "PyObject *res = _PyNumber_Index(op);" in as_double or "double val = PyLong_AsDouble(res);" in as_double:
|
||||
fail("PyFloat_AsDouble must not redeclare its entry temporaries")
|
||||
if not as_double.find("res = _PyNumber_Index(op);") < as_double.find("val = PyLong_AsDouble(res);") < as_double.find("Py_DECREF(res);"):
|
||||
fail("PyFloat_AsDouble must preserve index-convert/decref order")
|
||||
|
||||
vectorcall = extract_function(source, "float_vectorcall(PyObject *type, PyObject * const*args,")
|
||||
for declaration in (" Py_ssize_t nargs;", " PyObject *x;"):
|
||||
if declaration not in vectorcall:
|
||||
fail("float_vectorcall locals must be declared at function entry")
|
||||
if "Py_ssize_t nargs =" in vectorcall or "PyObject *x =" in vectorcall:
|
||||
fail("float_vectorcall locals must not be initialized at declaration")
|
||||
if not vectorcall.find("nargs = PyVectorcall_NARGS(nargsf);") < vectorcall.find("x = nargs >= 1 ? args[0] : NULL;"):
|
||||
fail("float_vectorcall must preserve nargs-before-argument selection order")
|
||||
|
||||
type_init = extract_function(source, "PyTypeObject PyFloat_Type =")
|
||||
expected_slots = """ float_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 */
|
||||
(vectorcallfunc)float_vectorcall, /* tp_vectorcall */"""
|
||||
if expected_slots not in type_init or ".tp_vectorcall =" in type_init:
|
||||
fail("PyFloat_Type vectorcall must use C89 positional trailing slots")
|
||||
|
||||
state_init = extract_function(source, "_PyFloat_InitState(PyInterpreterState *interp)")
|
||||
format_declaration = " float_format_type detected_double_format, detected_float_format;"
|
||||
if format_declaration not in state_init:
|
||||
fail("float format detection locals must be declared at function entry")
|
||||
if format_declaration in state_init[state_init.find("if (!_Py_IsMainInterpreter(interp))"):]:
|
||||
fail("float format detection locals must not be declared after a statement")
|
||||
|
||||
clinic = Path(sys.argv[2]).read_text(encoding="utf-8")
|
||||
wrapper = extract_function(clinic, "float___getformat__(PyTypeObject *type, PyObject *arg)")
|
||||
declaration = " Py_ssize_t typestr_length;"
|
||||
if declaration not in wrapper:
|
||||
fail("float.__getformat__ typestr_length must be declared at wrapper entry")
|
||||
validation = wrapper.find("if (!PyUnicode_Check(arg))")
|
||||
assignment = wrapper.find("typestr = PyUnicode_AsUTF8AndSize(arg, &typestr_length);")
|
||||
if validation < 0 or assignment < validation or "Py_ssize_t typestr_length;" in wrapper[validation:]:
|
||||
fail("Clinic wrapper must preserve type validation before string conversion")
|
||||
print("floatobject C89 declarations and generated Clinic validation order: PASS")
|
||||
Reference in New Issue
Block a user