Skip to content

Commit 355f5dd

Browse files
authored
bpo-43693: Turn localspluskinds into an object (GH-26749)
Managing it as a bare pointer to malloc'ed bytes is just too awkward in a few places.
1 parent c5d700f commit 355f5dd

14 files changed

+5454
-5394
lines changed

Include/cpython/code.h

+1-5
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ typedef uint16_t _Py_CODEUNIT;
2626
typedef struct _PyOpcache _PyOpcache;
2727

2828

29-
typedef unsigned char _PyLocalsPlusKind;
30-
typedef _PyLocalsPlusKind *_PyLocalsPlusKinds;
31-
3229
/* Bytecode object */
3330
struct PyCodeObject {
3431
PyObject_HEAD
@@ -75,7 +72,7 @@ struct PyCodeObject {
7572
int co_firstlineno; /* first source line number */
7673
PyObject *co_code; /* instruction opcodes */
7774
PyObject *co_localsplusnames; /* tuple mapping offsets to names */
78-
_PyLocalsPlusKinds co_localspluskinds; /* array mapping to local kinds */
75+
PyObject *co_localspluskinds; /* Bytes mapping to local kinds (one byte per variable) */
7976
PyObject *co_filename; /* unicode (where it was loaded from) */
8077
PyObject *co_name; /* unicode (name, for reference) */
8178
PyObject *co_linetable; /* string (encoding addr<->lineno mapping) See
@@ -222,4 +219,3 @@ void PyLineTable_InitAddressRange(const char *linetable, Py_ssize_t length, int
222219
int PyLineTable_NextAddressRange(PyCodeAddressRange *range);
223220
int PyLineTable_PreviousAddressRange(PyCodeAddressRange *range);
224221

225-

Include/internal/pycore_code.h

+17-22
Original file line numberDiff line numberDiff line change
@@ -180,39 +180,34 @@ extern Py_ssize_t _Py_QuickenedCount;
180180
* "free" kind is mutually exclusive with both.
181181
*/
182182

183-
// For now _PyLocalsPlusKind and _PyLocalsPlusKinds are defined
184-
// in Include/cpython/code.h.
185-
/* Note that these all fit within _PyLocalsPlusKind, as do combinations. */
183+
// Note that these all fit within a byte, as do combinations.
186184
// Later, we will use the smaller numbers to differentiate the different
187185
// kinds of locals (e.g. pos-only arg, varkwargs, local-only).
188186
#define CO_FAST_LOCAL 0x20
189187
#define CO_FAST_CELL 0x40
190188
#define CO_FAST_FREE 0x80
191189

192-
static inline int
193-
_PyCode_InitLocalsPlusKinds(int num, _PyLocalsPlusKinds *pkinds)
190+
typedef unsigned char _PyLocals_Kind;
191+
192+
static inline _PyLocals_Kind
193+
_PyLocals_GetKind(PyObject *kinds, int i)
194194
{
195-
if (num == 0) {
196-
*pkinds = NULL;
197-
return 0;
198-
}
199-
_PyLocalsPlusKinds kinds = PyMem_NEW(_PyLocalsPlusKind, num);
200-
if (kinds == NULL) {
201-
PyErr_NoMemory();
202-
return -1;
203-
}
204-
*pkinds = kinds;
205-
return 0;
195+
assert(PyBytes_Check(kinds));
196+
assert(0 <= i && i < PyBytes_GET_SIZE(kinds));
197+
char *ptr = PyBytes_AS_STRING(kinds);
198+
return (_PyLocals_Kind)(ptr[i]);
206199
}
207200

208201
static inline void
209-
_PyCode_ClearLocalsPlusKinds(_PyLocalsPlusKinds kinds)
202+
_PyLocals_SetKind(PyObject *kinds, int i, _PyLocals_Kind kind)
210203
{
211-
if (kinds != NULL) {
212-
PyMem_Free(kinds);
213-
}
204+
assert(PyBytes_Check(kinds));
205+
assert(0 <= i && i < PyBytes_GET_SIZE(kinds));
206+
char *ptr = PyBytes_AS_STRING(kinds);
207+
ptr[i] = (char) kind;
214208
}
215209

210+
216211
struct _PyCodeConstructor {
217212
/* metadata */
218213
PyObject *filename;
@@ -229,8 +224,8 @@ struct _PyCodeConstructor {
229224
PyObject *names;
230225

231226
/* mapping frame offsets to information */
232-
PyObject *localsplusnames;
233-
_PyLocalsPlusKinds localspluskinds;
227+
PyObject *localsplusnames; // Tuple of strings
228+
PyObject *localspluskinds; // Bytes object, one byte per variable
234229

235230
/* args (within varnames) */
236231
int argcount;

Lib/ctypes/test/test_values.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ class struct_frozen(Structure):
8080
continue
8181
items.append((entry.name.decode("ascii"), entry.size))
8282

83-
expected = [("__hello__", 128),
84-
("__phello__", -128),
85-
("__phello__.spam", 128),
83+
expected = [("__hello__", 133),
84+
("__phello__", -133),
85+
("__phello__.spam", 133),
8686
]
8787
self.assertEqual(items, expected, "PyImport_FrozenModules example "
8888
"in Doc/library/ctypes.rst may be out of date")

Lib/importlib/_bootstrap_external.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ def _write_atomic(path, data, mode=0o666):
359359
# Python 3.11a1 3454 (compute cell offsets relative to locals bpo-43693)
360360
# Python 3.11a1 3455 (add MAKE_CELL bpo-43693)
361361
# Python 3.11a1 3456 (interleave cell args bpo-43693)
362+
# Python 3.11a1 3457 (Change localsplus to a bytes object bpo-43693)
362363

363364
#
364365
# MAGIC must change whenever the bytecode emitted by the compiler may no
@@ -368,7 +369,7 @@ def _write_atomic(path, data, mode=0o666):
368369
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
369370
# in PC/launcher.c must also be updated.
370371

371-
MAGIC_NUMBER = (3456).to_bytes(2, 'little') + b'\r\n'
372+
MAGIC_NUMBER = (3457).to_bytes(2, 'little') + b'\r\n'
372373
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
373374

374375
_PYCACHE = '__pycache__'

Objects/codeobject.c

+24-19
Original file line numberDiff line numberDiff line change
@@ -156,16 +156,16 @@ validate_and_copy_tuple(PyObject *tup)
156156

157157
// This is also used in compile.c.
158158
void
159-
_Py_set_localsplus_info(int offset, PyObject *name, _PyLocalsPlusKind kind,
160-
PyObject *names, _PyLocalsPlusKinds kinds)
159+
_Py_set_localsplus_info(int offset, PyObject *name, _PyLocals_Kind kind,
160+
PyObject *names, PyObject *kinds)
161161
{
162162
Py_INCREF(name);
163163
PyTuple_SET_ITEM(names, offset, name);
164-
kinds[offset] = kind;
164+
_PyLocals_SetKind(kinds, offset, kind);
165165
}
166166

167167
static void
168-
get_localsplus_counts(PyObject *names, _PyLocalsPlusKinds kinds,
168+
get_localsplus_counts(PyObject *names, PyObject *kinds,
169169
int *pnlocals, int *pnplaincellvars, int *pncellvars,
170170
int *pnfreevars)
171171
{
@@ -175,17 +175,18 @@ get_localsplus_counts(PyObject *names, _PyLocalsPlusKinds kinds,
175175
int nfreevars = 0;
176176
Py_ssize_t nlocalsplus = PyTuple_GET_SIZE(names);
177177
for (int i = 0; i < nlocalsplus; i++) {
178-
if (kinds[i] & CO_FAST_LOCAL) {
178+
_PyLocals_Kind kind = _PyLocals_GetKind(kinds, i);
179+
if (kind & CO_FAST_LOCAL) {
179180
nlocals += 1;
180-
if (kinds[i] & CO_FAST_CELL) {
181+
if (kind & CO_FAST_CELL) {
181182
ncellvars += 1;
182183
}
183184
}
184-
else if (kinds[i] & CO_FAST_CELL) {
185+
else if (kind & CO_FAST_CELL) {
185186
ncellvars += 1;
186187
nplaincellvars += 1;
187188
}
188-
else if (kinds[i] & CO_FAST_FREE) {
189+
else if (kind & CO_FAST_FREE) {
189190
nfreevars += 1;
190191
}
191192
}
@@ -204,15 +205,16 @@ get_localsplus_counts(PyObject *names, _PyLocalsPlusKinds kinds,
204205
}
205206

206207
static PyObject *
207-
get_localsplus_names(PyCodeObject *co, _PyLocalsPlusKind kind, int num)
208+
get_localsplus_names(PyCodeObject *co, _PyLocals_Kind kind, int num)
208209
{
209210
PyObject *names = PyTuple_New(num);
210211
if (names == NULL) {
211212
return NULL;
212213
}
213214
int index = 0;
214215
for (int offset = 0; offset < co->co_nlocalsplus; offset++) {
215-
if ((co->co_localspluskinds[offset] & kind) == 0) {
216+
_PyLocals_Kind k = _PyLocals_GetKind(co->co_localspluskinds, offset);
217+
if ((k & kind) == 0) {
216218
continue;
217219
}
218220
assert(index < num);
@@ -236,8 +238,9 @@ _PyCode_Validate(struct _PyCodeConstructor *con)
236238
con->consts == NULL || !PyTuple_Check(con->consts) ||
237239
con->names == NULL || !PyTuple_Check(con->names) ||
238240
con->localsplusnames == NULL || !PyTuple_Check(con->localsplusnames) ||
239-
(PyTuple_GET_SIZE(con->localsplusnames) && con->localspluskinds == NULL) ||
240-
(!PyTuple_GET_SIZE(con->localsplusnames) && con->localspluskinds != NULL) ||
241+
con->localspluskinds == NULL || !PyBytes_Check(con->localspluskinds) ||
242+
PyTuple_GET_SIZE(con->localsplusnames)
243+
!= PyBytes_GET_SIZE(con->localspluskinds) ||
241244
con->name == NULL || !PyUnicode_Check(con->name) ||
242245
con->filename == NULL || !PyUnicode_Check(con->filename) ||
243246
con->linetable == NULL || !PyBytes_Check(con->linetable) ||
@@ -309,7 +312,7 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
309312

310313
Py_INCREF(con->localsplusnames);
311314
co->co_localsplusnames = con->localsplusnames;
312-
// We take ownership of the kinds array.
315+
Py_INCREF(con->localspluskinds);
313316
co->co_localspluskinds = con->localspluskinds;
314317

315318
co->co_argcount = con->argcount;
@@ -394,7 +397,7 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount,
394397
{
395398
PyCodeObject *co = NULL;
396399
PyObject *localsplusnames = NULL;
397-
_PyLocalsPlusKinds localspluskinds = NULL;
400+
PyObject *localspluskinds = NULL;
398401

399402
if (varnames == NULL || !PyTuple_Check(varnames) ||
400403
cellvars == NULL || !PyTuple_Check(cellvars) ||
@@ -413,7 +416,8 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount,
413416
if (localsplusnames == NULL) {
414417
goto error;
415418
}
416-
if (_PyCode_InitLocalsPlusKinds(nlocalsplus, &localspluskinds) < 0) {
419+
localspluskinds = PyBytes_FromStringAndSize(NULL, nlocalsplus);
420+
if (localspluskinds == NULL) {
417421
goto error;
418422
}
419423
int offset = 0;
@@ -438,7 +442,8 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount,
438442
// Merge the localsplus indices.
439443
nlocalsplus -= 1;
440444
offset -= 1;
441-
localspluskinds[argoffset] |= CO_FAST_CELL;
445+
_PyLocals_Kind kind = _PyLocals_GetKind(localspluskinds, argoffset);
446+
_PyLocals_SetKind(localspluskinds, argoffset, kind | CO_FAST_CELL);
442447
continue;
443448
}
444449
_Py_set_localsplus_info(offset, name, CO_FAST_CELL,
@@ -495,7 +500,6 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount,
495500
goto error;
496501
}
497502

498-
localspluskinds = NULL; // This keeps it from getting freed below.
499503
Py_INCREF(varnames);
500504
co->co_varnames = varnames;
501505
Py_INCREF(cellvars);
@@ -505,7 +509,7 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount,
505509

506510
error:
507511
Py_XDECREF(localsplusnames);
508-
_PyCode_ClearLocalsPlusKinds(localspluskinds);
512+
Py_XDECREF(localspluskinds);
509513
return co;
510514
}
511515

@@ -558,6 +562,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
558562
.consts = nulltuple,
559563
.names = nulltuple,
560564
.localsplusnames = nulltuple,
565+
.localspluskinds = emptystring,
561566
.exceptiontable = emptystring,
562567
};
563568
result = _PyCode_New(&con);
@@ -1142,7 +1147,7 @@ code_dealloc(PyCodeObject *co)
11421147
Py_XDECREF(co->co_consts);
11431148
Py_XDECREF(co->co_names);
11441149
Py_XDECREF(co->co_localsplusnames);
1145-
_PyCode_ClearLocalsPlusKinds(co->co_localspluskinds);
1150+
Py_XDECREF(co->co_localspluskinds);
11461151
Py_XDECREF(co->co_varnames);
11471152
Py_XDECREF(co->co_freevars);
11481153
Py_XDECREF(co->co_cellvars);

Objects/frameobject.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -958,7 +958,7 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f)
958958
co = _PyFrame_GetCode(f);
959959
fast = f->f_localsptr;
960960
for (int i = 0; i < co->co_nlocalsplus; i++) {
961-
_PyLocalsPlusKind kind = co->co_localspluskinds[i];
961+
_PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);
962962

963963
/* If the namespace is unoptimized, then one of the
964964
following cases applies:
@@ -1052,7 +1052,7 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
10521052

10531053
PyErr_Fetch(&error_type, &error_value, &error_traceback);
10541054
for (int i = 0; i < co->co_nlocalsplus; i++) {
1055-
_PyLocalsPlusKind kind = co->co_localspluskinds[i];
1055+
_PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);
10561056

10571057
/* Same test as in PyFrame_FastToLocals() above. */
10581058
if (kind & CO_FAST_FREE && !(co->co_flags & CO_OPTIMIZED)) {

Objects/typeobject.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -8864,7 +8864,7 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co,
88648864

88658865
PyObject *firstarg = f->f_localsptr[0];
88668866
// The first argument might be a cell.
8867-
if (firstarg != NULL && (co->co_localspluskinds[0] & CO_FAST_CELL)) {
8867+
if (firstarg != NULL && (_PyLocals_GetKind(co->co_localspluskinds, 0) & CO_FAST_CELL)) {
88688868
// "firstarg" is a cell here unless (very unlikely) super()
88698869
// was called from the C-API before the first MAKE_CELL op.
88708870
if (f->f_lasti >= 0) {
@@ -8883,7 +8883,7 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co,
88838883
PyTypeObject *type = NULL;
88848884
int i = co->co_nlocals + co->co_nplaincellvars;
88858885
for (; i < co->co_nlocalsplus; i++) {
8886-
assert((co->co_localspluskinds[i] & CO_FAST_FREE) != 0);
8886+
assert((_PyLocals_GetKind(co->co_localspluskinds, i) & CO_FAST_FREE) != 0);
88878887
PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
88888888
assert(PyUnicode_Check(name));
88898889
if (_PyUnicode_EqualToASCIIId(name, &PyId___class__)) {

Programs/test_frozenmain.h

+5-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/compile.c

+8-12
Original file line numberDiff line numberDiff line change
@@ -7187,23 +7187,21 @@ merge_const_one(struct compiler *c, PyObject **obj)
71877187
}
71887188

71897189
// This is in codeobject.c.
7190-
extern void _Py_set_localsplus_info(int, PyObject *, _PyLocalsPlusKind,
7191-
PyObject *, _PyLocalsPlusKinds);
7190+
extern void _Py_set_localsplus_info(int, PyObject *, unsigned char,
7191+
PyObject *, PyObject *);
71927192

71937193
static void
71947194
compute_localsplus_info(struct compiler *c, int nlocalsplus,
7195-
PyObject *names, _PyLocalsPlusKinds kinds)
7195+
PyObject *names, PyObject *kinds)
71967196
{
7197-
assert(PyTuple_GET_SIZE(names) == nlocalsplus);
7198-
71997197
PyObject *k, *v;
72007198
Py_ssize_t pos = 0;
72017199
while (PyDict_Next(c->u->u_varnames, &pos, &k, &v)) {
72027200
int offset = (int)PyLong_AS_LONG(v);
72037201
assert(offset >= 0);
72047202
assert(offset < nlocalsplus);
72057203
// For now we do not distinguish arg kinds.
7206-
_PyLocalsPlusKind kind = CO_FAST_LOCAL;
7204+
_PyLocals_Kind kind = CO_FAST_LOCAL;
72077205
if (PyDict_GetItem(c->u->u_cellvars, k) != NULL) {
72087206
kind |= CO_FAST_CELL;
72097207
}
@@ -7245,7 +7243,7 @@ makecode(struct compiler *c, struct assembler *a, PyObject *constslist,
72457243
PyObject *names = NULL;
72467244
PyObject *consts = NULL;
72477245
PyObject *localsplusnames = NULL;
7248-
_PyLocalsPlusKinds localspluskinds = NULL;
7246+
PyObject *localspluskinds = NULL;
72497247
PyObject *name = NULL;
72507248

72517249
names = dict_keys_inorder(c->u->u_names, 0);
@@ -7281,7 +7279,8 @@ makecode(struct compiler *c, struct assembler *a, PyObject *constslist,
72817279
if (localsplusnames == NULL) {
72827280
goto error;
72837281
}
7284-
if (_PyCode_InitLocalsPlusKinds(nlocalsplus, &localspluskinds) < 0) {
7282+
localspluskinds = PyBytes_FromStringAndSize(NULL, nlocalsplus);
7283+
if (localspluskinds == NULL) {
72857284
goto error;
72867285
}
72877286
compute_localsplus_info(c, nlocalsplus, localsplusnames, localspluskinds);
@@ -7315,7 +7314,6 @@ makecode(struct compiler *c, struct assembler *a, PyObject *constslist,
73157314
}
73167315

73177316
if (!merge_const_one(c, &localsplusnames)) {
7318-
_PyCode_ClearLocalsPlusKinds(con.localspluskinds);
73197317
goto error;
73207318
}
73217319
con.localsplusnames = localsplusnames;
@@ -7325,13 +7323,11 @@ makecode(struct compiler *c, struct assembler *a, PyObject *constslist,
73257323
goto error;
73267324
}
73277325

7328-
localspluskinds = NULL; // This keeps it from getting freed below.
7329-
73307326
error:
73317327
Py_XDECREF(names);
73327328
Py_XDECREF(consts);
73337329
Py_XDECREF(localsplusnames);
7334-
_PyCode_ClearLocalsPlusKinds(localspluskinds);
7330+
Py_XDECREF(localspluskinds);
73357331
Py_XDECREF(name);
73367332
return co;
73377333
}

Python/frozen_hello.h

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ const unsigned char _Py_M__hello[] = {
55
100,1,131,1,1,0,100,2,83,0,41,3,84,122,12,72,
66
101,108,108,111,32,119,111,114,108,100,33,78,41,2,90,11,
77
105,110,105,116,105,97,108,105,122,101,100,218,5,112,114,105,
8-
110,116,169,0,122,14,60,102,114,111,122,101,110,32,104,101,
9-
108,108,111,62,218,8,60,109,111,100,117,108,101,62,1,0,
10-
0,0,115,4,0,0,0,4,0,12,1,243,0,0,0,0,
8+
110,116,169,0,243,0,0,0,0,122,14,60,102,114,111,122,
9+
101,110,32,104,101,108,108,111,62,218,8,60,109,111,100,117,
10+
108,101,62,1,0,0,0,115,4,0,0,0,4,0,12,1,
11+
114,2,0,0,0,
1112
};

0 commit comments

Comments
 (0)