Skip to content

Commit 5c4de28

Browse files
committed
Add the co_extra field and accompanying APIs to code objects.
This completes PEP 523.
1 parent a9296e7 commit 5c4de28

File tree

8 files changed

+139
-3
lines changed

8 files changed

+139
-3
lines changed

Doc/whatsnew/3.6.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ evaluation, etc.
127127

128128
This API is not part of the limited C API and is marked as private to
129129
signal that usage of this API is expected to be limited and only
130-
applicable to very select, low-level use-cases.
130+
applicable to very select, low-level use-cases. Semantics of the
131+
API will change with Python as necessary.
131132

132133
.. seealso::
133134

Include/ceval.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ PyAPI_FUNC(void) _PyEval_SetSwitchInterval(unsigned long microseconds);
187187
PyAPI_FUNC(unsigned long) _PyEval_GetSwitchInterval(void);
188188
#endif
189189

190+
#ifndef Py_LIMITED_API
191+
PyAPI_FUNC(Py_ssize_t) _PyEval_RequestCodeExtraIndex(freefunc);
192+
#endif
193+
190194
#define Py_BEGIN_ALLOW_THREADS { \
191195
PyThreadState *_save; \
192196
_save = PyEval_SaveThread();

Include/code.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@
77
extern "C" {
88
#endif
99

10+
11+
/* Holder for co_extra information */
12+
typedef struct {
13+
Py_ssize_t ce_size;
14+
void **ce_extras;
15+
} _PyCodeObjectExtra;
16+
17+
1018
/* Bytecode object */
1119
typedef struct {
1220
PyObject_HEAD
@@ -15,6 +23,7 @@ typedef struct {
1523
int co_nlocals; /* #local variables */
1624
int co_stacksize; /* #entries needed for evaluation stack */
1725
int co_flags; /* CO_..., see below */
26+
int co_firstlineno; /* first source line number */
1827
PyObject *co_code; /* instruction opcodes */
1928
PyObject *co_consts; /* list (constants used) */
2029
PyObject *co_names; /* list of strings (names used) */
@@ -30,11 +39,12 @@ typedef struct {
3039
unsigned char *co_cell2arg; /* Maps cell vars which are arguments. */
3140
PyObject *co_filename; /* unicode (where it was loaded from) */
3241
PyObject *co_name; /* unicode (name, for reference) */
33-
int co_firstlineno; /* first source line number */
3442
PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) See
3543
Objects/lnotab_notes.txt for details. */
3644
void *co_zombieframe; /* for optimization only (see frameobject.c) */
3745
PyObject *co_weakreflist; /* to support weakrefs to code objects */
46+
/* Scratch space for extra data relating to the code object */
47+
_PyCodeObjectExtra *co_extra;
3848
} PyCodeObject;
3949

4050
/* Masks for co_flags above */
@@ -128,6 +138,14 @@ PyAPI_FUNC(PyObject*) _PyCode_ConstantKey(PyObject *obj);
128138
PyAPI_FUNC(PyObject*) PyCode_Optimize(PyObject *code, PyObject* consts,
129139
PyObject *names, PyObject *lnotab);
130140

141+
142+
#ifndef Py_LIMITED_API
143+
PyAPI_FUNC(int) _PyCode_GetExtra(PyObject *code, Py_ssize_t index,
144+
void **extra);
145+
PyAPI_FUNC(int) _PyCode_SetExtra(PyObject *code, Py_ssize_t index,
146+
void *extra);
147+
#endif
148+
131149
#ifdef __cplusplus
132150
}
133151
#endif

Include/pystate.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
extern "C" {
99
#endif
1010

11+
/* This limitation is for performance and simplicity. If needed it can be
12+
removed (with effort). */
13+
#define MAX_CO_EXTRA_USERS 255
14+
1115
/* State shared between threads */
1216

1317
struct _ts; /* Forward */
@@ -141,6 +145,9 @@ typedef struct _ts {
141145
PyObject *coroutine_wrapper;
142146
int in_coroutine_wrapper;
143147

148+
Py_ssize_t co_extra_user_count;
149+
freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS];
150+
144151
/* XXX signal handlers should also be here */
145152

146153
} PyThreadState;

Misc/NEWS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Core and Builtins
2727
the braces (where the expressions are). This is a breaking change
2828
from the 3.6 alpha releases.
2929

30-
- Implement the frame evaluation part of PEP 523.
30+
- Implement PEP 523.
3131

3232
- Issue #27870: A left shift of zero by a large integer no longer attempts
3333
to allocate large amounts of memory.

Objects/codeobject.c

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ PyCode_New(int argcount, int kwonlyargcount,
152152
co->co_lnotab = lnotab;
153153
co->co_zombieframe = NULL;
154154
co->co_weakreflist = NULL;
155+
co->co_extra = NULL;
155156
return co;
156157
}
157158

@@ -361,6 +362,20 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kw)
361362
static void
362363
code_dealloc(PyCodeObject *co)
363364
{
365+
if (co->co_extra != NULL) {
366+
PyThreadState *tstate = PyThreadState_Get();
367+
368+
for (Py_ssize_t i = 0; i < co->co_extra->ce_size; i++) {
369+
freefunc free_extra = tstate->co_extra_freefuncs[i];
370+
371+
if (free_extra != NULL) {
372+
free_extra(co->co_extra->ce_extras[i]);
373+
}
374+
}
375+
376+
PyMem_FREE(co->co_extra);
377+
}
378+
364379
Py_XDECREF(co->co_code);
365380
Py_XDECREF(co->co_consts);
366381
Py_XDECREF(co->co_names);
@@ -752,3 +767,79 @@ _PyCode_CheckLineNumber(PyCodeObject* co, int lasti, PyAddrPair *bounds)
752767

753768
return line;
754769
}
770+
771+
772+
int
773+
_PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra)
774+
{
775+
PyCodeObject *o;
776+
777+
assert(*extra == NULL);
778+
779+
if (!PyCode_Check(code)) {
780+
PyErr_BadInternalCall();
781+
return 1;
782+
}
783+
784+
o = (PyCodeObject*) code;
785+
786+
if (o->co_extra == NULL || o->co_extra->ce_size <= index) {
787+
return 0;
788+
}
789+
790+
*extra = o->co_extra->ce_extras[index];
791+
return 0;
792+
}
793+
794+
795+
int
796+
_PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra)
797+
{
798+
PyCodeObject *o;
799+
PyThreadState *tstate = PyThreadState_Get();
800+
801+
if (!PyCode_Check(code) || index < 0 ||
802+
index >= tstate->co_extra_user_count) {
803+
PyErr_BadInternalCall();
804+
return 1;
805+
}
806+
807+
o = (PyCodeObject*) code;
808+
809+
if (o->co_extra == NULL) {
810+
o->co_extra = (_PyCodeObjectExtra*) PyMem_Malloc(
811+
sizeof(_PyCodeObjectExtra));
812+
if (o->co_extra == NULL) {
813+
return 1;
814+
}
815+
816+
o->co_extra->ce_extras = PyMem_Malloc(
817+
tstate->co_extra_user_count * sizeof(void*));
818+
if (o->co_extra->ce_extras == NULL) {
819+
return 1;
820+
}
821+
822+
o->co_extra->ce_size = tstate->co_extra_user_count;
823+
824+
for (Py_ssize_t i = 0; i < o->co_extra->ce_size; i++) {
825+
o->co_extra->ce_extras[i] = NULL;
826+
}
827+
}
828+
else if (o->co_extra->ce_size <= index) {
829+
o->co_extra->ce_extras = PyMem_Realloc(
830+
o->co_extra->ce_extras, tstate->co_extra_user_count * sizeof(void*));
831+
832+
if (o->co_extra->ce_extras == NULL) {
833+
return 1;
834+
}
835+
836+
o->co_extra->ce_size = tstate->co_extra_user_count;
837+
838+
for (Py_ssize_t i = o->co_extra->ce_size; i < o->co_extra->ce_size; i++) {
839+
o->co_extra->ce_extras[i] = NULL;
840+
}
841+
}
842+
843+
o->co_extra->ce_extras[index] = extra;
844+
return 0;
845+
}

Python/ceval.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5608,3 +5608,17 @@ _Py_GetDXProfile(PyObject *self, PyObject *args)
56085608
}
56095609

56105610
#endif
5611+
5612+
Py_ssize_t
5613+
_PyEval_RequestCodeExtraIndex(freefunc free)
5614+
{
5615+
PyThreadState *tstate = PyThreadState_Get();
5616+
Py_ssize_t new_index;
5617+
5618+
if (tstate->co_extra_user_count == MAX_CO_EXTRA_USERS - 1) {
5619+
return -1;
5620+
}
5621+
new_index = tstate->co_extra_user_count++;
5622+
tstate->co_extra_freefuncs[new_index] = free;
5623+
return new_index;
5624+
}

Python/pystate.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ new_threadstate(PyInterpreterState *interp, int init)
227227

228228
tstate->coroutine_wrapper = NULL;
229229
tstate->in_coroutine_wrapper = 0;
230+
tstate->co_extra_user_count = 0;
230231

231232
if (init)
232233
_PyThreadState_Init(tstate);

0 commit comments

Comments
 (0)