port: expand VC6 C89 core frontier
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Guard C89 local ordering in frame_getlocals."""
|
||||
"""Guard C89 local ordering and frame operation order."""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import NoReturn
|
||||
@@ -92,4 +92,479 @@ if " int result, i;" not in firstline or "for (int i =" in firstline:
|
||||
fail("first_line_not_before loop index must be declared at function entry")
|
||||
if not firstline.find("result = INT_MAX;") < firstline.find("for (i = 0; i < len; i++)"):
|
||||
fail("first_line_not_before must initialize its minimum before scanning")
|
||||
|
||||
setlineno = extract_function(source, 'frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignored))')
|
||||
entry_declarations = (
|
||||
'PyFrameState state;',
|
||||
'int i;',
|
||||
'int new_lineno;',
|
||||
'int overflow;',
|
||||
'long l_new_lineno;',
|
||||
'int len;',
|
||||
'int *lines;',
|
||||
'int64_t *stacks;',
|
||||
'int64_t best_stack;',
|
||||
'int best_addr;',
|
||||
'int64_t start_stack;',
|
||||
'int err;',
|
||||
'const char *msg;',
|
||||
)
|
||||
remaining = setlineno.lstrip()
|
||||
for declaration in entry_declarations:
|
||||
if not remaining.startswith(declaration + "\n"):
|
||||
fail("frame_setlineno function-entry declaration missing or out of order: " + declaration)
|
||||
remaining = remaining[len(declaration):].lstrip()
|
||||
|
||||
# Freeze the execution body, including validation, cleanup, error selection,
|
||||
# loop order, frame stack operations, and unchanged nested block declarations.
|
||||
# Only blank lines and indentation are ignored; no executable text is omitted.
|
||||
expected_execution = r"""
|
||||
|
||||
if (p_new_lineno == NULL) {
|
||||
PyErr_SetString(PyExc_AttributeError, "cannot delete attribute");
|
||||
return -1;
|
||||
}
|
||||
/* f_lineno must be an integer. */
|
||||
if (!PyLong_CheckExact(p_new_lineno)) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"lineno must be an integer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
state = _PyFrame_GetState(f);
|
||||
/*
|
||||
* This code preserves the historical restrictions on
|
||||
* setting the line number of a frame.
|
||||
* Jumps are forbidden on a 'return' trace event (except after a yield).
|
||||
* Jumps from 'call' trace events are also forbidden.
|
||||
* In addition, jumps are forbidden when not tracing,
|
||||
* as this is a debugging feature.
|
||||
*/
|
||||
switch(PyThreadState_GET()->tracing_what) {
|
||||
case PyTrace_EXCEPTION:
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"can only jump from a 'line' trace event");
|
||||
return -1;
|
||||
case PyTrace_CALL:
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"can't jump from the 'call' trace event of a new frame");
|
||||
return -1;
|
||||
case PyTrace_LINE:
|
||||
break;
|
||||
case PyTrace_RETURN:
|
||||
if (state == FRAME_SUSPENDED) {
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"can only jump from a 'line' trace event");
|
||||
return -1;
|
||||
}
|
||||
if (!f->f_trace) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"f_lineno can only be set by a trace function");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Fail if the line falls outside the code block and
|
||||
select first line with actual code. */
|
||||
l_new_lineno = PyLong_AsLongAndOverflow(p_new_lineno, &overflow);
|
||||
if (overflow
|
||||
#if SIZEOF_LONG > SIZEOF_INT
|
||||
|| l_new_lineno > INT_MAX
|
||||
|| l_new_lineno < INT_MIN
|
||||
#endif
|
||||
) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"lineno out of range");
|
||||
return -1;
|
||||
}
|
||||
new_lineno = (int)l_new_lineno;
|
||||
|
||||
if (new_lineno < f->f_frame->f_code->co_firstlineno) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"line %d comes before the current code block",
|
||||
new_lineno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* PyCode_NewWithPosOnlyArgs limits co_code to be under INT_MAX so this
|
||||
* should never overflow. */
|
||||
len = (int)Py_SIZE(f->f_frame->f_code);
|
||||
lines = marklines(f->f_frame->f_code, len);
|
||||
if (lines == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
new_lineno = first_line_not_before(lines, len, new_lineno);
|
||||
if (new_lineno < 0) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"line %d comes after the current code block",
|
||||
(int)l_new_lineno);
|
||||
PyMem_Free(lines);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stacks = mark_stacks(f->f_frame->f_code, len);
|
||||
if (stacks == NULL) {
|
||||
PyMem_Free(lines);
|
||||
return -1;
|
||||
}
|
||||
|
||||
best_stack = OVERFLOWED;
|
||||
best_addr = -1;
|
||||
start_stack = stacks[_PyInterpreterFrame_LASTI(f->f_frame)];
|
||||
err = -1;
|
||||
msg = "cannot find bytecode for specified line";
|
||||
for (i = 0; i < len; i++) {
|
||||
if (lines[i] == new_lineno) {
|
||||
int64_t target_stack = stacks[i];
|
||||
if (compatible_stack(start_stack, target_stack)) {
|
||||
err = 0;
|
||||
if (target_stack > best_stack) {
|
||||
best_stack = target_stack;
|
||||
best_addr = i;
|
||||
}
|
||||
}
|
||||
else if (err < 0) {
|
||||
if (start_stack == OVERFLOWED) {
|
||||
msg = "stack to deep to analyze";
|
||||
}
|
||||
else if (start_stack == UNINITIALIZED) {
|
||||
msg = "can't jump from unreachable code";
|
||||
}
|
||||
else {
|
||||
msg = explain_incompatible_stack(target_stack);
|
||||
err = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PyMem_Free(stacks);
|
||||
PyMem_Free(lines);
|
||||
if (err) {
|
||||
PyErr_SetString(PyExc_ValueError, msg);
|
||||
return -1;
|
||||
}
|
||||
if (state == FRAME_SUSPENDED) {
|
||||
/* Account for value popped by yield */
|
||||
start_stack = pop_value(start_stack);
|
||||
}
|
||||
while (start_stack > best_stack) {
|
||||
if (top_of_stack(start_stack) == Except) {
|
||||
/* Pop exception stack as well as the evaluation stack */
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
_PyErr_StackItem *exc_info = tstate->exc_info;
|
||||
PyObject *value = exc_info->exc_value;
|
||||
PyObject *exc = _PyFrame_StackPop(f->f_frame);
|
||||
assert(PyExceptionInstance_Check(exc) || exc == Py_None);
|
||||
exc_info->exc_value = exc;
|
||||
Py_XDECREF(value);
|
||||
}
|
||||
else {
|
||||
PyObject *v = _PyFrame_StackPop(f->f_frame);
|
||||
Py_XDECREF(v);
|
||||
}
|
||||
start_stack = pop_value(start_stack);
|
||||
}
|
||||
/* Finally set the new lasti and return OK. */
|
||||
f->f_lineno = 0;
|
||||
f->f_frame->prev_instr = _PyCode_CODE(f->f_frame->f_code) + best_addr;
|
||||
return 0;
|
||||
"""
|
||||
def execution_lines(text: str) -> list:
|
||||
return [line.strip() for line in text.splitlines() if line.strip()]
|
||||
|
||||
if execution_lines(remaining) != execution_lines(expected_execution):
|
||||
fail("frame_setlineno execution body changed or contains a late function-scope declaration")
|
||||
|
||||
frame_dealloc = extract_function(source, "frame_dealloc(PyFrameObject *f)")
|
||||
expected_dealloc_declarations = (
|
||||
"PyCodeObject *co;",
|
||||
"_PyInterpreterFrame *frame;",
|
||||
"PyObject **locals;",
|
||||
"int i;",
|
||||
)
|
||||
remaining = frame_dealloc.lstrip()
|
||||
for declaration in expected_dealloc_declarations:
|
||||
if not remaining.startswith(declaration + "\n"):
|
||||
fail("frame_dealloc entry declaration missing or out of order: " + declaration)
|
||||
remaining = remaining[len(declaration):].lstrip()
|
||||
if "PyCodeObject *co =" in frame_dealloc or "_PyInterpreterFrame *frame =" in frame_dealloc or "PyObject **locals =" in frame_dealloc or "for (int i =" in frame_dealloc:
|
||||
fail("frame_dealloc must not declare locals after statements")
|
||||
ordered_dealloc = (
|
||||
"Py_TRASHCAN_BEGIN(f, frame_dealloc);",
|
||||
"co = NULL;",
|
||||
"frame = (_PyInterpreterFrame *)f->_f_frame_data;",
|
||||
"if (f->f_frame == frame && frame->owner == FRAME_OWNED_BY_FRAME_OBJECT)",
|
||||
"co = frame->f_code;",
|
||||
"frame->f_code = NULL;",
|
||||
"Py_CLEAR(frame->f_func);",
|
||||
"Py_CLEAR(frame->f_locals);",
|
||||
"locals = _PyFrame_GetLocalsArray(frame);",
|
||||
"for (i = 0; i < frame->stacktop; i++)",
|
||||
"Py_CLEAR(f->f_back);",
|
||||
"Py_CLEAR(f->f_trace);",
|
||||
"PyObject_GC_Del(f);",
|
||||
"Py_XDECREF(co);",
|
||||
"Py_TRASHCAN_END;",
|
||||
)
|
||||
positions = [frame_dealloc.find(token) for token in ordered_dealloc]
|
||||
if any(position < 0 for position in positions) or positions != sorted(positions):
|
||||
fail("frame_dealloc must preserve trashcan, owned-frame cleanup, and decref order")
|
||||
|
||||
frame_clear_locals = extract_function(source, "frame_tp_clear(PyFrameObject *f)")
|
||||
remaining = frame_clear_locals.lstrip()
|
||||
for declaration in ("PyObject **locals;", "int i;"):
|
||||
if not remaining.startswith(declaration + "\n"):
|
||||
fail("frame_tp_clear entry declaration missing or out of order: " + declaration)
|
||||
remaining = remaining[len(declaration):].lstrip()
|
||||
if "PyObject **locals =" in frame_clear_locals or "for (int i =" in frame_clear_locals:
|
||||
fail("frame_tp_clear must not declare locals after statements")
|
||||
ordered_clear = (
|
||||
"Py_CLEAR(f->f_trace);",
|
||||
"locals = _PyFrame_GetLocalsArray(f->f_frame);",
|
||||
"assert(f->f_frame->stacktop >= 0);",
|
||||
"for (i = 0; i < f->f_frame->stacktop; i++)",
|
||||
"Py_CLEAR(locals[i]);",
|
||||
"f->f_frame->stacktop = 0;",
|
||||
)
|
||||
positions = [frame_clear_locals.find(token) for token in ordered_clear]
|
||||
if any(position < 0 for position in positions) or positions != sorted(positions):
|
||||
fail("frame_tp_clear must preserve trace/local/stack cleanup order")
|
||||
|
||||
frame_sizeof = extract_function(source, "frame_sizeof(PyFrameObject *f, PyObject *Py_UNUSED(ignored))")
|
||||
remaining = frame_sizeof.lstrip()
|
||||
for declaration in ("Py_ssize_t res;", "PyCodeObject *code;"):
|
||||
if not remaining.startswith(declaration + "\n"):
|
||||
fail("frame_sizeof entry declaration missing or out of order: " + declaration)
|
||||
remaining = remaining[len(declaration):].lstrip()
|
||||
if "PyCodeObject *code =" in frame_sizeof:
|
||||
fail("frame_sizeof code pointer must not be declared after initialization")
|
||||
ordered_sizeof = (
|
||||
"res = offsetof(PyFrameObject, _f_frame_data) + offsetof(_PyInterpreterFrame, localsplus);",
|
||||
"code = f->f_frame->f_code;",
|
||||
"res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *);",
|
||||
"return PyLong_FromSsize_t(res);",
|
||||
)
|
||||
positions = [frame_sizeof.find(token) for token in ordered_sizeof]
|
||||
if any(position < 0 for position in positions) or positions != sorted(positions):
|
||||
fail("frame_sizeof must preserve size calculation and return order")
|
||||
|
||||
init_frame = extract_function(source, "init_frame(_PyInterpreterFrame *frame, PyFunctionObject *func, PyObject *locals)")
|
||||
remaining = init_frame.lstrip()
|
||||
for declaration in ("PyCodeObject *code;", "Py_ssize_t i;"):
|
||||
if not remaining.startswith(declaration + "\n"):
|
||||
fail("init_frame entry declaration missing or out of order: " + declaration)
|
||||
remaining = remaining[len(declaration):].lstrip()
|
||||
if "PyCodeObject *code =" in init_frame or "for (Py_ssize_t i =" in init_frame:
|
||||
fail("init_frame must not declare locals after statements")
|
||||
ordered_init_frame = (
|
||||
"Py_INCREF(func);",
|
||||
"code = (PyCodeObject *)func->func_code;",
|
||||
"_PyFrame_InitializeSpecials(frame, func, locals, code->co_nlocalsplus);",
|
||||
"frame->previous = NULL;",
|
||||
"for (i = 0; i < code->co_nlocalsplus; i++)",
|
||||
"frame->localsplus[i] = NULL;",
|
||||
)
|
||||
positions = [init_frame.find(token) for token in ordered_init_frame]
|
||||
if any(position < 0 for position in positions) or positions != sorted(positions):
|
||||
fail("init_frame must preserve reference and frame initialization order")
|
||||
|
||||
new_no_track = extract_function(source, "_PyFrame_New_NoTrack(PyCodeObject *code)")
|
||||
remaining = new_no_track.lstrip()
|
||||
for declaration in ("int slots;", "PyFrameObject *f;"):
|
||||
if not remaining.startswith(declaration + "\n"):
|
||||
fail("_PyFrame_New_NoTrack entry declaration missing or out of order: " + declaration)
|
||||
remaining = remaining[len(declaration):].lstrip()
|
||||
if "int slots =" in new_no_track or "PyFrameObject *f =" in new_no_track:
|
||||
fail("_PyFrame_New_NoTrack locals must not be initialized after statements")
|
||||
ordered_new_no_track = (
|
||||
"CALL_STAT_INC(frame_objects_created);",
|
||||
"slots = code->co_nlocalsplus + code->co_stacksize;",
|
||||
"f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, slots);",
|
||||
"if (f == NULL)",
|
||||
"f->f_back = NULL;",
|
||||
"f->f_lineno = 0;",
|
||||
"return f;",
|
||||
)
|
||||
positions = [new_no_track.find(token) for token in ordered_new_no_track]
|
||||
if any(position < 0 for position in positions) or positions != sorted(positions):
|
||||
fail("_PyFrame_New_NoTrack must preserve stat, allocation, and initialization order")
|
||||
|
||||
frame_new = extract_function(source, "PyFrame_New(PyThreadState *tstate, PyCodeObject *code,")
|
||||
remaining = frame_new.lstrip()
|
||||
for declaration in (
|
||||
"PyObject *builtins;",
|
||||
"PyFrameConstructor desc;",
|
||||
"PyFunctionObject *func;",
|
||||
"PyFrameObject *f;",
|
||||
):
|
||||
if not remaining.startswith(declaration + "\n"):
|
||||
fail("PyFrame_New entry declaration missing or out of order: " + declaration)
|
||||
remaining = remaining[len(declaration):].lstrip()
|
||||
if "PyFrameConstructor desc =" in frame_new or "PyFunctionObject *func =" in frame_new or "PyFrameObject *f =" in frame_new:
|
||||
fail("PyFrame_New must not use initialized late declarations")
|
||||
if "\n .fc_globals =" in frame_new or "\n .fc_builtins =" in frame_new:
|
||||
fail("PyFrame_New constructor must not use designated initializers")
|
||||
ordered_frame_new = (
|
||||
"builtins = _PyEval_BuiltinsFromGlobals(tstate, globals);",
|
||||
"if (builtins == NULL)",
|
||||
"desc.fc_globals = globals;",
|
||||
"desc.fc_builtins = builtins;",
|
||||
"desc.fc_name = code->co_name;",
|
||||
"desc.fc_qualname = code->co_name;",
|
||||
"desc.fc_code = (PyObject *)code;",
|
||||
"desc.fc_defaults = NULL;",
|
||||
"desc.fc_kwdefaults = NULL;",
|
||||
"desc.fc_closure = NULL;",
|
||||
"func = _PyFunction_FromConstructor(&desc);",
|
||||
"if (func == NULL)",
|
||||
"f = _PyFrame_New_NoTrack(code);",
|
||||
"if (f == NULL)",
|
||||
"init_frame((_PyInterpreterFrame *)f->_f_frame_data, func, locals);",
|
||||
"Py_DECREF(func);",
|
||||
"return f;",
|
||||
)
|
||||
positions = [frame_new.rfind(token) if token == "Py_DECREF(func);" else frame_new.find(token) for token in ordered_frame_new]
|
||||
if any(position < 0 for position in positions) or positions != sorted(positions):
|
||||
fail("PyFrame_New must preserve validation, construction, and cleanup order")
|
||||
|
||||
op_already_ran = extract_function(source, "_PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, int opcode, int oparg)")
|
||||
remaining = op_already_ran.lstrip()
|
||||
for declaration in ("int check_oparg;", "_Py_CODEUNIT *instruction;"):
|
||||
if not remaining.startswith(declaration + "\n"):
|
||||
fail("_PyFrame_OpAlreadyRan entry declaration missing or out of order: " + declaration)
|
||||
remaining = remaining[len(declaration):].lstrip()
|
||||
if "int check_oparg =" in op_already_ran or "for (_Py_CODEUNIT *instruction =" in op_already_ran:
|
||||
fail("_PyFrame_OpAlreadyRan must not declare locals after statements")
|
||||
ordered_op_check = (
|
||||
"assert(_PyOpcode_Deopt[opcode] == opcode);",
|
||||
"check_oparg = 0;",
|
||||
"for (instruction = _PyCode_CODE(frame->f_code);",
|
||||
"instruction < frame->prev_instr; instruction++)",
|
||||
"int check_opcode = _PyOpcode_Deopt[_Py_OPCODE(*instruction)];",
|
||||
"check_oparg |= _Py_OPARG(*instruction);",
|
||||
"instruction += _PyOpcode_Caches[check_opcode];",
|
||||
"return 0;",
|
||||
)
|
||||
positions = [op_already_ran.find(token) for token in ordered_op_check]
|
||||
if any(position < 0 for position in positions) or positions != sorted(positions):
|
||||
fail("_PyFrame_OpAlreadyRan must preserve assert, opcode scan, and return order")
|
||||
|
||||
fast_to_locals = extract_function(source, "_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame)")
|
||||
entry_statements = fast_to_locals.find("locals = frame->f_locals;")
|
||||
for declaration in ("int lasti;", "int i;"):
|
||||
if fast_to_locals.find(declaration) < 0 or fast_to_locals.find(declaration) > entry_statements:
|
||||
fail("_PyFrame_FastToLocalsWithError loop/state locals must be declared at entry: " + declaration)
|
||||
if "int lasti =" in fast_to_locals or "for (int i =" in fast_to_locals:
|
||||
fail("_PyFrame_FastToLocalsWithError must not use late or loop declarations")
|
||||
if not fast_to_locals.find("lasti = _PyInterpreterFrame_LASTI(frame);") < fast_to_locals.find("if (lasti < 0"):
|
||||
fail("_PyFrame_FastToLocalsWithError must preserve instruction-state lookup order")
|
||||
if "PyObject *name;" not in fast_to_locals or "PyObject *value;" not in fast_to_locals:
|
||||
fail("_PyFrame_FastToLocalsWithError loop item locals must be declared before loop statements")
|
||||
if not fast_to_locals.find("if (kind & CO_FAST_FREE && !(co->co_flags & CO_OPTIMIZED))") < fast_to_locals.find("name = PyTuple_GET_ITEM(co->co_localsplusnames, i);") < fast_to_locals.find("value = fast[i];"):
|
||||
fail("_PyFrame_FastToLocalsWithError must preserve skip and local lookup order")
|
||||
|
||||
public_fast_to_locals = extract_function(source, "PyFrame_FastToLocalsWithError(PyFrameObject *f)")
|
||||
if not public_fast_to_locals.lstrip().startswith("int err;") or "int err =" in public_fast_to_locals:
|
||||
fail("PyFrame_FastToLocalsWithError result must be declared at function entry")
|
||||
ordered_public_fast = (
|
||||
"assert(!_PyFrame_IsIncomplete(f->f_frame));",
|
||||
"if (f == NULL)",
|
||||
"err = _PyFrame_FastToLocalsWithError(f->f_frame);",
|
||||
"if (err == 0)",
|
||||
"f->f_fast_as_locals = 1;",
|
||||
"return err;",
|
||||
)
|
||||
positions = [public_fast_to_locals.find(token) for token in ordered_public_fast]
|
||||
if any(position < 0 for position in positions) or positions != sorted(positions):
|
||||
fail("PyFrame_FastToLocalsWithError must preserve guard and status-update order")
|
||||
|
||||
locals_to_fast = extract_function(source, "_PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)")
|
||||
entry_statement = locals_to_fast.find("locals = frame->f_locals;")
|
||||
if "int i;" not in locals_to_fast or locals_to_fast.find("int i;") > entry_statement or "for (int i =" in locals_to_fast:
|
||||
fail("_PyFrame_LocalsToFast index must be declared before execution")
|
||||
if "PyObject *name;" not in locals_to_fast or "PyObject *value;" not in locals_to_fast or "PyObject *oldvalue;" not in locals_to_fast or "PyObject *cell;" not in locals_to_fast:
|
||||
fail("_PyFrame_LocalsToFast loop temporaries must be declared at loop entry")
|
||||
if "PyObject *name =" in locals_to_fast or "PyObject *value =" in locals_to_fast or "PyObject *oldvalue =" in locals_to_fast or "PyObject *cell =" in locals_to_fast:
|
||||
fail("_PyFrame_LocalsToFast must not use late initialized declarations")
|
||||
ordered_locals_to_fast = (
|
||||
"PyErr_Fetch(&error_type, &error_value, &error_traceback);",
|
||||
"for (i = 0; i < co->co_nlocalsplus; i++)",
|
||||
"kind = _PyLocals_GetKind(co->co_localspluskinds, i);",
|
||||
"if (kind & CO_FAST_FREE && !(co->co_flags & CO_OPTIMIZED))",
|
||||
"name = PyTuple_GET_ITEM(co->co_localsplusnames, i);",
|
||||
"value = PyObject_GetItem(locals, name);",
|
||||
"if (value == NULL)",
|
||||
"oldvalue = fast[i];",
|
||||
"cell = NULL;",
|
||||
"Py_XDECREF(value);",
|
||||
"PyErr_Restore(error_type, error_value, error_traceback);",
|
||||
)
|
||||
positions = [locals_to_fast.find(token) for token in ordered_locals_to_fast]
|
||||
if any(position < 0 for position in positions) or positions != sorted(positions):
|
||||
fail("_PyFrame_LocalsToFast must preserve error, lookup, and refcount order")
|
||||
|
||||
get_code = extract_function(source, "PyFrame_GetCode(PyFrameObject *frame)")
|
||||
if not get_code.lstrip().startswith("PyCodeObject *code;") or "PyCodeObject *code =" in get_code:
|
||||
fail("PyFrame_GetCode local must be declared at function entry")
|
||||
ordered_get_code = (
|
||||
"assert(frame != NULL);",
|
||||
"assert(!_PyFrame_IsIncomplete(frame->f_frame));",
|
||||
"code = frame->f_frame->f_code;",
|
||||
"assert(code != NULL);",
|
||||
"Py_INCREF(code);",
|
||||
"return code;",
|
||||
)
|
||||
positions = [get_code.find(token) for token in ordered_get_code]
|
||||
if any(position < 0 for position in positions) or positions != sorted(positions):
|
||||
fail("PyFrame_GetCode must preserve validation and reference ownership order")
|
||||
|
||||
get_back = extract_function(source, "PyFrame_GetBack(PyFrameObject *frame)")
|
||||
if not get_back.lstrip().startswith("PyFrameObject *back;") or "PyFrameObject *back =" in get_back:
|
||||
fail("PyFrame_GetBack local must be declared at function entry")
|
||||
ordered_get_back = (
|
||||
"assert(frame != NULL);",
|
||||
"assert(!_PyFrame_IsIncomplete(frame->f_frame));",
|
||||
"back = frame->f_back;",
|
||||
"if (back == NULL)",
|
||||
"_PyInterpreterFrame *prev = frame->f_frame->previous;",
|
||||
"back = _PyFrame_GetFrameObject(prev);",
|
||||
"Py_XINCREF(back);",
|
||||
"return back;",
|
||||
)
|
||||
positions = [get_back.find(token) for token in ordered_get_back]
|
||||
if any(position < 0 for position in positions) or positions != sorted(positions):
|
||||
fail("PyFrame_GetBack must preserve frame-chain lookup and incref order")
|
||||
|
||||
get_lasti = extract_function(source, "PyFrame_GetLasti(PyFrameObject *frame)")
|
||||
if not get_lasti.lstrip().startswith("int lasti;") or "int lasti =" in get_lasti:
|
||||
fail("PyFrame_GetLasti local must be declared at function entry")
|
||||
ordered_get_lasti = (
|
||||
"assert(!_PyFrame_IsIncomplete(frame->f_frame));",
|
||||
"lasti = _PyInterpreterFrame_LASTI(frame->f_frame);",
|
||||
"if (lasti < 0)",
|
||||
"return lasti * sizeof(_Py_CODEUNIT);",
|
||||
)
|
||||
positions = [get_lasti.find(token) for token in ordered_get_lasti]
|
||||
if any(position < 0 for position in positions) or positions != sorted(positions):
|
||||
fail("PyFrame_GetLasti must preserve assertion and bytecode-offset order")
|
||||
|
||||
get_generator = extract_function(source, "PyFrame_GetGenerator(PyFrameObject *frame)")
|
||||
if not get_generator.lstrip().startswith("PyGenObject *gen;") or "PyGenObject *gen =" in get_generator:
|
||||
fail("PyFrame_GetGenerator local must be declared at function entry")
|
||||
ordered_get_generator = (
|
||||
"assert(!_PyFrame_IsIncomplete(frame->f_frame));",
|
||||
"if (frame->f_frame->owner != FRAME_OWNED_BY_GENERATOR)",
|
||||
"return NULL;",
|
||||
"gen = _PyFrame_GetGenerator(frame->f_frame);",
|
||||
"return Py_NewRef(gen);",
|
||||
)
|
||||
positions = [get_generator.find(token) for token in ordered_get_generator]
|
||||
if any(position < 0 for position in positions) or positions != sorted(positions):
|
||||
fail("PyFrame_GetGenerator must preserve owner guard and new-reference order")
|
||||
|
||||
print("frameobject C89 declarations and frame locals ownership order: PASS")
|
||||
|
||||
Reference in New Issue
Block a user