[3.14] gh-132775: Use _PyCode GetScriptXIData() (gh-134515)

(cherry picked from commit 09e72cf091, AKA gh-134511)

Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
This commit is contained in:
Miss Islington (bot) 2025-05-22 17:07:11 +02:00 committed by GitHub
parent c31b25c705
commit bbf8048c0f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 106 additions and 242 deletions

View file

@ -9,7 +9,6 @@
#include "pycore_code.h" // _PyCode_HAS_EXECUTORS()
#include "pycore_crossinterp.h" // _PyXIData_t
#include "pycore_pyerrors.h" // _PyErr_GetRaisedException()
#include "pycore_function.h" // _PyFunction_VerifyStateless()
#include "pycore_interp.h" // _PyInterpreterState_IDIncref()
#include "pycore_modsupport.h" // _PyArg_BadArgument()
#include "pycore_namespace.h" // _PyNamespace_New()
@ -361,81 +360,6 @@ _get_current_xibufferview_type(void)
}
/* Python code **************************************************************/
static const char *
check_code_str(PyUnicodeObject *text)
{
assert(text != NULL);
if (PyUnicode_GET_LENGTH(text) == 0) {
return "too short";
}
// XXX Verify that it parses?
return NULL;
}
#ifndef NDEBUG
static int
code_has_args(PyCodeObject *code)
{
assert(code != NULL);
return (code->co_argcount > 0
|| code->co_posonlyargcount > 0
|| code->co_kwonlyargcount > 0
|| code->co_flags & (CO_VARARGS | CO_VARKEYWORDS));
}
#endif
#define RUN_TEXT 1
#define RUN_CODE 2
static const char *
get_code_str(PyObject *arg, Py_ssize_t *len_p, PyObject **bytes_p, int *flags_p)
{
const char *codestr = NULL;
Py_ssize_t len = -1;
PyObject *bytes_obj = NULL;
int flags = 0;
if (PyUnicode_Check(arg)) {
assert(PyUnicode_Check(arg)
&& (check_code_str((PyUnicodeObject *)arg) == NULL));
codestr = PyUnicode_AsUTF8AndSize(arg, &len);
if (codestr == NULL) {
return NULL;
}
if (strlen(codestr) != (size_t)len) {
PyErr_SetString(PyExc_ValueError,
"source code string cannot contain null bytes");
return NULL;
}
flags = RUN_TEXT;
}
else {
assert(PyCode_Check(arg));
assert(_PyCode_VerifyStateless(
PyThreadState_Get(), (PyCodeObject *)arg, NULL, NULL, NULL) == 0);
assert(!code_has_args((PyCodeObject *)arg));
flags = RUN_CODE;
// Serialize the code object.
bytes_obj = PyMarshal_WriteObjectToString(arg, Py_MARSHAL_VERSION);
if (bytes_obj == NULL) {
return NULL;
}
codestr = PyBytes_AS_STRING(bytes_obj);
len = PyBytes_GET_SIZE(bytes_obj);
}
*flags_p = flags;
*bytes_p = bytes_obj;
*len_p = len;
return codestr;
}
/* interpreter-specific code ************************************************/
static int
@ -499,22 +423,14 @@ config_from_object(PyObject *configobj, PyInterpreterConfig *config)
static int
_run_script(PyObject *ns, const char *codestr, Py_ssize_t codestrlen, int flags)
_run_script(_PyXIData_t *script, PyObject *ns)
{
PyObject *result = NULL;
if (flags & RUN_TEXT) {
result = PyRun_StringFlags(codestr, Py_file_input, ns, ns, NULL);
}
else if (flags & RUN_CODE) {
PyObject *code = PyMarshal_ReadObjectFromString(codestr, codestrlen);
if (code != NULL) {
result = PyEval_EvalCode(code, ns, ns);
Py_DECREF(code);
}
}
else {
Py_UNREACHABLE();
PyObject *code = _PyXIData_NewObject(script);
if (code == NULL) {
return -1;
}
PyObject *result = PyEval_EvalCode(code, ns, ns);
Py_DECREF(code);
if (result == NULL) {
return -1;
}
@ -523,17 +439,16 @@ _run_script(PyObject *ns, const char *codestr, Py_ssize_t codestrlen, int flags)
}
static int
_run_in_interpreter(PyInterpreterState *interp,
const char *codestr, Py_ssize_t codestrlen,
PyObject *shareables, int flags,
_exec_in_interpreter(PyThreadState *tstate, PyInterpreterState *interp,
_PyXIData_t *script, PyObject *shareables,
PyObject **p_excinfo)
{
assert(!PyErr_Occurred());
assert(!_PyErr_Occurred(tstate));
_PyXI_session session = {0};
// Prep and switch interpreters.
if (_PyXI_Enter(&session, interp, shareables) < 0) {
if (PyErr_Occurred()) {
if (_PyErr_Occurred(tstate)) {
// If an error occured at this step, it means that interp
// was not prepared and switched.
return -1;
@ -548,7 +463,7 @@ _run_in_interpreter(PyInterpreterState *interp,
}
// Run the script.
int res = _run_script(session.main_ns, codestr, codestrlen, flags);
int res = _run_script(script, session.main_ns);
// Clean up and switch back.
_PyXI_Exit(&session);
@ -935,104 +850,23 @@ PyDoc_STRVAR(set___main___attrs_doc,
Bind the given attributes in the interpreter's __main__ module.");
static PyUnicodeObject *
convert_script_arg(PyThreadState *tstate,
PyObject *arg, const char *fname, const char *displayname,
const char *expected)
static void
unwrap_not_shareable(PyThreadState *tstate)
{
PyUnicodeObject *str = NULL;
if (PyUnicode_CheckExact(arg)) {
str = (PyUnicodeObject *)Py_NewRef(arg);
PyObject *exctype = _PyXIData_GetNotShareableErrorType(tstate);
if (!_PyErr_ExceptionMatches(tstate, exctype)) {
return;
}
else if (PyUnicode_Check(arg)) {
// XXX str = PyUnicode_FromObject(arg);
str = (PyUnicodeObject *)Py_NewRef(arg);
}
else {
_PyArg_BadArgument(fname, displayname, expected, arg);
return NULL;
}
const char *err = check_code_str(str);
if (err != NULL) {
Py_DECREF(str);
_PyErr_Format(tstate, PyExc_ValueError,
"%.200s(): bad script text (%s)", fname, err);
return NULL;
}
return str;
}
static PyCodeObject *
convert_code_arg(PyThreadState *tstate,
PyObject *arg, const char *fname, const char *displayname,
const char *expected)
{
PyObject *cause;
PyCodeObject *code = NULL;
if (PyFunction_Check(arg)) {
// For now we allow globals, so we can't use
// _PyFunction_VerifyStateless().
PyObject *codeobj = PyFunction_GetCode(arg);
if (_PyCode_VerifyStateless(
tstate, (PyCodeObject *)codeobj, NULL, NULL, NULL) < 0) {
goto chained;
}
code = (PyCodeObject *)Py_NewRef(codeobj);
}
else if (PyCode_Check(arg)) {
if (_PyCode_VerifyStateless(
tstate, (PyCodeObject *)arg, NULL, NULL, NULL) < 0) {
goto chained;
}
code = (PyCodeObject *)Py_NewRef(arg);
}
else {
_PyArg_BadArgument(fname, displayname, expected, arg);
return NULL;
}
return code;
chained:
cause = _PyErr_GetRaisedException(tstate);
assert(cause != NULL);
_PyArg_BadArgument(fname, displayname, expected, arg);
PyObject *exc = _PyErr_GetRaisedException(tstate);
PyException_SetCause(exc, cause);
PyObject *cause = PyException_GetCause(exc);
if (cause != NULL) {
Py_DECREF(exc);
exc = cause;
}
else {
assert(PyException_GetContext(exc) == NULL);
}
_PyErr_SetRaisedException(tstate, exc);
return NULL;
}
static int
_interp_exec(PyObject *self, PyInterpreterState *interp,
PyObject *code_arg, PyObject *shared_arg, PyObject **p_excinfo)
{
if (shared_arg != NULL && !PyDict_CheckExact(shared_arg)) {
PyErr_SetString(PyExc_TypeError, "expected 'shared' to be a dict");
return -1;
}
// Extract code.
Py_ssize_t codestrlen = -1;
PyObject *bytes_obj = NULL;
int flags = 0;
const char *codestr = get_code_str(code_arg,
&codestrlen, &bytes_obj, &flags);
if (codestr == NULL) {
return -1;
}
// Run the code in the interpreter.
int res = _run_in_interpreter(interp, codestr, codestrlen,
shared_arg, flags, p_excinfo);
Py_XDECREF(bytes_obj);
if (res < 0) {
return -1;
}
return 0;
}
static PyObject *
@ -1045,8 +879,9 @@ interp_exec(PyObject *self, PyObject *args, PyObject *kwds)
PyObject *shared = NULL;
int restricted = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
"OO|O$p:" FUNCNAME, kwlist,
&id, &code, &shared, &restricted))
"OO|O!$p:" FUNCNAME, kwlist,
&id, &code, &PyDict_Type, &shared,
&restricted))
{
return NULL;
}
@ -1058,22 +893,17 @@ interp_exec(PyObject *self, PyObject *args, PyObject *kwds)
return NULL;
}
const char *expected = "a string, a function, or a code object";
if (PyUnicode_Check(code)) {
code = (PyObject *)convert_script_arg(tstate, code, FUNCNAME,
"argument 2", expected);
}
else {
code = (PyObject *)convert_code_arg(tstate, code, FUNCNAME,
"argument 2", expected);
}
if (code == NULL) {
// We don't need the script to be "pure", which means it can use
// global variables. They will be resolved against __main__.
_PyXIData_t xidata = {0};
if (_PyCode_GetScriptXIData(tstate, code, &xidata) < 0) {
unwrap_not_shareable(tstate);
return NULL;
}
PyObject *excinfo = NULL;
int res = _interp_exec(self, interp, code, shared, &excinfo);
Py_DECREF(code);
int res = _exec_in_interpreter(tstate, interp, &xidata, shared, &excinfo);
_PyXIData_Release(&xidata);
if (res < 0) {
assert((excinfo == NULL) != (PyErr_Occurred() == NULL));
return excinfo;
@ -1109,8 +939,9 @@ interp_run_string(PyObject *self, PyObject *args, PyObject *kwds)
PyObject *shared = NULL;
int restricted = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
"OU|O$p:" FUNCNAME, kwlist,
&id, &script, &shared, &restricted))
"OU|O!$p:" FUNCNAME, kwlist,
&id, &script, &PyDict_Type, &shared,
&restricted))
{
return NULL;
}
@ -1122,15 +953,20 @@ interp_run_string(PyObject *self, PyObject *args, PyObject *kwds)
return NULL;
}
script = (PyObject *)convert_script_arg(tstate, script, FUNCNAME,
"argument 2", "a string");
if (script == NULL) {
if (PyFunction_Check(script) || PyCode_Check(script)) {
_PyArg_BadArgument(FUNCNAME, "argument 2", "a string", script);
return NULL;
}
_PyXIData_t xidata = {0};
if (_PyCode_GetScriptXIData(tstate, script, &xidata) < 0) {
unwrap_not_shareable(tstate);
return NULL;
}
PyObject *excinfo = NULL;
int res = _interp_exec(self, interp, script, shared, &excinfo);
Py_DECREF(script);
int res = _exec_in_interpreter(tstate, interp, &xidata, shared, &excinfo);
_PyXIData_Release(&xidata);
if (res < 0) {
assert((excinfo == NULL) != (PyErr_Occurred() == NULL));
return excinfo;
@ -1156,8 +992,9 @@ interp_run_func(PyObject *self, PyObject *args, PyObject *kwds)
PyObject *shared = NULL;
int restricted = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
"OO|O$p:" FUNCNAME, kwlist,
&id, &func, &shared, &restricted))
"OO|O!$p:" FUNCNAME, kwlist,
&id, &func, &PyDict_Type, &shared,
&restricted))
{
return NULL;
}
@ -1169,16 +1006,29 @@ interp_run_func(PyObject *self, PyObject *args, PyObject *kwds)
return NULL;
}
PyCodeObject *code = convert_code_arg(tstate, func, FUNCNAME,
"argument 2",
"a function or a code object");
if (code == NULL) {
// We don't worry about checking globals. They will be resolved
// against __main__.
PyObject *code;
if (PyFunction_Check(func)) {
code = PyFunction_GET_CODE(func);
}
else if (PyCode_Check(func)) {
code = func;
}
else {
_PyArg_BadArgument(FUNCNAME, "argument 2", "a function", func);
return NULL;
}
_PyXIData_t xidata = {0};
if (_PyCode_GetScriptXIData(tstate, code, &xidata) < 0) {
unwrap_not_shareable(tstate);
return NULL;
}
PyObject *excinfo = NULL;
int res = _interp_exec(self, interp, (PyObject *)code, shared, &excinfo);
Py_DECREF(code);
int res = _exec_in_interpreter(tstate, interp, &xidata, shared, &excinfo);
_PyXIData_Release(&xidata);
if (res < 0) {
assert((excinfo == NULL) != (PyErr_Occurred() == NULL));
return excinfo;
@ -1231,15 +1081,15 @@ interp_call(PyObject *self, PyObject *args, PyObject *kwds)
return NULL;
}
PyObject *code = (PyObject *)convert_code_arg(tstate, callable, FUNCNAME,
"argument 2", "a function");
if (code == NULL) {
_PyXIData_t xidata = {0};
if (_PyCode_GetPureScriptXIData(tstate, callable, &xidata) < 0) {
unwrap_not_shareable(tstate);
return NULL;
}
PyObject *excinfo = NULL;
int res = _interp_exec(self, interp, code, NULL, &excinfo);
Py_DECREF(code);
int res = _exec_in_interpreter(tstate, interp, &xidata, NULL, &excinfo);
_PyXIData_Release(&xidata);
if (res < 0) {
assert((excinfo == NULL) != (PyErr_Occurred() == NULL));
return excinfo;