Add the co_extra field and accompanying APIs to code objects.

This completes PEP 523.
This commit is contained in:
Brett Cannon 2016-09-07 11:16:41 -07:00
parent a9296e7f3b
commit 5c4de2863b
8 changed files with 139 additions and 3 deletions

View File

@ -127,7 +127,8 @@ evaluation, etc.
This API is not part of the limited C API and is marked as private to This API is not part of the limited C API and is marked as private to
signal that usage of this API is expected to be limited and only signal that usage of this API is expected to be limited and only
applicable to very select, low-level use-cases. applicable to very select, low-level use-cases. Semantics of the
API will change with Python as necessary.
.. seealso:: .. seealso::

View File

@ -187,6 +187,10 @@ PyAPI_FUNC(void) _PyEval_SetSwitchInterval(unsigned long microseconds);
PyAPI_FUNC(unsigned long) _PyEval_GetSwitchInterval(void); PyAPI_FUNC(unsigned long) _PyEval_GetSwitchInterval(void);
#endif #endif
#ifndef Py_LIMITED_API
PyAPI_FUNC(Py_ssize_t) _PyEval_RequestCodeExtraIndex(freefunc);
#endif
#define Py_BEGIN_ALLOW_THREADS { \ #define Py_BEGIN_ALLOW_THREADS { \
PyThreadState *_save; \ PyThreadState *_save; \
_save = PyEval_SaveThread(); _save = PyEval_SaveThread();

View File

@ -7,6 +7,14 @@
extern "C" { extern "C" {
#endif #endif
/* Holder for co_extra information */
typedef struct {
Py_ssize_t ce_size;
void **ce_extras;
} _PyCodeObjectExtra;
/* Bytecode object */ /* Bytecode object */
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
@ -15,6 +23,7 @@ typedef struct {
int co_nlocals; /* #local variables */ int co_nlocals; /* #local variables */
int co_stacksize; /* #entries needed for evaluation stack */ int co_stacksize; /* #entries needed for evaluation stack */
int co_flags; /* CO_..., see below */ int co_flags; /* CO_..., see below */
int co_firstlineno; /* first source line number */
PyObject *co_code; /* instruction opcodes */ PyObject *co_code; /* instruction opcodes */
PyObject *co_consts; /* list (constants used) */ PyObject *co_consts; /* list (constants used) */
PyObject *co_names; /* list of strings (names used) */ PyObject *co_names; /* list of strings (names used) */
@ -30,11 +39,12 @@ typedef struct {
unsigned char *co_cell2arg; /* Maps cell vars which are arguments. */ unsigned char *co_cell2arg; /* Maps cell vars which are arguments. */
PyObject *co_filename; /* unicode (where it was loaded from) */ PyObject *co_filename; /* unicode (where it was loaded from) */
PyObject *co_name; /* unicode (name, for reference) */ PyObject *co_name; /* unicode (name, for reference) */
int co_firstlineno; /* first source line number */
PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) See PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) See
Objects/lnotab_notes.txt for details. */ Objects/lnotab_notes.txt for details. */
void *co_zombieframe; /* for optimization only (see frameobject.c) */ void *co_zombieframe; /* for optimization only (see frameobject.c) */
PyObject *co_weakreflist; /* to support weakrefs to code objects */ PyObject *co_weakreflist; /* to support weakrefs to code objects */
/* Scratch space for extra data relating to the code object */
_PyCodeObjectExtra *co_extra;
} PyCodeObject; } PyCodeObject;
/* Masks for co_flags above */ /* Masks for co_flags above */
@ -128,6 +138,14 @@ PyAPI_FUNC(PyObject*) _PyCode_ConstantKey(PyObject *obj);
PyAPI_FUNC(PyObject*) PyCode_Optimize(PyObject *code, PyObject* consts, PyAPI_FUNC(PyObject*) PyCode_Optimize(PyObject *code, PyObject* consts,
PyObject *names, PyObject *lnotab); PyObject *names, PyObject *lnotab);
#ifndef Py_LIMITED_API
PyAPI_FUNC(int) _PyCode_GetExtra(PyObject *code, Py_ssize_t index,
void **extra);
PyAPI_FUNC(int) _PyCode_SetExtra(PyObject *code, Py_ssize_t index,
void *extra);
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -8,6 +8,10 @@
extern "C" { extern "C" {
#endif #endif
/* This limitation is for performance and simplicity. If needed it can be
removed (with effort). */
#define MAX_CO_EXTRA_USERS 255
/* State shared between threads */ /* State shared between threads */
struct _ts; /* Forward */ struct _ts; /* Forward */
@ -141,6 +145,9 @@ typedef struct _ts {
PyObject *coroutine_wrapper; PyObject *coroutine_wrapper;
int in_coroutine_wrapper; int in_coroutine_wrapper;
Py_ssize_t co_extra_user_count;
freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS];
/* XXX signal handlers should also be here */ /* XXX signal handlers should also be here */
} PyThreadState; } PyThreadState;

