Skip to content

Commit 8558e5e

Browse files
committed
with sync_fast_locals enabled eval and exec now skips initializing fast locals from arguments
1 parent 061eb21 commit 8558e5e

File tree

2 files changed

+40
-20
lines changed

2 files changed

+40
-20
lines changed

Lib/test/test_builtin.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,21 +1112,17 @@ def test_exec_filter_syntax_warnings_by_module(self):
11121112
self.assertIs(wm.category, SyntaxWarning)
11131113

11141114
def test_eval_exec_sync_fast_locals(self):
1115-
def func_assign():
1116-
a = 1
1117-
1118-
def func_read():
1115+
def func(a, *args, **kwargs):
11191116
b = a + 1
1120-
a = 3
1117+
del a
1118+
args.append(3)
1119+
kwargs['b'] = kwargs['a'] + 1
11211120

11221121
for executor in eval, exec:
11231122
with self.subTest(executor=executor.__name__):
1124-
ns = {}
1125-
executor(func_assign.__code__, {}, ns, sync_fast_locals=True)
1126-
self.assertEqual(ns, {'a': 1})
1127-
ns = {'a': 1}
1128-
executor(func_read.__code__, {}, ns, sync_fast_locals=True)
1129-
self.assertEqual(ns, {'a': 3, 'b': 2})
1123+
ns = {'a': 1, 'args': [2], 'kwargs': {'a': 4}}
1124+
executor(func, {}, ns, sync_fast_locals=True)
1125+
self.assertEqual(ns, {'b': 2, 'args': [2, 3], 'kwargs': {'a': 4, 'b': 5}})
11301126

11311127
def test_filter(self):
11321128
self.assertEqual(list(filter(lambda c: 'a' <= c <= 'z', 'Hello World')), list('elloorld'))

Python/ceval.c

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1532,13 +1532,13 @@ typedef struct {
15321532
static int
15331533
_PyEval_SyncLocalsToFast(_PyInterpreterFrame *frame)
15341534
{
1535-
PyObject *mapping = frame->f_locals;
1535+
PyObject *locals = frame->f_locals;
15361536
PyCodeObject *co = _PyFrame_GetCode(frame);
15371537
PyObject *names = co->co_localsplusnames;
15381538

15391539
for (int i = 0; i < co->co_nlocalsplus; i++) {
15401540
PyObject *name = PyTuple_GET_ITEM(names, i);
1541-
PyObject *value = PyObject_GetItem(mapping, name);
1541+
PyObject *value = PyObject_GetItem(locals, name);
15421542
if (value != NULL) {
15431543
frame->localsplus[i] = PyStackRef_FromPyObjectSteal(value);
15441544
}
@@ -1552,19 +1552,32 @@ _PyEval_SyncLocalsToFast(_PyInterpreterFrame *frame)
15521552
return 0;
15531553
}
15541554

1555+
15551556
static int
15561557
_PyEval_SyncFastToLocals(_PyInterpreterFrame *frame)
15571558
{
1558-
PyObject *mapping = frame->f_locals;
1559+
PyObject *locals = frame->f_locals;
15591560
PyCodeObject *co = _PyFrame_GetCode(frame);
15601561
PyObject *names = co->co_localsplusnames;
15611562

15621563
for (int i = 0; i < co->co_nlocalsplus; i++) {
1564+
PyObject *name = PyTuple_GET_ITEM(names, i);
15631565
_PyStackRef sref = frame->localsplus[i];
1564-
if (!PyStackRef_IsNull(sref)) {
1565-
PyObject *name = PyTuple_GET_ITEM(names, i);
1566-
PyObject *obj = PyStackRef_AsPyObjectSteal(sref);
1567-
if (PyObject_SetItem(mapping, name, obj) < 0) {
1566+
1567+
if (PyStackRef_IsNull(sref)) {
1568+
if (PyObject_DelItem(locals, name) < 0) {
1569+
if (PyErr_ExceptionMatches(PyExc_KeyError)) {
1570+
PyErr_Clear();
1571+
}
1572+
else {
1573+
return -1;
1574+
}
1575+
}
1576+
}
1577+
else {
1578+
PyObject *obj = PyStackRef_AsPyObjectNew(sref);
1579+
if (PyObject_SetItem(locals, name, obj) < 0) {
1580+
Py_DECREF(obj);
15681581
return -1;
15691582
}
15701583
}
@@ -2428,7 +2441,7 @@ _PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame * frame)
24282441
PyCodeObject *co = _PyFrame_GetCode(frame);
24292442
if ((co->co_flags & CO_OPTIMIZED) && frame->f_locals != NULL &&
24302443
frame->f_locals != frame->f_globals && _PyEval_SyncFastToLocals(frame) < 0) {
2431-
/* Swallow the error while the frame is in a teardown state */
2444+
/* Ignore any error while the frame is in a teardown state */
24322445
PyErr_WriteUnraisable(frame->f_locals);
24332446
}
24342447
// Update last_profiled_frame for remote profiler frame caching.
@@ -2463,7 +2476,18 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, _PyStackRef func,
24632476
goto fail;
24642477
}
24652478
_PyFrame_Initialize(tstate, frame, func, locals, code, 0, previous);
2466-
if (initialize_locals(tstate, func_obj, frame->localsplus, args, argcount, kwnames)) {
2479+
if (code->co_flags & CO_OPTIMIZED && locals != NULL && locals != func_obj->func_globals) {
2480+
for (size_t i = 0; i < argcount; i++) {
2481+
PyStackRef_CLOSE(args[i]);
2482+
}
2483+
if (kwnames) {
2484+
Py_ssize_t kwcount = PyTuple_GET_SIZE(kwnames);
2485+
for (Py_ssize_t i = 0; i < kwcount; i++) {
2486+
PyStackRef_CLOSE(args[i+argcount]);
2487+
}
2488+
}
2489+
}
2490+
else if (initialize_locals(tstate, func_obj, frame->localsplus, args, argcount, kwnames)) {
24672491
assert(frame->owner == FRAME_OWNED_BY_THREAD);
24682492
clear_thread_frame(tstate, frame);
24692493
return NULL;

0 commit comments

Comments
 (0)