diff options
Diffstat (limited to 'contrib/tools/python3/Objects/codeobject.c')
| -rw-r--r-- | contrib/tools/python3/Objects/codeobject.c | 738 |
1 files changed, 530 insertions, 208 deletions
diff --git a/contrib/tools/python3/Objects/codeobject.c b/contrib/tools/python3/Objects/codeobject.c index 1681d97613e..8a0b1027b94 100644 --- a/contrib/tools/python3/Objects/codeobject.c +++ b/contrib/tools/python3/Objects/codeobject.c @@ -1,17 +1,21 @@ -#include <stdbool.h> - #include "Python.h" #include "opcode.h" -#include "structmember.h" // PyMemberDef + #include "pycore_code.h" // _PyCodeConstructor #include "pycore_frame.h" // FRAME_SPECIALS_SIZE +#include "pycore_hashtable.h" // _Py_hashtable_t +#include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_interp.h" // PyInterpreterState.co_extra_freefuncs -#include "pycore_opcode.h" // _PyOpcode_Deopt +#include "pycore_object.h" // _PyObject_SetDeferredRefcount +#include "pycore_opcode_metadata.h" // _PyOpcode_Deopt, _PyOpcode_Caches +#include "pycore_opcode_utils.h" // RESUME_AT_FUNC_START #include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_setobject.h" // _PySet_NextEntry() #include "pycore_tuple.h" // _PyTuple_ITEMS() +#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS() #include "clinic/codeobject.c.h" -static PyObject* code_repr(PyCodeObject *co); +#include <stdbool.h> static const char * code_event_name(PyCodeEvent event) { @@ -40,21 +44,9 @@ notify_code_watchers(PyCodeEvent event, PyCodeObject *co) // callback must be non-null if the watcher bit is set assert(cb != NULL); if (cb(event, co) < 0) { - // Don't risk resurrecting the object if an unraisablehook keeps - // a reference; pass a string as context. - PyObject *context = NULL; - PyObject *repr = code_repr(co); - if (repr) { - context = PyUnicode_FromFormat( - "%s watcher callback for %U", - code_event_name(event), repr); - Py_DECREF(repr); - } - if (context == NULL) { - context = Py_NewRef(Py_None); - } - PyErr_WriteUnraisable(context); - Py_DECREF(context); + PyErr_FormatUnraisable( + "Exception ignored in %s watcher callback for %R", + code_event_name(event), co); } } i++; @@ -111,10 +103,20 @@ PyCode_ClearWatcher(int watcher_id) * generic helpers ******************/ -/* all_name_chars(s): true iff s matches [a-zA-Z0-9_]* */ static int -all_name_chars(PyObject *o) +should_intern_string(PyObject *o) { +#ifdef Py_GIL_DISABLED + // The free-threaded build interns (and immortalizes) all string constants + // unless we've disabled immortalizing objects that use deferred reference + // counting. + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (_Py_atomic_load_int(&interp->gc.immortalize) < 0) { + return 1; + } +#endif + + // compute if s matches [a-zA-Z0-9_] const unsigned char *s, *e; if (!PyUnicode_IS_ASCII(o)) @@ -129,6 +131,42 @@ all_name_chars(PyObject *o) return 1; } +#ifdef Py_GIL_DISABLED +static PyObject *intern_one_constant(PyObject *op); + +// gh-130851: In the free threading build, we intern and immortalize most +// constants, except code objects. However, users can generate code objects +// with arbitrary co_consts. We don't want to immortalize or intern unexpected +// constants or tuples/sets containing unexpected constants. +static int +should_immortalize_constant(PyObject *v) +{ + // Only immortalize containers if we've already immortalized all their + // elements. + if (PyTuple_CheckExact(v)) { + for (Py_ssize_t i = PyTuple_GET_SIZE(v); --i >= 0; ) { + if (!_Py_IsImmortal(PyTuple_GET_ITEM(v, i))) { + return 0; + } + } + return 1; + } + else if (PyFrozenSet_CheckExact(v)) { + PyObject *item; + Py_hash_t hash; + Py_ssize_t pos = 0; + while (_PySet_NextEntry(v, &pos, &item, &hash)) { + if (!_Py_IsImmortal(item)) { + return 0; + } + } + return 1; + } + return (PyLong_CheckExact(v) || PyFloat_CheckExact(v) || + PyComplex_Check(v) || PyBytes_CheckExact(v)); +} +#endif + static int intern_strings(PyObject *tuple) { @@ -147,19 +185,17 @@ intern_strings(PyObject *tuple) return 0; } -/* Intern selected string constants */ +/* Intern constants. In the default build, this interns selected string + constants. In the free-threaded build, this also interns non-string + constants. */ static int -intern_string_constants(PyObject *tuple, int *modified) +intern_constants(PyObject *tuple, int *modified) { PyInterpreterState *interp = _PyInterpreterState_GET(); for (Py_ssize_t i = PyTuple_GET_SIZE(tuple); --i >= 0; ) { PyObject *v = PyTuple_GET_ITEM(tuple, i); if (PyUnicode_CheckExact(v)) { - if (PyUnicode_READY(v) == -1) { - return -1; - } - - if (all_name_chars(v)) { + if (should_intern_string(v)) { PyObject *w = v; _PyUnicode_InternMortal(interp, &v); if (w != v) { @@ -171,7 +207,7 @@ intern_string_constants(PyObject *tuple, int *modified) } } else if (PyTuple_CheckExact(v)) { - if (intern_string_constants(v, NULL) < 0) { + if (intern_constants(v, NULL) < 0) { return -1; } } @@ -182,7 +218,7 @@ intern_string_constants(PyObject *tuple, int *modified) return -1; } int tmp_modified = 0; - if (intern_string_constants(tmp, &tmp_modified) < 0) { + if (intern_constants(tmp, &tmp_modified) < 0) { Py_DECREF(tmp); return -1; } @@ -201,6 +237,59 @@ intern_string_constants(PyObject *tuple, int *modified) } Py_DECREF(tmp); } +#ifdef Py_GIL_DISABLED + else if (PySlice_Check(v)) { + PySliceObject *slice = (PySliceObject *)v; + PyObject *tmp = PyTuple_New(3); + if (tmp == NULL) { + return -1; + } + PyTuple_SET_ITEM(tmp, 0, Py_NewRef(slice->start)); + PyTuple_SET_ITEM(tmp, 1, Py_NewRef(slice->stop)); + PyTuple_SET_ITEM(tmp, 2, Py_NewRef(slice->step)); + int tmp_modified = 0; + if (intern_constants(tmp, &tmp_modified) < 0) { + Py_DECREF(tmp); + return -1; + } + if (tmp_modified) { + v = PySlice_New(PyTuple_GET_ITEM(tmp, 0), + PyTuple_GET_ITEM(tmp, 1), + PyTuple_GET_ITEM(tmp, 2)); + if (v == NULL) { + Py_DECREF(tmp); + return -1; + } + PyTuple_SET_ITEM(tuple, i, v); + Py_DECREF(slice); + if (modified) { + *modified = 1; + } + } + Py_DECREF(tmp); + } + + // Intern non-string consants in the free-threaded build, but only if + // we are also immortalizing objects that use deferred reference + // counting. + PyThreadState *tstate = PyThreadState_GET(); + if (!_Py_IsImmortal(v) && !PyUnicode_CheckExact(v) && + should_immortalize_constant(v) && + _Py_atomic_load_int(&tstate->interp->gc.immortalize) >= 0) + { + PyObject *interned = intern_one_constant(v); + if (interned == NULL) { + return -1; + } + else if (interned != v) { + PyTuple_SET_ITEM(tuple, i, interned); + Py_SETREF(v, interned); + if (modified) { + *modified = 1; + } + } + } +#endif } return 0; } @@ -248,21 +337,32 @@ validate_and_copy_tuple(PyObject *tup) } static int -init_co_cached(PyCodeObject *self) { - if (self->_co_cached == NULL) { - self->_co_cached = PyMem_New(_PyCoCached, 1); - if (self->_co_cached == NULL) { +init_co_cached(PyCodeObject *self) +{ + _PyCoCached *cached = FT_ATOMIC_LOAD_PTR(self->_co_cached); + if (cached != NULL) { + return 0; + } + + Py_BEGIN_CRITICAL_SECTION(self); + cached = self->_co_cached; + if (cached == NULL) { + cached = PyMem_New(_PyCoCached, 1); + if (cached == NULL) { PyErr_NoMemory(); - return -1; } - self->_co_cached->_co_code = NULL; - self->_co_cached->_co_cellvars = NULL; - self->_co_cached->_co_freevars = NULL; - self->_co_cached->_co_varnames = NULL; + else { + cached->_co_code = NULL; + cached->_co_cellvars = NULL; + cached->_co_freevars = NULL; + cached->_co_varnames = NULL; + FT_ATOMIC_STORE_PTR(self->_co_cached, cached); + } } - return 0; - + Py_END_CRITICAL_SECTION(); + return cached != NULL ? 0 : -1; } + /****************** * _PyCode_New() ******************/ @@ -400,10 +500,17 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) int nlocals, ncellvars, nfreevars; get_localsplus_counts(con->localsplusnames, con->localspluskinds, &nlocals, &ncellvars, &nfreevars); + if (con->stacksize == 0) { + con->stacksize = 1; + } + PyInterpreterState *interp = _PyInterpreterState_GET(); co->co_filename = Py_NewRef(con->filename); co->co_name = Py_NewRef(con->name); co->co_qualname = Py_NewRef(con->qualname); + _PyUnicode_InternMortal(interp, &co->co_filename); + _PyUnicode_InternMortal(interp, &co->co_name); + _PyUnicode_InternMortal(interp, &co->co_qualname); co->co_flags = con->flags; co->co_firstlineno = con->firstlineno; @@ -429,16 +536,23 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) co->co_framesize = nlocalsplus + con->stacksize + FRAME_SPECIALS_SIZE; co->co_ncellvars = ncellvars; co->co_nfreevars = nfreevars; - co->co_version = _Py_next_func_version; - if (_Py_next_func_version != 0) { - _Py_next_func_version++; +#ifdef Py_GIL_DISABLED + PyMutex_Lock(&interp->func_state.mutex); +#endif + co->co_version = interp->func_state.next_version; + if (interp->func_state.next_version != 0) { + interp->func_state.next_version++; } +#ifdef Py_GIL_DISABLED + PyMutex_Unlock(&interp->func_state.mutex); +#endif co->_co_monitoring = NULL; co->_co_instrumentation_version = 0; /* not set */ co->co_weakreflist = NULL; co->co_extra = NULL; co->_co_cached = NULL; + co->co_executors = NULL; memcpy(_PyCode_CODE(co), PyBytes_AS_STRING(con->code), PyBytes_GET_SIZE(con->code)); @@ -543,29 +657,41 @@ remove_column_info(PyObject *locations) return res; } -/* The caller is responsible for ensuring that the given data is valid. */ - -PyCodeObject * -_PyCode_New(struct _PyCodeConstructor *con) +static int +intern_code_constants(struct _PyCodeConstructor *con) { - /* Ensure that strings are ready Unicode string */ - if (PyUnicode_READY(con->name) < 0) { - return NULL; - } - if (PyUnicode_READY(con->qualname) < 0) { - return NULL; - } - if (PyUnicode_READY(con->filename) < 0) { - return NULL; - } - +#ifdef Py_GIL_DISABLED + PyInterpreterState *interp = _PyInterpreterState_GET(); + struct _py_code_state *state = &interp->code_state; + PyMutex_Lock(&state->mutex); +#endif if (intern_strings(con->names) < 0) { - return NULL; + goto error; } - if (intern_string_constants(con->consts, NULL) < 0) { - return NULL; + if (intern_constants(con->consts, NULL) < 0) { + goto error; } if (intern_strings(con->localsplusnames) < 0) { + goto error; + } +#ifdef Py_GIL_DISABLED + PyMutex_Unlock(&state->mutex); +#endif + return 0; + +error: +#ifdef Py_GIL_DISABLED + PyMutex_Unlock(&state->mutex); +#endif + return -1; +} + +/* The caller is responsible for ensuring that the given data is valid. */ + +PyCodeObject * +_PyCode_New(struct _PyCodeConstructor *con) +{ + if (intern_code_constants(con) < 0) { return NULL; } @@ -581,13 +707,22 @@ _PyCode_New(struct _PyCodeConstructor *con) } Py_ssize_t size = PyBytes_GET_SIZE(con->code) / sizeof(_Py_CODEUNIT); - PyCodeObject *co = PyObject_NewVar(PyCodeObject, &PyCode_Type, size); + PyCodeObject *co; +#ifdef Py_GIL_DISABLED + co = PyObject_GC_NewVar(PyCodeObject, &PyCode_Type, size); +#else + co = PyObject_NewVar(PyCodeObject, &PyCode_Type, size); +#endif if (co == NULL) { Py_XDECREF(replacement_locations); PyErr_NoMemory(); return NULL; } init_code(co, con); +#ifdef Py_GIL_DISABLED + _PyObject_SetDeferredRefcount((PyObject *)co); + _PyObject_GC_TRACK(co); +#endif Py_XDECREF(replacement_locations); return co; } @@ -773,7 +908,7 @@ PyUnstable_Code_New(int argcount, int kwonlyargcount, // test.test_code.CodeLocationTest.test_code_new_empty to keep it in sync! static const uint8_t assert0[6] = { - RESUME, 0, + RESUME, RESUME_AT_FUNC_START, LOAD_ASSERTION_ERROR, 0, RAISE_VARARGS, 1 }; @@ -853,7 +988,27 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq) if (addrq < 0) { return co->co_firstlineno; } - assert(addrq >= 0 && addrq <= _PyCode_NBYTES(co)); + if (co->_co_monitoring && co->_co_monitoring->lines) { + return _Py_Instrumentation_GetLine(co, addrq/sizeof(_Py_CODEUNIT)); + } + // assert(addrq >= 0 && addrq < _PyCode_NBYTES(co)); + PyCodeAddressRange bounds; + _PyCode_InitAddressRange(co, &bounds); + return _PyCode_CheckLineNumber(addrq, &bounds); +} + +int +_PyCode_SafeAddr2Line(PyCodeObject *co, int addrq) +{ + if (addrq < 0) { + return co->co_firstlineno; + } + if (co->_co_monitoring && co->_co_monitoring->lines) { + return _Py_Instrumentation_GetLine(co, addrq/sizeof(_Py_CODEUNIT)); + } + if (!(addrq >= 0 && addrq < _PyCode_NBYTES(co))) { + return -1; + } PyCodeAddressRange bounds; _PyCode_InitAddressRange(co, &bounds); return _PyCode_CheckLineNumber(addrq, &bounds); @@ -1453,16 +1608,21 @@ get_cached_locals(PyCodeObject *co, PyObject **cached_field, { assert(cached_field != NULL); assert(co->_co_cached != NULL); - if (*cached_field != NULL) { - return Py_NewRef(*cached_field); + PyObject *varnames = FT_ATOMIC_LOAD_PTR(*cached_field); + if (varnames != NULL) { + return Py_NewRef(varnames); } - assert(*cached_field == NULL); - PyObject *varnames = get_localsplus_names(co, kind, num); + + Py_BEGIN_CRITICAL_SECTION(co); + varnames = *cached_field; if (varnames == NULL) { - return NULL; + varnames = get_localsplus_names(co, kind, num); + if (varnames != NULL) { + FT_ATOMIC_STORE_PTR(*cached_field, varnames); + } } - *cached_field = Py_NewRef(varnames); - return varnames; + Py_END_CRITICAL_SECTION(); + return Py_XNewRef(varnames); } PyObject * @@ -1510,12 +1670,42 @@ PyCode_GetFreevars(PyCodeObject *code) return _PyCode_GetFreevars(code); } +#ifdef _Py_TIER2 + +static void +clear_executors(PyCodeObject *co) +{ + assert(co->co_executors); + for (int i = 0; i < co->co_executors->size; i++) { + if (co->co_executors->executors[i]) { + _Py_ExecutorDetach(co->co_executors->executors[i]); + assert(co->co_executors->executors[i] == NULL); + } + } + PyMem_Free(co->co_executors); + co->co_executors = NULL; +} + +void +_PyCode_Clear_Executors(PyCodeObject *code) +{ + clear_executors(code); +} + +#endif + static void deopt_code(PyCodeObject *code, _Py_CODEUNIT *instructions) { Py_ssize_t len = Py_SIZE(code); for (int i = 0; i < len; i++) { int opcode = _Py_GetBaseOpcode(code, i); + if (opcode == ENTER_EXECUTOR) { + _PyExecutorObject *exec = code->co_executors->executors[instructions[i].op.arg]; + opcode = _PyOpcode_Deopt[exec->vm_data.opcode]; + instructions[i].op.arg = exec->vm_data.oparg; + } + assert(opcode != ENTER_EXECUTOR); int caches = _PyOpcode_Caches[opcode]; instructions[i].op.code = opcode; for (int j = 1; j <= caches; j++) { @@ -1531,18 +1721,26 @@ _PyCode_GetCode(PyCodeObject *co) if (init_co_cached(co)) { return NULL; } - if (co->_co_cached->_co_code != NULL) { - return Py_NewRef(co->_co_cached->_co_code); + + _PyCoCached *cached = co->_co_cached; + PyObject *code = FT_ATOMIC_LOAD_PTR(cached->_co_code); + if (code != NULL) { + return Py_NewRef(code); } - PyObject *code = PyBytes_FromStringAndSize((const char *)_PyCode_CODE(co), - _PyCode_NBYTES(co)); + + Py_BEGIN_CRITICAL_SECTION(co); + code = cached->_co_code; if (code == NULL) { - return NULL; + code = PyBytes_FromStringAndSize((const char *)_PyCode_CODE(co), + _PyCode_NBYTES(co)); + if (code != NULL) { + deopt_code(co, (_Py_CODEUNIT *)PyBytes_AS_STRING(code)); + assert(cached->_co_code == NULL); + FT_ATOMIC_STORE_PTR(cached->_co_code, code); + } } - deopt_code(co, (_Py_CODEUNIT *)PyBytes_AS_STRING(code)); - assert(co->_co_cached->_co_code == NULL); - co->_co_cached->_co_code = Py_NewRef(code); - return code; + Py_END_CRITICAL_SECTION(); + return Py_XNewRef(code); } PyObject * @@ -1700,15 +1898,17 @@ free_monitoring_data(_PyCoMonitoringData *data) static void code_dealloc(PyCodeObject *co) { - assert(Py_REFCNT(co) == 0); - Py_SET_REFCNT(co, 1); + _PyObject_ResurrectStart((PyObject *)co); notify_code_watchers(PY_CODE_EVENT_DESTROY, co); - if (Py_REFCNT(co) > 1) { - Py_SET_REFCNT(co, Py_REFCNT(co) - 1); + if (_PyObject_ResurrectEnd((PyObject *)co)) { return; } - Py_SET_REFCNT(co, 0); +#ifdef Py_GIL_DISABLED + PyObject_GC_UnTrack(co); +#endif + + _PyFunction_ClearCodeByVersion(co->co_version); if (co->co_extra != NULL) { PyInterpreterState *interp = _PyInterpreterState_GET(); _PyCodeObjectExtra *co_extra = co->co_extra; @@ -1723,6 +1923,11 @@ code_dealloc(PyCodeObject *co) PyMem_Free(co_extra); } +#ifdef _Py_TIER2 + if (co->co_executors != NULL) { + clear_executors(co); + } +#endif Py_XDECREF(co->co_consts); Py_XDECREF(co->co_names); @@ -1740,13 +1945,20 @@ code_dealloc(PyCodeObject *co) Py_XDECREF(co->_co_cached->_co_varnames); PyMem_Free(co->_co_cached); } - if (co->co_weakreflist != NULL) { - PyObject_ClearWeakRefs((PyObject*)co); - } + FT_CLEAR_WEAKREFS((PyObject*)co, co->co_weakreflist); free_monitoring_data(co->_co_monitoring); PyObject_Free(co); } +#ifdef Py_GIL_DISABLED +static int +code_traverse(PyCodeObject *co, visitproc visit, void *arg) +{ + Py_VISIT(co->co_consts); + return 0; +} +#endif + static PyObject * code_repr(PyCodeObject *co) { @@ -1802,13 +2014,31 @@ code_richcompare(PyObject *self, PyObject *other, int op) for (int i = 0; i < Py_SIZE(co); i++) { _Py_CODEUNIT co_instr = _PyCode_CODE(co)[i]; _Py_CODEUNIT cp_instr = _PyCode_CODE(cp)[i]; - co_instr.op.code = _Py_GetBaseOpcode(co, i); - cp_instr.op.code = _Py_GetBaseOpcode(cp, i); - eq = co_instr.cache == cp_instr.cache; - if (!eq) { + uint8_t co_code = _Py_GetBaseOpcode(co, i); + uint8_t co_arg = co_instr.op.arg; + uint8_t cp_code = _Py_GetBaseOpcode(cp, i); + uint8_t cp_arg = cp_instr.op.arg; + + if (co_code == ENTER_EXECUTOR) { + const int exec_index = co_arg; + _PyExecutorObject *exec = co->co_executors->executors[exec_index]; + co_code = _PyOpcode_Deopt[exec->vm_data.opcode]; + co_arg = exec->vm_data.oparg; + } + assert(co_code != ENTER_EXECUTOR); + + if (cp_code == ENTER_EXECUTOR) { + const int exec_index = cp_arg; + _PyExecutorObject *exec = cp->co_executors->executors[exec_index]; + cp_code = _PyOpcode_Deopt[exec->vm_data.opcode]; + cp_arg = exec->vm_data.oparg; + } + assert(cp_code != ENTER_EXECUTOR); + + if (co_code != cp_code || co_arg != cp_arg) { goto unequal; } - i += _PyOpcode_Caches[co_instr.op.code]; + i += _PyOpcode_Caches[co_code]; } /* compare constants */ @@ -1864,7 +2094,7 @@ code_hash(PyCodeObject *co) Py_uhash_t uhash = 20221211; #define SCRAMBLE_IN(H) do { \ uhash ^= (Py_uhash_t)(H); \ - uhash *= _PyHASH_MULTIPLIER; \ + uhash *= PyHASH_MULTIPLIER; \ } while (0) #define SCRAMBLE_IN_HASH(EXPR) do { \ Py_hash_t h = PyObject_Hash(EXPR); \ @@ -1887,10 +2117,22 @@ code_hash(PyCodeObject *co) SCRAMBLE_IN(co->co_firstlineno); SCRAMBLE_IN(Py_SIZE(co)); for (int i = 0; i < Py_SIZE(co); i++) { - int deop = _Py_GetBaseOpcode(co, i); - SCRAMBLE_IN(deop); - SCRAMBLE_IN(_PyCode_CODE(co)[i].op.arg); - i += _PyOpcode_Caches[deop]; + _Py_CODEUNIT co_instr = _PyCode_CODE(co)[i]; + uint8_t co_code = co_instr.op.code; + uint8_t co_arg = co_instr.op.arg; + if (co_code == ENTER_EXECUTOR) { + _PyExecutorObject *exec = co->co_executors->executors[co_arg]; + assert(exec != NULL); + assert(exec->vm_data.opcode != ENTER_EXECUTOR); + co_code = _PyOpcode_Deopt[exec->vm_data.opcode]; + co_arg = exec->vm_data.oparg; + } + else { + co_code = _Py_GetBaseOpcode(co, i); + } + SCRAMBLE_IN(co_code); + SCRAMBLE_IN(co_arg); + i += _PyOpcode_Caches[co_code]; } if ((Py_hash_t)uhash == -1) { return -2; @@ -1902,20 +2144,20 @@ code_hash(PyCodeObject *co) #define OFF(x) offsetof(PyCodeObject, x) static PyMemberDef code_memberlist[] = { - {"co_argcount", T_INT, OFF(co_argcount), READONLY}, - {"co_posonlyargcount", T_INT, OFF(co_posonlyargcount), READONLY}, - {"co_kwonlyargcount", T_INT, OFF(co_kwonlyargcount), READONLY}, - {"co_stacksize", T_INT, OFF(co_stacksize), READONLY}, - {"co_flags", T_INT, OFF(co_flags), READONLY}, - {"co_nlocals", T_INT, OFF(co_nlocals), READONLY}, - {"co_consts", T_OBJECT, OFF(co_consts), READONLY}, - {"co_names", T_OBJECT, OFF(co_names), READONLY}, - {"co_filename", T_OBJECT, OFF(co_filename), READONLY}, - {"co_name", T_OBJECT, OFF(co_name), READONLY}, - {"co_qualname", T_OBJECT, OFF(co_qualname), READONLY}, - {"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY}, - {"co_linetable", T_OBJECT, OFF(co_linetable), READONLY}, - {"co_exceptiontable", T_OBJECT, OFF(co_exceptiontable), READONLY}, + {"co_argcount", Py_T_INT, OFF(co_argcount), Py_READONLY}, + {"co_posonlyargcount", Py_T_INT, OFF(co_posonlyargcount), Py_READONLY}, + {"co_kwonlyargcount", Py_T_INT, OFF(co_kwonlyargcount), Py_READONLY}, + {"co_stacksize", Py_T_INT, OFF(co_stacksize), Py_READONLY}, + {"co_flags", Py_T_INT, OFF(co_flags), Py_READONLY}, + {"co_nlocals", Py_T_INT, OFF(co_nlocals), Py_READONLY}, + {"co_consts", _Py_T_OBJECT, OFF(co_consts), Py_READONLY}, + {"co_names", _Py_T_OBJECT, OFF(co_names), Py_READONLY}, + {"co_filename", _Py_T_OBJECT, OFF(co_filename), Py_READONLY}, + {"co_name", _Py_T_OBJECT, OFF(co_name), Py_READONLY}, + {"co_qualname", _Py_T_OBJECT, OFF(co_qualname), Py_READONLY}, + {"co_firstlineno", Py_T_INT, OFF(co_firstlineno), Py_READONLY}, + {"co_linetable", _Py_T_OBJECT, OFF(co_linetable), Py_READONLY}, + {"co_exceptiontable", _Py_T_OBJECT, OFF(co_exceptiontable), Py_READONLY}, {NULL} /* Sentinel */ }; @@ -2135,6 +2377,8 @@ static struct PyMethodDef code_methods[] = { {"co_positions", (PyCFunction)code_positionsiterator, METH_NOARGS}, CODE_REPLACE_METHODDEF CODE__VARNAME_FROM_OPARG_METHODDEF + {"__replace__", _PyCFunction_CAST(code_replace), METH_FASTCALL|METH_KEYWORDS, + PyDoc_STR("__replace__($self, /, **changes)\n--\n\nThe same as replace().")}, {NULL, NULL} /* sentinel */ }; @@ -2159,9 +2403,17 @@ PyTypeObject PyCode_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ +#ifdef Py_GIL_DISABLED + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ +#else Py_TPFLAGS_DEFAULT, /* tp_flags */ +#endif code_new__doc__, /* tp_doc */ +#ifdef Py_GIL_DISABLED + (traverseproc)code_traverse, /* tp_traverse */ +#else 0, /* tp_traverse */ +#endif 0, /* tp_clear */ code_richcompare, /* tp_richcompare */ offsetof(PyCodeObject, co_weakreflist), /* tp_weaklistoffset */ @@ -2314,118 +2566,188 @@ _PyCode_ConstantKey(PyObject *op) return key; } -void -_PyStaticCode_Fini(PyCodeObject *co) +#ifdef Py_GIL_DISABLED +static PyObject * +intern_one_constant(PyObject *op) { - deopt_code(co, _PyCode_CODE(co)); - PyMem_Free(co->co_extra); - if (co->_co_cached != NULL) { - Py_CLEAR(co->_co_cached->_co_code); - Py_CLEAR(co->_co_cached->_co_cellvars); - Py_CLEAR(co->_co_cached->_co_freevars); - Py_CLEAR(co->_co_cached->_co_varnames); - PyMem_Free(co->_co_cached); - co->_co_cached = NULL; - } - co->co_extra = NULL; - if (co->co_weakreflist != NULL) { - PyObject_ClearWeakRefs((PyObject *)co); - co->co_weakreflist = NULL; + PyInterpreterState *interp = _PyInterpreterState_GET(); + _Py_hashtable_t *consts = interp->code_state.constants; + + assert(!PyUnicode_CheckExact(op)); // strings are interned separately + + _Py_hashtable_entry_t *entry = _Py_hashtable_get_entry(consts, op); + if (entry == NULL) { + if (_Py_hashtable_set(consts, op, op) != 0) { + PyErr_NoMemory(); + return NULL; + } + +#ifdef Py_REF_DEBUG + Py_ssize_t refcnt = Py_REFCNT(op); + if (refcnt != 1) { + // Adjust the reftotal to account for the fact that we only + // restore a single reference in _PyCode_Fini. + _Py_AddRefTotal(_PyThreadState_GET(), -(refcnt - 1)); + } +#endif + + _Py_SetImmortal(op); + return op; } - free_monitoring_data(co->_co_monitoring); - co->_co_monitoring = NULL; + + assert(_Py_IsImmortal(entry->value)); + return (PyObject *)entry->value; } -int -_PyStaticCode_Init(PyCodeObject *co) +static int +compare_constants(const void *key1, const void *key2) { - int res = intern_strings(co->co_names); - if (res < 0) { - return -1; + PyObject *op1 = (PyObject *)key1; + PyObject *op2 = (PyObject *)key2; + if (op1 == op2) { + return 1; } - res = intern_string_constants(co->co_consts, NULL); - if (res < 0) { - return -1; + if (Py_TYPE(op1) != Py_TYPE(op2)) { + return 0; } - res = intern_strings(co->co_localsplusnames); - if (res < 0) { - return -1; + // We compare container contents by identity because we have already + // internalized the items. + if (PyTuple_CheckExact(op1)) { + Py_ssize_t size = PyTuple_GET_SIZE(op1); + if (size != PyTuple_GET_SIZE(op2)) { + return 0; + } + for (Py_ssize_t i = 0; i < size; i++) { + if (PyTuple_GET_ITEM(op1, i) != PyTuple_GET_ITEM(op2, i)) { + return 0; + } + } + return 1; } - _PyCode_Quicken(co); + else if (PyFrozenSet_CheckExact(op1)) { + if (PySet_GET_SIZE(op1) != PySet_GET_SIZE(op2)) { + return 0; + } + Py_ssize_t pos1 = 0, pos2 = 0; + PyObject *obj1, *obj2; + Py_hash_t hash1, hash2; + while ((_PySet_NextEntry(op1, &pos1, &obj1, &hash1)) && + (_PySet_NextEntry(op2, &pos2, &obj2, &hash2))) + { + if (obj1 != obj2) { + return 0; + } + } + return 1; + } + else if (PySlice_Check(op1)) { + PySliceObject *s1 = (PySliceObject *)op1; + PySliceObject *s2 = (PySliceObject *)op2; + return (s1->start == s2->start && + s1->stop == s2->stop && + s1->step == s2->step); + } + else if (PyBytes_CheckExact(op1) || PyLong_CheckExact(op1)) { + return PyObject_RichCompareBool(op1, op2, Py_EQ); + } + else if (PyFloat_CheckExact(op1)) { + // Ensure that, for example, +0.0 and -0.0 are distinct + double f1 = PyFloat_AS_DOUBLE(op1); + double f2 = PyFloat_AS_DOUBLE(op2); + return memcmp(&f1, &f2, sizeof(double)) == 0; + } + else if (PyComplex_CheckExact(op1)) { + Py_complex c1 = ((PyComplexObject *)op1)->cval; + Py_complex c2 = ((PyComplexObject *)op2)->cval; + return memcmp(&c1, &c2, sizeof(Py_complex)) == 0; + } + // gh-130851: Treat instances of unexpected types as distinct if they are + // not the same object. return 0; } -#define MAX_CODE_UNITS_PER_LOC_ENTRY 8 - -PyCodeObject * -_Py_MakeShimCode(const _PyShimCodeDef *codedef) +static Py_uhash_t +hash_const(const void *key) { - PyObject *name = NULL; - PyObject *co_code = NULL; - PyObject *lines = NULL; - PyCodeObject *codeobj = NULL; - uint8_t *loc_table = NULL; - - name = _PyUnicode_FromASCII(codedef->cname, strlen(codedef->cname)); - if (name == NULL) { - goto cleanup; + PyObject *op = (PyObject *)key; + if (PySlice_Check(op)) { + PySliceObject *s = (PySliceObject *)op; + PyObject *data[3] = { s->start, s->stop, s->step }; + return _Py_HashBytes(&data, sizeof(data)); } - co_code = PyBytes_FromStringAndSize( - (const char *)codedef->code, codedef->codelen); - if (co_code == NULL) { - goto cleanup; + else if (PyTuple_CheckExact(op)) { + Py_ssize_t size = PyTuple_GET_SIZE(op); + PyObject **data = _PyTuple_ITEMS(op); + return _Py_HashBytes(data, sizeof(PyObject *) * size); } - int code_units = codedef->codelen / sizeof(_Py_CODEUNIT); - int loc_entries = (code_units + MAX_CODE_UNITS_PER_LOC_ENTRY - 1) / - MAX_CODE_UNITS_PER_LOC_ENTRY; - loc_table = PyMem_Malloc(loc_entries); - if (loc_table == NULL) { - PyErr_NoMemory(); - goto cleanup; + Py_hash_t h = PyObject_Hash(op); + if (h == -1) { + // gh-130851: Other than slice objects, every constant that the + // bytecode compiler generates is hashable. However, users can + // provide their own constants, when constructing code objects via + // types.CodeType(). If the user-provided constant is unhashable, we + // use the memory address of the object as a fallback hash value. + PyErr_Clear(); + return (Py_uhash_t)(uintptr_t)key; } - for (int i = 0; i < loc_entries-1; i++) { - loc_table[i] = 0x80 | (PY_CODE_LOCATION_INFO_NONE << 3) | 7; - code_units -= MAX_CODE_UNITS_PER_LOC_ENTRY; + return (Py_uhash_t)h; +} + +static int +clear_containers(_Py_hashtable_t *ht, const void *key, const void *value, + void *user_data) +{ + // First clear containers to avoid recursive deallocation later on in + // destroy_key. + PyObject *op = (PyObject *)key; + if (PyTuple_CheckExact(op)) { + for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(op); i++) { + Py_CLEAR(_PyTuple_ITEMS(op)[i]); + } } - assert(loc_entries > 0); - assert(code_units > 0 && code_units <= MAX_CODE_UNITS_PER_LOC_ENTRY); - loc_table[loc_entries-1] = 0x80 | - (PY_CODE_LOCATION_INFO_NONE << 3) | (code_units-1); - lines = PyBytes_FromStringAndSize((const char *)loc_table, loc_entries); - PyMem_Free(loc_table); - if (lines == NULL) { - goto cleanup; + else if (PySlice_Check(op)) { + PySliceObject *slice = (PySliceObject *)op; + Py_SETREF(slice->start, Py_None); + Py_SETREF(slice->stop, Py_None); + Py_SETREF(slice->step, Py_None); } - _Py_DECLARE_STR(shim_name, "<shim>"); - struct _PyCodeConstructor con = { - .filename = &_Py_STR(shim_name), - .name = name, - .qualname = name, - .flags = CO_NEWLOCALS | CO_OPTIMIZED, - - .code = co_code, - .firstlineno = 1, - .linetable = lines, - - .consts = (PyObject *)&_Py_SINGLETON(tuple_empty), - .names = (PyObject *)&_Py_SINGLETON(tuple_empty), - - .localsplusnames = (PyObject *)&_Py_SINGLETON(tuple_empty), - .localspluskinds = (PyObject *)&_Py_SINGLETON(bytes_empty), - - .argcount = 0, - .posonlyargcount = 0, - .kwonlyargcount = 0, + else if (PyFrozenSet_CheckExact(op)) { + _PySet_ClearInternal((PySetObject *)op); + } + return 0; +} - .stacksize = codedef->stacksize, +static void +destroy_key(void *key) +{ + _Py_ClearImmortal(key); +} +#endif - .exceptiontable = (PyObject *)&_Py_SINGLETON(bytes_empty), - }; +PyStatus +_PyCode_Init(PyInterpreterState *interp) +{ +#ifdef Py_GIL_DISABLED + struct _py_code_state *state = &interp->code_state; + state->constants = _Py_hashtable_new_full(&hash_const, &compare_constants, + &destroy_key, NULL, NULL); + if (state->constants == NULL) { + return _PyStatus_NO_MEMORY(); + } +#endif + return _PyStatus_OK(); +} - codeobj = _PyCode_New(&con); -cleanup: - Py_XDECREF(name); - Py_XDECREF(co_code); - Py_XDECREF(lines); - return codeobj; +void +_PyCode_Fini(PyInterpreterState *interp) +{ +#ifdef Py_GIL_DISABLED + // Free interned constants + struct _py_code_state *state = &interp->code_state; + if (state->constants) { + _Py_hashtable_foreach(state->constants, &clear_containers, NULL); + _Py_hashtable_destroy(state->constants); + state->constants = NULL; + } +#endif } |
