Skip to content

Commit 41bb43a

Browse files
committed
Issue #18408: Add a new PyFrame_FastToLocalsWithError() function to handle
exceptions when merging fast locals into f_locals of a frame. PyEval_GetLocals() now raises an exception and return NULL on failure.
1 parent 28c63f7 commit 41bb43a

File tree

7 files changed

+87
-46
lines changed

7 files changed

+87
-46
lines changed

Include/frameobject.h

+2
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ PyAPI_FUNC(PyObject **) PyFrame_ExtendStack(PyFrameObject *, int, int);
7878
/* Conversions between "fast locals" and locals in dictionary */
7979

8080
PyAPI_FUNC(void) PyFrame_LocalsToFast(PyFrameObject *, int);
81+
82+
PyAPI_FUNC(int) PyFrame_FastToLocalsWithError(PyFrameObject *f);
8183
PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *);
8284

8385
PyAPI_FUNC(int) PyFrame_ClearFreeList(void);

Misc/NEWS

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ Projected release date: 2013-11-24
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #18408: Add a new PyFrame_FastToLocalsWithError() function to handle
14+
exceptions when merging fast locals into f_locals of a frame.
15+
PyEval_GetLocals() now raises an exception and return NULL on failure.
16+
1317
- Issue #19369: Optimized the usage of __length_hint__().
1418

1519
- Issue #18603: Ensure that PyOS_mystricmp and PyOS_mystrnicmp are in the

Objects/frameobject.c

+50-27
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ static PyMemberDef frame_memberlist[] = {
2121
static PyObject *
2222
frame_getlocals(PyFrameObject *f, void *closure)
2323
{
24-
PyFrame_FastToLocals(f);
24+
if (PyFrame_FastToLocalsWithError(f) < 0)
25+
return NULL;
2526
Py_INCREF(f->f_locals);
2627
return f->f_locals;
2728
}
@@ -772,12 +773,9 @@ PyFrame_BlockPop(PyFrameObject *f)
772773
If deref is true, then the values being copied are cell variables
773774
and the value is extracted from the cell variable before being put
774775
in dict.
775-
776-
Exceptions raised while modifying the dict are silently ignored,
777-
because there is no good way to report them.
778776
*/
779777

780-
static void
778+
static int
781779
map_to_dict(PyObject *map, Py_ssize_t nmap, PyObject *dict, PyObject **values,
782780
int deref)
783781
{
@@ -794,14 +792,19 @@ map_to_dict(PyObject *map, Py_ssize_t nmap, PyObject *dict, PyObject **values,
794792
value = PyCell_GET(value);
795793
}
796794
if (value == NULL) {
797-
if (PyObject_DelItem(dict, key) != 0)
798-
PyErr_Clear();
795+
if (PyObject_DelItem(dict, key) != 0) {
796+
if (PyErr_ExceptionMatches(PyExc_KeyError))
797+
PyErr_Clear();
798+
else
799+
return -1;
800+
}
799801
}
800802
else {
801803
if (PyObject_SetItem(dict, key, value) != 0)
802-
PyErr_Clear();
804+
return -1;
803805
}
804806
}
807+
return 0;
805808
}
806809

