Skip to content

Commit 9487a17

Browse files
authored
bpo-44207: Add an internal version number to function objects. (pythonGH-27078)
1 parent e14d5ae commit 9487a17

File tree

3 files changed

+36
-2
lines changed

3 files changed

+36
-2
lines changed

Include/funcobject.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ typedef struct {
4242
PyObject *func_module; /* The __module__ attribute, can be anything */
4343
PyObject *func_annotations; /* Annotations, a dict or NULL */
4444
vectorcallfunc vectorcall;
45+
/* Version number for use by specializer.
46+
* Can set to non-zero when we want to specialize.
47+
* Will be set to zero if any of these change:
48+
* defaults
49+
* kwdefaults (only if the object changes, not the contents of the dict)
50+
* code
51+
* annotations */
52+
uint32_t func_version;
4553

4654
/* Invariant:
4755
* func_closure contains the bindings for func_code->co_freevars, so
@@ -74,6 +82,8 @@ PyAPI_FUNC(PyObject *) _PyFunction_Vectorcall(
7482
PyObject *const *stack,
7583
size_t nargsf,
7684
PyObject *kwnames);
85+
86+
uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func);
7787
#endif
7888

7989
/* Macros for direct access to these values. Type checks are *not*

Lib/test/test_sys.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1278,7 +1278,7 @@ class C(object): pass
12781278
check(x, size('4P3i4cP'))
12791279
# function
12801280
def func(): pass
1281-
check(func, size('14P'))
1281+
check(func, size('14Pi'))
12821282
class c():
12831283
@staticmethod
12841284
def foo():

Objects/funcobject.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include "pycore_pyerrors.h" // _PyErr_Occurred()
88
#include "structmember.h" // PyMemberDef
99

10+
static uint32_t next_func_version = 1;
11+
1012
PyObject *
1113
PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname)
1214
{
@@ -79,7 +81,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
7981
op->func_module = module;
8082
op->func_annotations = NULL;
8183
op->vectorcall = _PyFunction_Vectorcall;
82-
84+
op->func_version = 0;
8385
_PyObject_GC_TRACK(op);
8486
return (PyObject *)op;
8587

@@ -94,6 +96,19 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
9496
return NULL;
9597
}
9698

99+
uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func)
100+
{
101+
if (func->func_version != 0) {
102+
return func->func_version;
103+
}
104+
if (next_func_version == 0) {
105+
return 0;
106+
}
107+
uint32_t v = next_func_version++;
108+
func->func_version = v;
109+
return v;
110+
}
111+
97112
PyObject *
98113
PyFunction_New(PyObject *code, PyObject *globals)
99114
{
@@ -156,6 +171,7 @@ PyFunction_SetDefaults(PyObject *op, PyObject *defaults)
156171
PyErr_SetString(PyExc_SystemError, "non-tuple default args");
157172
return -1;
158173
}
174+
((PyFunctionObject *)op)->func_version = 0;
159175
Py_XSETREF(((PyFunctionObject *)op)->func_defaults, defaults);
160176
return 0;
161177
}
@@ -187,6 +203,7 @@ PyFunction_SetKwDefaults(PyObject *op, PyObject *defaults)
187203
"non-dict keyword only default args");
188204
return -1;
189205
}
206+
((PyFunctionObject *)op)->func_version = 0;
190207
Py_XSETREF(((PyFunctionObject *)op)->func_kwdefaults, defaults);
191208
return 0;
192209
}
@@ -219,6 +236,7 @@ PyFunction_SetClosure(PyObject *op, PyObject *closure)
219236
Py_TYPE(closure)->tp_name);
220237
return -1;
221238
}
239+
((PyFunctionObject *)op)->func_version = 0;
222240
Py_XSETREF(((PyFunctionObject *)op)->func_closure, closure);
223241
return 0;
224242
}
@@ -250,6 +268,7 @@ PyFunction_SetAnnotations(PyObject *op, PyObject *annotations)
250268
"non-dict annotations");
251269
return -1;
252270
}
271+
((PyFunctionObject *)op)->func_version = 0;
253272
Py_XSETREF(((PyFunctionObject *)op)->func_annotations, annotations);
254273
return 0;
255274
}
@@ -308,6 +327,7 @@ func_set_code(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
308327
nclosure, nfree);
309328
return -1;
310329
}
330+
op->func_version = 0;
311331
Py_INCREF(value);
312332
Py_XSETREF(op->func_code, value);
313333
return 0;
@@ -392,6 +412,7 @@ func_set_defaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored
392412
return -1;
393413
}
394414

415+
op->func_version = 0;
395416
Py_XINCREF(value);
396417
Py_XSETREF(op->func_defaults, value);
397418
return 0;
@@ -433,6 +454,7 @@ func_set_kwdefaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignor
433454
return -1;
434455
}
435456

457+
op->func_version = 0;
436458
Py_XINCREF(value);
437459
Py_XSETREF(op->func_kwdefaults, value);
438460
return 0;
@@ -482,6 +504,7 @@ func_set_annotations(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(igno
482504
"__annotations__ must be set to a dict object");
483505
return -1;
484506
}
507+
op->func_version = 0;
485508
Py_XINCREF(value);
486509
Py_XSETREF(op->func_annotations, value);
487510
return 0;
@@ -611,6 +634,7 @@ func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals,
611634
static int
612635
func_clear(PyFunctionObject *op)
613636
{
637+
op->func_version = 0;
614638
Py_CLEAR(op->func_code);
615639
Py_CLEAR(op->func_globals);
616640
Py_CLEAR(op->func_builtins);

0 commit comments

Comments
 (0)