View File

@ -27,7 +27,7 @@ Core and Builtins
the braces (where the expressions are). This is a breaking change the braces (where the expressions are). This is a breaking change
from the 3.6 alpha releases. from the 3.6 alpha releases.
- Implement the frame evaluation part of PEP 523. - Implement PEP 523.
- Issue #27870: A left shift of zero by a large integer no longer attempts - Issue #27870: A left shift of zero by a large integer no longer attempts
to allocate large amounts of memory. to allocate large amounts of memory.

View File

@ -152,6 +152,7 @@ PyCode_New(int argcount, int kwonlyargcount,
co->co_lnotab = lnotab; co->co_lnotab = lnotab;
co->co_zombieframe = NULL; co->co_zombieframe = NULL;
co->co_weakreflist = NULL; co->co_weakreflist = NULL;
co->co_extra = NULL;
return co; return co;
} }
@ -361,6 +362,20 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kw)
static void static void
code_dealloc(PyCodeObject *co) code_dealloc(PyCodeObject *co)
{ {
if (co->co_extra != NULL) {
PyThreadState *tstate = PyThreadState_Get();
for (Py_ssize_t i = 0; i < co->co_extra->ce_size; i++) {
freefunc free_extra = tstate->co_extra_freefuncs[i];
if (free_extra != NULL) {
free_extra(co->co_extra->ce_extras[i]);
}
}
PyMem_FREE(co->co_extra);
}
Py_XDECREF(co->co_code); Py_XDECREF(co->co_code);
Py_XDECREF(co->co_consts); Py_XDECREF(co->co_consts);
Py_XDECREF(co->co_names); Py_XDECREF(co->co_names);
@ -752,3 +767,79 @@ _PyCode_CheckLineNumber(PyCodeObject* co, int lasti, PyAddrPair *bounds)
return line; return line;
} }
int
_PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra)
{
PyCodeObject *o;
assert(*extra == NULL);
if (!PyCode_Check(code)) {
PyErr_BadInternalCall();
return 1;
}
o = (PyCodeObject*) code;
if (o->co_extra == NULL || o->co_extra->ce_size <= index) {
return 0;
}
*extra = o->co_extra->ce_extras[index];
return 0;
}
int
_PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra)
{
PyCodeObject *o;
PyThreadState *tstate = PyThreadState_Get();
if (!PyCode_Check(code) || index < 0 ||
index >= tstate->co_extra_user_count) {
PyErr_BadInternalCall();
return 1;
}
o = (PyCodeObject*) code;
if (o->co_extra == NULL) {
o->co_extra = (_PyCodeObjectExtra*) PyMem_Malloc(
sizeof(_PyCodeObjectExtra));
if (o->co_extra == NULL) {
return 1;
}
o->co_extra->ce_extras = PyMem_Malloc(
tstate->co_extra_user_count * sizeof(void*));
if (o->co_extra->ce_extras == NULL) {
return 1;
}
o->co_extra->ce_size = tstate->co_extra_user_count;
for (Py_ssize_t i = 0; i < o->co_extra->ce_size; i++) {
o->co_extra->ce_extras[i] = NULL;
}
}
else if (o->co_extra->ce_size <= index) {
o->co_extra->ce_extras = PyMem_Realloc(
o->co_extra->ce_extras, tstate->co_extra_user_count * sizeof(void*));
if (o->co_extra->ce_extras == NULL) {
return 1;
}
o->co_extra->ce_size = tstate->co_extra_user_count;
for (Py_ssize_t i = o->co_extra->ce_size; i < o->co_extra->ce_size; i++) {
o->co_extra->ce_extras[i] = NULL;
}
}
o->co_extra->ce_extras[index] = extra;
return 0;
}

View File

@ -5608,3 +5608,17 @@ _Py_GetDXProfile(PyObject *self, PyObject *args)
} }
#endif #endif
Py_ssize_t
_PyEval_RequestCodeExtraIndex(freefunc free)
{
PyThreadState *tstate = PyThreadState_Get();
Py_ssize_t new_index;
if (tstate->co_extra_user_count == MAX_CO_EXTRA_USERS - 1) {
return -1;
}
new_index = tstate->co_extra_user_count++;
tstate->co_extra_freefuncs[new_index] = free;
return new_index;
}

View File

@ -227,6 +227,7 @@ new_threadstate(PyInterpreterState *interp, int init)
tstate->coroutine_wrapper = NULL; tstate->coroutine_wrapper = NULL;
tstate->in_coroutine_wrapper = 0; tstate->in_coroutine_wrapper = 0;
tstate->co_extra_user_count = 0;
if (init) if (init)
_PyThreadState_Init(tstate); _PyThreadState_Init(tstate);