807810
/* Copy values from the "locals" dict into the fast locals.
@@ -858,42 +861,49 @@ dict_to_map(PyObject *map, Py_ssize_t nmap, PyObject *dict, PyObject **values,
858861
}
859862
}
860863

861-
void
862-
PyFrame_FastToLocals(PyFrameObject *f)
864+
int
865+
PyFrame_FastToLocalsWithError(PyFrameObject *f)
863866
{
864867
/* Merge fast locals into f->f_locals */
865868
PyObject *locals, *map;
866869
PyObject **fast;
867-
PyObject *error_type, *error_value, *error_traceback;
868870
PyCodeObject *co;
869871
Py_ssize_t j;
870872
Py_ssize_t ncells, nfreevars;
871-
if (f == NULL)
872-
return;
873+
874+
if (f == NULL) {
875+
PyErr_BadInternalCall();
876+
return -1;
877+
}
873878
locals = f->f_locals;
874879
if (locals == NULL) {
875880
locals = f->f_locals = PyDict_New();
876-
if (locals == NULL) {
877-
PyErr_Clear(); /* Can't report it :-( */
878-
return;
879-
}
881+
if (locals == NULL)
882+
return -1;
880883
}
881884
co = f->f_code;
882885
map = co->co_varnames;
883-
if (!PyTuple_Check(map))
884-
return;
885-
PyErr_Fetch(&error_type, &error_value, &error_traceback);
886+
if (!PyTuple_Check(map)) {
887+
PyErr_Format(PyExc_SystemError,
888+
"co_varnames must be a tuple, not %s",
889+
Py_TYPE(map)->tp_name);
890+
return -1;
891+
}
886892
fast = f->f_localsplus;
887893
j = PyTuple_GET_SIZE(map);
888894
if (j > co->co_nlocals)
889895
j = co->co_nlocals;
890-
if (co->co_nlocals)
891-
map_to_dict(map, j, locals, fast, 0);
896+
if (co->co_nlocals) {
897+
if (map_to_dict(map, j, locals, fast, 0) < 0)
898+
return -1;
899+
}
892900
ncells = PyTuple_GET_SIZE(co->co_cellvars);
893901
nfreevars = PyTuple_GET_SIZE(co->co_freevars);
894902
if (ncells || nfreevars) {
895-
map_to_dict(co->co_cellvars, ncells,
896-
locals, fast + co->co_nlocals, 1);
903+
if (map_to_dict(co->co_cellvars, ncells,
904+
locals, fast + co->co_nlocals, 1))
905+
return -1;
906+
897907
/* If the namespace is unoptimized, then one of the
898908
following cases applies:
899909
1. It does not contain free variables, because it
@@ -903,11 +913,24 @@ PyFrame_FastToLocals(PyFrameObject *f)
903913
into the locals dict used by the class.
904914
*/
905915
if (co->co_flags & CO_OPTIMIZED) {
906-
map_to_dict(co->co_freevars, nfreevars,
907-
locals, fast + co->co_nlocals + ncells, 1);
916+
if (map_to_dict(co->co_freevars, nfreevars,
917+
locals, fast + co->co_nlocals + ncells, 1) < 0)
918+
return -1;
908919
}
909920
}
910-
PyErr_Restore(error_type, error_value, error_traceback);
921+
return 0;
922+
}
923+
924+
void
925+
PyFrame_FastToLocals(PyFrameObject *f)
926+
{
927+
int res;
928+
929+
assert(!PyErr_Occurred());
930+
931+
res = PyFrame_FastToLocalsWithError(f);
932+
if (res < 0)
933+
PyErr_Clear();
911934
}
912935

913936
void

Objects/object.c

+3-4
Original file line numberDiff line numberDiff line change
@@ -1407,12 +1407,11 @@ static PyObject *
14071407
_dir_locals(void)
14081408
{
14091409
PyObject *names;
1410-
PyObject *locals = PyEval_GetLocals();
1410+
PyObject *locals;
14111411

1412-
if (locals == NULL) {
1413-
PyErr_SetString(PyExc_SystemError, "frame does not exist");
1412+
locals = PyEval_GetLocals();
1413+
if (locals == NULL)
14141414
return NULL;
1415-
}
14161415

14171416
names = PyMapping_Keys(locals);
14181417
if (!names)

Python/bltinmodule.c

+9-8
Original file line numberDiff line numberDiff line change
@@ -755,8 +755,11 @@ builtin_eval(PyObject *self, PyObject *args)
755755
}
756756
if (globals == Py_None) {
757757
globals = PyEval_GetGlobals();
758-
if (locals == Py_None)
758+
if (locals == Py_None) {
759759
locals = PyEval_GetLocals();
760+
if (locals == NULL)
761+
return NULL;
762+
}
760763
}
761764
else if (locals == Py_None)
762765
locals = globals;
@@ -820,6 +823,8 @@ builtin_exec(PyObject *self, PyObject *args)
820823
globals = PyEval_GetGlobals();
821824
if (locals == Py_None) {
822825
locals = PyEval_GetLocals();
826+
if (locals == NULL)
827+
return NULL;
823828
}
824829
if (!globals || !locals) {
825830
PyErr_SetString(PyExc_SystemError,
@@ -1926,13 +1931,9 @@ builtin_vars(PyObject *self, PyObject *args)
19261931
return NULL;
19271932
if (v == NULL) {
19281933
d = PyEval_GetLocals();
1929-
if (d == NULL) {
1930-
if (!PyErr_Occurred())
1931-
PyErr_SetString(PyExc_SystemError,
1932-
"vars(): no locals!?");
1933-
}
1934-
else
1935-
Py_INCREF(d);
1934+
if (d == NULL)
1935+
return NULL;
1936+
Py_INCREF(d);
19361937
}
19371938
else {
19381939
_Py_IDENTIFIER(__dict__);

Python/ceval.c

+14-5
Original file line numberDiff line numberDiff line change
@@ -2472,7 +2472,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
24722472
TARGET(IMPORT_STAR) {
24732473
PyObject *from = POP(), *locals;
24742474
int err;
2475-
PyFrame_FastToLocals(f);
2475+
if (PyFrame_FastToLocalsWithError(f) < 0)
2476+
goto error;
2477+
24762478
locals = f->f_locals;
24772479
if (locals == NULL) {
24782480
PyErr_SetString(PyExc_SystemError,
@@ -4005,9 +4007,15 @@ PyObject *
40054007
PyEval_GetLocals(void)
40064008
{
40074009
PyFrameObject *current_frame = PyEval_GetFrame();
4008-
if (current_frame == NULL)
4010+
if (current_frame == NULL) {
4011+
PyErr_SetString(PyExc_SystemError, "frame does not exist");
40094012
return NULL;
4010-
PyFrame_FastToLocals(current_frame);
4013+
}
4014+
4015+
if (PyFrame_FastToLocalsWithError(current_frame) < 0)
4016+
return NULL;
4017+
4018+
assert(current_frame->f_locals != NULL);
40114019
return current_frame->f_locals;
40124020
}
40134021

@@ -4017,8 +4025,9 @@ PyEval_GetGlobals(void)
40174025
PyFrameObject *current_frame = PyEval_GetFrame();
40184026
if (current_frame == NULL)
40194027
return NULL;
4020-
else
4021-
return current_frame->f_globals;
4028+
4029+
assert(current_frame->f_globals != NULL);
4030+
return current_frame->f_globals;
40224031
}
40234032

40244033
PyFrameObject *

Python/sysmodule.c

+5-2
Original file line numberDiff line numberDiff line change
@@ -332,12 +332,16 @@ static PyObject *
332332
call_trampoline(PyThreadState *tstate, PyObject* callback,
333333
PyFrameObject *frame, int what, PyObject *arg)
334334
{
335-
PyObject *args = PyTuple_New(3);
335+
PyObject *args;
336336
PyObject *whatstr;
337337
PyObject *result;
338338

339+
args = PyTuple_New(3);
339340
if (args == NULL)
340341
return NULL;
342+
if (PyFrame_FastToLocalsWithError(frame) < 0)
343+
return NULL;
344+
341345
Py_INCREF(frame);
342346
whatstr = whatstrings[what];
343347
Py_INCREF(whatstr);
@@ -349,7 +353,6 @@ call_trampoline(PyThreadState *tstate, PyObject* callback,
349353
PyTuple_SET_ITEM(args, 2, arg);
350354

351355
/* call the Python-level function */
352-
PyFrame_FastToLocals(frame);
353356
result = PyEval_CallObject(callback, args);
354357
PyFrame_LocalsToFast(frame, 1);
355358
if (result == NULL)

0 commit comments

Comments
 (0)