mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
[3.14] gh-132775: Add _PyCode_GetScriptXIData() (gh-133676)
This converts functions, code, str, bytes, bytearray, and memoryview objects to PyCodeObject,
and ensure that the object looks like a script. That means no args, no return, and no closure.
_PyCode_GetPureScriptXIData() takes it a step further and ensures there are no globals.
We also add _PyObject_SupportedAsScript() to the internal C-API.
(cherry picked from commit c81fa2b9cd
, AKA gh-133480)
Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
This commit is contained in:
parent
dc1a4dda88
commit
1059548686
8 changed files with 366 additions and 0 deletions
|
@ -6,8 +6,10 @@
|
|||
#include "osdefs.h" // MAXPATHLEN
|
||||
#include "pycore_ceval.h" // _Py_simple_func
|
||||
#include "pycore_crossinterp.h" // _PyXIData_t
|
||||
#include "pycore_function.h" // _PyFunction_VerifyStateless()
|
||||
#include "pycore_initconfig.h" // _PyStatus_OK()
|
||||
#include "pycore_namespace.h" // _PyNamespace_New()
|
||||
#include "pycore_pythonrun.h" // _Py_SourceAsString()
|
||||
#include "pycore_typeobject.h" // _PyStaticType_InitBuiltin()
|
||||
|
||||
|
||||
|
@ -784,6 +786,131 @@ _PyMarshal_GetXIData(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata)
|
|||
}
|
||||
|
||||
|
||||
/* script wrapper */
|
||||
|
||||
static int
|
||||
verify_script(PyThreadState *tstate, PyCodeObject *co, int checked, int pure)
|
||||
{
|
||||
// Make sure it isn't a closure and (optionally) doesn't use globals.
|
||||
PyObject *builtins = NULL;
|
||||
if (pure) {
|
||||
builtins = _PyEval_GetBuiltins(tstate);
|
||||
assert(builtins != NULL);
|
||||
}
|
||||
if (checked) {
|
||||
assert(_PyCode_VerifyStateless(tstate, co, NULL, NULL, builtins) == 0);
|
||||
}
|
||||
else if (_PyCode_VerifyStateless(tstate, co, NULL, NULL, builtins) < 0) {
|
||||
return -1;
|
||||
}
|
||||
// Make sure it doesn't have args.
|
||||
if (co->co_argcount > 0
|
||||
|| co->co_posonlyargcount > 0
|
||||
|| co->co_kwonlyargcount > 0
|
||||
|| co->co_flags & (CO_VARARGS | CO_VARKEYWORDS))
|
||||
{
|
||||
_PyErr_SetString(tstate, PyExc_ValueError,
|
||||
"code with args not supported");
|
||||
return -1;
|
||||
}
|
||||
// Make sure it doesn't return anything.
|
||||
if (!_PyCode_ReturnsOnlyNone(co)) {
|
||||
_PyErr_SetString(tstate, PyExc_ValueError,
|
||||
"code that returns a value is not a script");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
get_script_xidata(PyThreadState *tstate, PyObject *obj, int pure,
|
||||
_PyXIData_t *xidata)
|
||||
{
|
||||
// Get the corresponding code object.
|
||||
PyObject *code = NULL;
|
||||
int checked = 0;
|
||||
if (PyCode_Check(obj)) {
|
||||
code = obj;
|
||||
Py_INCREF(code);
|
||||
}
|
||||
else if (PyFunction_Check(obj)) {
|
||||
code = PyFunction_GET_CODE(obj);
|
||||
assert(code != NULL);
|
||||
Py_INCREF(code);
|
||||
if (pure) {
|
||||
if (_PyFunction_VerifyStateless(tstate, obj) < 0) {
|
||||
goto error;
|
||||
}
|
||||
checked = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const char *filename = "<script>";
|
||||
int optimize = 0;
|
||||
PyCompilerFlags cf = _PyCompilerFlags_INIT;
|
||||
cf.cf_flags = PyCF_SOURCE_IS_UTF8;
|
||||
PyObject *ref = NULL;
|
||||
const char *script = _Py_SourceAsString(obj, "???", "???", &cf, &ref);
|
||||
if (script == NULL) {
|
||||
if (!_PyObject_SupportedAsScript(obj)) {
|
||||
// We discard the raised exception.
|
||||
_PyErr_Format(tstate, PyExc_TypeError,
|
||||
"unsupported script %R", obj);
|
||||
}
|
||||
goto error;
|
||||
}
|
||||
code = Py_CompileStringExFlags(
|
||||
script, filename, Py_file_input, &cf, optimize);
|
||||
Py_XDECREF(ref);
|
||||
if (code == NULL) {
|
||||
goto error;
|
||||
}
|
||||
// Compiled text can't have args or any return statements,
|
||||
// nor be a closure. It can use globals though.
|
||||
if (!pure) {
|
||||
// We don't need to check for globals either.
|
||||
checked = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure it's actually a script.
|
||||
if (verify_script(tstate, (PyCodeObject *)code, checked, pure) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Convert the code object.
|
||||
int res = _PyCode_GetXIData(tstate, code, xidata);
|
||||
Py_DECREF(code);
|
||||
if (res < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error:
|
||||
Py_XDECREF(code);
|
||||
PyObject *cause = _PyErr_GetRaisedException(tstate);
|
||||
assert(cause != NULL);
|
||||
_set_xid_lookup_failure(
|
||||
tstate, NULL, "object not a valid script", cause);
|
||||
Py_DECREF(cause);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
_PyCode_GetScriptXIData(PyThreadState *tstate,
|
||||
PyObject *obj, _PyXIData_t *xidata)
|
||||
{
|
||||
return get_script_xidata(tstate, obj, 0, xidata);
|
||||
}
|
||||
|
||||
int
|
||||
_PyCode_GetPureScriptXIData(PyThreadState *tstate,
|
||||
PyObject *obj, _PyXIData_t *xidata)
|
||||
{
|
||||
return get_script_xidata(tstate, obj, 1, xidata);
|
||||
}
|
||||
|
||||
|
||||
/* using cross-interpreter data */
|
||||
|
||||
PyObject *
|
||||
|
|
|
@ -1524,6 +1524,26 @@ Py_CompileStringExFlags(const char *str, const char *filename_str, int start,
|
|||
return co;
|
||||
}
|
||||
|
||||
int
|
||||
_PyObject_SupportedAsScript(PyObject *cmd)
|
||||
{
|
||||
if (PyUnicode_Check(cmd)) {
|
||||
return 1;
|
||||
}
|
||||
else if (PyBytes_Check(cmd)) {
|
||||
return 1;
|
||||
}
|
||||
else if (PyByteArray_Check(cmd)) {
|
||||
return 1;
|
||||
}
|
||||
else if (PyObject_CheckBuffer(cmd)) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
_Py_SourceAsString(PyObject *cmd, const char *funcname, const char *what, PyCompilerFlags *cf, PyObject **cmd_copy)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue