Skip to content

Commit daa2008

Browse files
committed
added sync_fast_locals option to eval and exec
1 parent 5b5ee3c commit daa2008

File tree

7 files changed

+124
-22
lines changed

7 files changed

+124
-22
lines changed

Include/internal/pycore_global_objects_fini_generated.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,7 @@ struct _Py_global_strings {
803803
STRUCT_FOR_ID(sub_key)
804804
STRUCT_FOR_ID(subcalls)
805805
STRUCT_FOR_ID(symmetric_difference_update)
806+
STRUCT_FOR_ID(sync_fast_locals)
806807
STRUCT_FOR_ID(tabsize)
807808
STRUCT_FOR_ID(tag)
808809
STRUCT_FOR_ID(take_bytes)

Include/internal/pycore_runtime_init_generated.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_unicodeobject_generated.h

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/bltinmodule.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,8 @@ eval as builtin_eval
955955
/
956956
globals: object = None
957957
locals: object = None
958+
*
959+
sync_fast_locals: bool = False
958960
959961
Evaluate the given source in the context of globals and locals.
960962
@@ -967,8 +969,8 @@ If only globals is given, locals defaults to it.
967969

968970
static PyObject *
969971
builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals,
970-
PyObject *locals)
971-
/*[clinic end generated code: output=0a0824aa70093116 input=7c7bce5299a89062]*/
972+
PyObject *locals, int sync_fast_locals)
973+
/*[clinic end generated code: output=a573401639e51347 input=440105eb08930503]*/
972974
{
973975
PyThreadState *tstate = _PyThreadState_GET();
974976
PyObject *result = NULL, *source_copy;
@@ -1037,6 +1039,10 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals,
10371039
"code object passed to eval() may not contain free variables");
10381040
goto error;
10391041
}
1042+
if (!sync_fast_locals && ((PyCodeObject *)source)->co_flags & CO_OPTIMIZED) {
1043+
Py_DECREF(locals);
1044+
locals = NULL;
1045+
}
10401046
result = PyEval_EvalCode(source, globals, locals);
10411047
}
10421048
else {
@@ -1078,6 +1084,7 @@ exec as builtin_exec
10781084
locals: object = None
10791085
*
10801086
closure: object(c_default="NULL") = None
1087+
sync_fast_locals: bool = False
10811088
10821089
Execute the given source in the context of globals and locals.
10831090
@@ -1092,8 +1099,8 @@ when source is a code object requiring exactly that many cellvars.
10921099

10931100
static PyObject *
10941101
builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
1095-
PyObject *locals, PyObject *closure)
1096-
/*[clinic end generated code: output=7579eb4e7646743d input=25e989b6d87a3a21]*/
1102+
PyObject *locals, PyObject *closure, int sync_fast_locals)
1103+
/*[clinic end generated code: output=ceab303bd7575dcf input=3a4103a242b26356]*/
10971104
{
10981105
PyThreadState *tstate = _PyThreadState_GET();
10991106
PyObject *v;
@@ -1189,6 +1196,10 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
11891196
goto error;
11901197
}
11911198

1199+
if (!sync_fast_locals && ((PyCodeObject *)source)->co_flags & CO_OPTIMIZED) {
1200+
Py_DECREF(locals);
1201+
locals = NULL;
1202+
}
11921203
if (!closure) {
11931204
v = PyEval_EvalCode(source, globals, locals);
11941205
} else {
@@ -1225,7 +1236,7 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
12251236
if (v == NULL)
12261237
goto error;
12271238
Py_DECREF(globals);
1228-
Py_DECREF(locals);
1239+
Py_XDECREF(locals);
12291240
Py_DECREF(v);
12301241
Py_RETURN_NONE;
12311242

Python/ceval.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,6 +1529,46 @@ typedef struct {
15291529
_PyStackRef stack[1];
15301530
} _PyEntryFrame;
15311531

1532+
static int
1533+
_PyEval_SyncMappingToFast(_PyInterpreterFrame *frame)
1534+
{
1535+
PyObject *mapping = frame->f_locals;
1536+
PyCodeObject *co = _PyFrame_GetCode(frame);
1537+
PyObject *names = co->co_localsplusnames;
1538+
1539+
for (int i = 0; i < co->co_nlocalsplus; i++) {
1540+
PyObject *name = PyTuple_GET_ITEM(names, i);
1541+
PyObject *value = PyObject_GetItem(mapping, name);
1542+
if (value != NULL) {
1543+
frame->localsplus[i] = PyStackRef_FromPyObjectSteal(value);
1544+
}
1545+
else {
1546+
PyErr_Clear();
1547+
}
1548+
}
1549+
return 0;
1550+
}
1551+
1552+
static int
1553+
_PyEval_SyncFastToMapping(_PyInterpreterFrame *frame)
1554+
{
1555+
PyObject *mapping = frame->f_locals;
1556+
PyCodeObject *co = _PyFrame_GetCode(frame);
1557+
PyObject *names = co->co_localsplusnames;
1558+
1559+
for (int i = 0; i < co->co_nlocalsplus; i++) {
1560+
_PyStackRef sref = frame->localsplus[i];
1561+
if (!PyStackRef_IsNull(sref)) {
1562+
PyObject *name = PyTuple_GET_ITEM(names, i);
1563+
PyObject *obj = PyStackRef_AsPyObjectSteal(sref);
1564+
if (PyObject_SetItem(mapping, name, obj) < 0) {
1565+
PyErr_Clear();
1566+
}
1567+
}
1568+
}
1569+
return 0;
1570+
}
1571+
15321572
PyObject* _Py_HOT_FUNCTION DONT_SLP_VECTORIZE
15331573
_PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag)
15341574
{
@@ -1591,6 +1631,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
15911631
frame->previous = &entry.frame;
15921632
tstate->current_frame = frame;
15931633
entry.frame.localsplus[0] = PyStackRef_NULL;
1634+
PyCodeObject *co = _PyFrame_GetCode(frame);
1635+
if ((co->co_flags & CO_OPTIMIZED) &&
1636+
frame->f_locals != NULL &&
1637+
frame->f_locals != frame->f_globals)
1638+
{
1639+
if (_PyEval_SyncMappingToFast(frame) < 0) {
1640+
goto early_exit;
1641+
}
1642+
}
15941643
#ifdef _Py_TIER2
15951644
if (tstate->current_executor != NULL) {
15961645
entry.frame.localsplus[0] = PyStackRef_FromPyObjectNew(tstate->current_executor);
@@ -2377,6 +2426,15 @@ clear_gen_frame(PyThreadState *tstate, _PyInterpreterFrame * frame)
23772426
void
23782427
_PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame * frame)
23792428
{
2429+
PyCodeObject *co = _PyFrame_GetCode(frame);
2430+
if ((co->co_flags & CO_OPTIMIZED) &&
2431+
frame->f_locals != NULL &&
2432+
frame->f_locals != frame->f_globals)
2433+
{
2434+
if (_PyEval_SyncFastToMapping(frame) < 0) {
2435+
PyErr_WriteUnraisable(frame->f_locals);
2436+
}
2437+
}
23802438
// Update last_profiled_frame for remote profiler frame caching.
23812439
// By this point, tstate->current_frame is already set to the parent frame.
23822440
// Only update if we're popping the exact frame that was last profiled.

Python/clinic/bltinmodule.c.h

Lines changed: 43 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)