summaryrefslogtreecommitdiffstats
path: root/Python/ceval.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/ceval.c')
-rw-r--r--Python/ceval.c610
1 files changed, 372 insertions, 238 deletions
diff --git a/Python/ceval.c b/Python/ceval.c
index f3433f1..f6d4b0b 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -13,7 +13,6 @@
#include "code.h"
#include "frameobject.h"
-#include "eval.h"
#include "opcode.h"
#include "structmember.h"
@@ -136,8 +135,10 @@ static PyObject * cmp_outcome(int, PyObject *, PyObject *);
static PyObject * import_from(PyObject *, PyObject *);
static int import_all_from(PyObject *, PyObject *);
static void format_exc_check_arg(PyObject *, const char *, PyObject *);
+static void format_exc_unbound(PyCodeObject *co, int oparg);
static PyObject * unicode_concatenate(PyObject *, PyObject *,
PyFrameObject *, unsigned char *);
+static PyObject * special_lookup(PyObject *, char *, PyObject **);
#define NAME_ERROR_MSG \
"name '%.200s' is not defined"
@@ -217,42 +218,127 @@ PyEval_GetCallStats(PyObject *self)
#ifdef WITH_THREAD
+#define GIL_REQUEST _Py_atomic_load_relaxed(&gil_drop_request)
+#else
+#define GIL_REQUEST 0
+#endif
+
+/* This can set eval_breaker to 0 even though gil_drop_request became
+ 1. We believe this is all right because the eval loop will release
+ the GIL eventually anyway. */
+#define COMPUTE_EVAL_BREAKER() \
+ _Py_atomic_store_relaxed( \
+ &eval_breaker, \
+ GIL_REQUEST | \
+ _Py_atomic_load_relaxed(&pendingcalls_to_do) | \
+ pending_async_exc)
+
+#ifdef WITH_THREAD
+
+#define SET_GIL_DROP_REQUEST() \
+ do { \
+ _Py_atomic_store_relaxed(&gil_drop_request, 1); \
+ _Py_atomic_store_relaxed(&eval_breaker, 1); \
+ } while (0)
+
+#define RESET_GIL_DROP_REQUEST() \
+ do { \
+ _Py_atomic_store_relaxed(&gil_drop_request, 0); \
+ COMPUTE_EVAL_BREAKER(); \
+ } while (0)
+
+#endif
+
+/* Pending calls are only modified under pending_lock */
+#define SIGNAL_PENDING_CALLS() \
+ do { \
+ _Py_atomic_store_relaxed(&pendingcalls_to_do, 1); \
+ _Py_atomic_store_relaxed(&eval_breaker, 1); \
+ } while (0)
+
+#define UNSIGNAL_PENDING_CALLS() \
+ do { \
+ _Py_atomic_store_relaxed(&pendingcalls_to_do, 0); \
+ COMPUTE_EVAL_BREAKER(); \
+ } while (0)
+
+#define SIGNAL_ASYNC_EXC() \
+ do { \
+ pending_async_exc = 1; \
+ _Py_atomic_store_relaxed(&eval_breaker, 1); \
+ } while (0)
+
+#define UNSIGNAL_ASYNC_EXC() \
+ do { pending_async_exc = 0; COMPUTE_EVAL_BREAKER(); } while (0)
+
+
+#ifdef WITH_THREAD
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include "pythread.h"
-static PyThread_type_lock interpreter_lock = 0; /* This is the GIL */
static PyThread_type_lock pending_lock = 0; /* for pending calls */
static long main_thread = 0;
+/* This single variable consolidates all requests to break out of the fast path
+ in the eval loop. */
+static _Py_atomic_int eval_breaker = {0};
+/* Request for dropping the GIL */
+static _Py_atomic_int gil_drop_request = {0};
+/* Request for running pending calls. */
+static _Py_atomic_int pendingcalls_to_do = {0};
+/* Request for looking at the `async_exc` field of the current thread state.
+ Guarded by the GIL. */
+static int pending_async_exc = 0;
+
+#include "ceval_gil.h"
int
PyEval_ThreadsInitialized(void)
{
- return interpreter_lock != 0;
+ return gil_created();
}
void
PyEval_InitThreads(void)
{
- if (interpreter_lock)
+ if (gil_created())
return;
- interpreter_lock = PyThread_allocate_lock();
- PyThread_acquire_lock(interpreter_lock, 1);
+ create_gil();
+ take_gil(PyThreadState_GET());
main_thread = PyThread_get_thread_ident();
+ if (!pending_lock)
+ pending_lock = PyThread_allocate_lock();
+}
+
+void
+_PyEval_FiniThreads(void)
+{
+ if (!gil_created())
+ return;
+ destroy_gil();
+ assert(!gil_created());
}
void
PyEval_AcquireLock(void)
{
- PyThread_acquire_lock(interpreter_lock, 1);
+ PyThreadState *tstate = PyThreadState_GET();
+ if (tstate == NULL)
+ Py_FatalError("PyEval_AcquireLock: current thread state is NULL");
+ take_gil(tstate);
}
void
PyEval_ReleaseLock(void)
{
- PyThread_release_lock(interpreter_lock);
+ /* This function must succeed when the current thread state is NULL.
+ We therefore avoid PyThreadState_GET() which dumps a fatal error
+ in debug mode.
+ */
+ drop_gil((PyThreadState*)_Py_atomic_load_relaxed(
+ &_PyThreadState_Current));
}
void
@@ -261,8 +347,8 @@ PyEval_AcquireThread(PyThreadState *tstate)
if (tstate == NULL)
Py_FatalError("PyEval_AcquireThread: NULL new thread state");
/* Check someone has called PyEval_InitThreads() to create the lock */
- assert(interpreter_lock);
- PyThread_acquire_lock(interpreter_lock, 1);
+ assert(gil_created());
+ take_gil(tstate);
if (PyThreadState_Swap(tstate) != NULL)
Py_FatalError(
"PyEval_AcquireThread: non-NULL old thread state");
@@ -275,7 +361,7 @@ PyEval_ReleaseThread(PyThreadState *tstate)
Py_FatalError("PyEval_ReleaseThread: NULL thread state");
if (PyThreadState_Swap(NULL) != tstate)
Py_FatalError("PyEval_ReleaseThread: wrong thread state");
- PyThread_release_lock(interpreter_lock);
+ drop_gil(tstate);
}
/* This function is called from PyOS_AfterFork to ensure that newly
@@ -287,17 +373,13 @@ void
PyEval_ReInitThreads(void)
{
PyObject *threading, *result;
- PyThreadState *tstate;
+ PyThreadState *tstate = PyThreadState_GET();
- if (!interpreter_lock)
+ if (!gil_created())
return;
- /*XXX Can't use PyThread_free_lock here because it does too
- much error-checking. Doing this cleanly would require
- adding a new function to each thread_*.h. Instead, just
- create a new lock and waste a little bit of memory */
- interpreter_lock = PyThread_allocate_lock();
+ recreate_gil();
pending_lock = PyThread_allocate_lock();
- PyThread_acquire_lock(interpreter_lock, 1);
+ take_gil(tstate);
main_thread = PyThread_get_thread_ident();
/* Update the threading module with the new state.
@@ -317,7 +399,20 @@ PyEval_ReInitThreads(void)
Py_DECREF(result);
Py_DECREF(threading);
}
-#endif
+
+#else
+static _Py_atomic_int eval_breaker = {0};
+static int pending_async_exc = 0;
+#endif /* WITH_THREAD */
+
+/* This function is used to signal that async exceptions are waiting to be
+ raised, therefore it is also useful in non-threaded builds. */
+
+void
+_PyEval_SignalAsyncExc(void)
+{
+ SIGNAL_ASYNC_EXC();
+}
/* Functions save_thread and restore_thread are always defined so
dynamically loaded modules needn't be compiled separately for use
@@ -330,8 +425,8 @@ PyEval_SaveThread(void)
if (tstate == NULL)
Py_FatalError("PyEval_SaveThread: NULL tstate");
#ifdef WITH_THREAD
- if (interpreter_lock)
- PyThread_release_lock(interpreter_lock);
+ if (gil_created())
+ drop_gil(tstate);
#endif
return tstate;
}
@@ -342,9 +437,9 @@ PyEval_RestoreThread(PyThreadState *tstate)
if (tstate == NULL)
Py_FatalError("PyEval_RestoreThread: NULL tstate");
#ifdef WITH_THREAD
- if (interpreter_lock) {
+ if (gil_created()) {
int err = errno;
- PyThread_acquire_lock(interpreter_lock, 1);
+ take_gil(tstate);
errno = err;
}
#endif
@@ -390,7 +485,6 @@ static struct {
} pendingcalls[NPENDINGCALLS];
static int pendingfirst = 0;
static int pendinglast = 0;
-static volatile int pendingcalls_to_do = 1; /* trigger initialization of lock */
static char pendingbusy = 0;
int
@@ -429,8 +523,7 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
pendinglast = j;
}
/* signal main loop */
- _Py_Ticker = 0;
- pendingcalls_to_do = 1;
+ SIGNAL_PENDING_CALLS();
if (lock != NULL)
PyThread_release_lock(lock);
return result;
@@ -472,7 +565,10 @@ Py_MakePendingCalls(void)
arg = pendingcalls[j].arg;
pendingfirst = (j + 1) % NPENDINGCALLS;
}
- pendingcalls_to_do = pendingfirst != pendinglast;
+ if (pendingfirst != pendinglast)
+ SIGNAL_PENDING_CALLS();
+ else
+ UNSIGNAL_PENDING_CALLS();
PyThread_release_lock(pending_lock);
/* having released the lock, perform the callback */
if (func == NULL)
@@ -517,7 +613,7 @@ static struct {
} pendingcalls[NPENDINGCALLS];
static volatile int pendingfirst = 0;
static volatile int pendinglast = 0;
-static volatile int pendingcalls_to_do = 0;
+static _Py_atomic_int pendingcalls_to_do = {0};
int
Py_AddPendingCall(int (*func)(void *), void *arg)
@@ -538,8 +634,7 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
pendingcalls[i].arg = arg;
pendinglast = j;
- _Py_Ticker = 0;
- pendingcalls_to_do = 1; /* Signal main loop */
+ SIGNAL_PENDING_CALLS();
busy = 0;
/* XXX End critical section */
return 0;
@@ -552,7 +647,7 @@ Py_MakePendingCalls(void)
if (busy)
return 0;
busy = 1;
- pendingcalls_to_do = 0;
+ UNSIGNAL_PENDING_CALLS();
for (;;) {
int i;
int (*func)(void *);
@@ -565,7 +660,7 @@ Py_MakePendingCalls(void)
pendingfirst = (i + 1) % NPENDINGCALLS;
if (func(arg) < 0) {
busy = 0;
- pendingcalls_to_do = 1; /* We're not done yet */
+ SIGNAL_PENDING_CALLS(); /* We're not done yet */
return -1;
}
}
@@ -658,13 +753,10 @@ static int unpack_iterable(PyObject *, int, int, PyObject **);
fast_next_opcode*/
static int _Py_TracingPossible = 0;
-/* for manipulating the thread switch and periodic "stuff" - used to be
- per thread, now just a pair o' globals */
-int _Py_CheckInterval = 100;
-volatile int _Py_Ticker = 0; /* so that we hit a "tick" first thing */
+
PyObject *
-PyEval_EvalCode(PyCodeObject *co, PyObject *globals, PyObject *locals)
+PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals)
{
return PyEval_EvalCodeEx(co,
globals, locals,
@@ -763,11 +855,24 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
-fno-crossjumping).
*/
-#if defined(USE_COMPUTED_GOTOS) && defined(DYNAMIC_EXECUTION_PROFILE)
+#ifdef DYNAMIC_EXECUTION_PROFILE
#undef USE_COMPUTED_GOTOS
+#define USE_COMPUTED_GOTOS 0
#endif
-#ifdef USE_COMPUTED_GOTOS
+#ifdef HAVE_COMPUTED_GOTOS
+ #ifndef USE_COMPUTED_GOTOS
+ #define USE_COMPUTED_GOTOS 1
+ #endif
+#else
+ #if defined(USE_COMPUTED_GOTOS) && USE_COMPUTED_GOTOS
+ #error "Computed gotos are not supported on this compiler."
+ #endif
+ #undef USE_COMPUTED_GOTOS
+ #define USE_COMPUTED_GOTOS 0
+#endif
+
+#if USE_COMPUTED_GOTOS
/* Import the static jump table */
#include "opcode_targets.h"
@@ -791,11 +896,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
#define DISPATCH() \
{ \
- /* Avoid multiple loads from _Py_Ticker despite `volatile` */ \
- int _tick = _Py_Ticker - 1; \
- _Py_Ticker = _tick; \
- if (_tick >= 0) { \
- FAST_DISPATCH(); \
+ if (!_Py_atomic_load_relaxed(&eval_breaker)) { \
+ FAST_DISPATCH(); \
} \
continue; \
}
@@ -916,7 +1018,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
*/
-#if defined(DYNAMIC_EXECUTION_PROFILE) || defined(USE_COMPUTED_GOTOS)
+#if defined(DYNAMIC_EXECUTION_PROFILE) || USE_COMPUTED_GOTOS
#define PREDICT(op) if (0) goto PRED_##op
#define PREDICTED(op) PRED_##op:
#define PREDICTED_WITH_ARG(op) PRED_##op:
@@ -937,10 +1039,12 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
#define SECOND() (stack_pointer[-2])
#define THIRD() (stack_pointer[-3])
#define FOURTH() (stack_pointer[-4])
+#define PEEK(n) (stack_pointer[-(n)])
#define SET_TOP(v) (stack_pointer[-1] = (v))
#define SET_SECOND(v) (stack_pointer[-2] = (v))
#define SET_THIRD(v) (stack_pointer[-3] = (v))
#define SET_FOURTH(v) (stack_pointer[-4] = (v))
+#define SET_VALUE(n, v) (stack_pointer[-(n)] = (v))
#define BASIC_STACKADJ(n) (stack_pointer += n)
#define BASIC_PUSH(v) (*stack_pointer++ = (v))
#define BASIC_POP() (*--stack_pointer)
@@ -1124,7 +1228,16 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
lltrace = PyDict_GetItemString(f->f_globals, "__lltrace__") != NULL;
#endif
#if defined(Py_DEBUG) || defined(LLTRACE)
- filename = _PyUnicode_AsString(co->co_filename);
+ {
+ PyObject *error_type, *error_value, *error_traceback;
+ PyErr_Fetch(&error_type, &error_value, &error_traceback);
+ filename = _PyUnicode_AsString(co->co_filename);
+ if (filename == NULL && tstate->overflowed) {
+ /* maximum recursion depth exceeded */
+ goto exit_eval_frame;
+ }
+ PyErr_Restore(error_type, error_value, error_traceback);
+ }
#endif
why = WHY_NOT;
@@ -1166,55 +1279,46 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
async I/O handler); see Py_AddPendingCall() and
Py_MakePendingCalls() above. */
- if (--_Py_Ticker < 0) {
+ if (_Py_atomic_load_relaxed(&eval_breaker)) {
if (*next_instr == SETUP_FINALLY) {
/* Make the last opcode before
a try: finally: block uninterruptable. */
goto fast_next_opcode;
}
- _Py_Ticker = _Py_CheckInterval;
tstate->tick_counter++;
#ifdef WITH_TSC
ticked = 1;
#endif
- if (pendingcalls_to_do) {
+ if (_Py_atomic_load_relaxed(&pendingcalls_to_do)) {
if (Py_MakePendingCalls() < 0) {
why = WHY_EXCEPTION;
goto on_error;
}
- if (pendingcalls_to_do)
- /* MakePendingCalls() didn't succeed.
- Force early re-execution of this
- "periodic" code, possibly after
- a thread switch */
- _Py_Ticker = 0;
}
#ifdef WITH_THREAD
- if (interpreter_lock) {
+ if (_Py_atomic_load_relaxed(&gil_drop_request)) {
/* Give another thread a chance */
-
if (PyThreadState_Swap(NULL) != tstate)
Py_FatalError("ceval: tstate mix-up");
- PyThread_release_lock(interpreter_lock);
+ drop_gil(tstate);
/* Other threads may run now */
- PyThread_acquire_lock(interpreter_lock, 1);
+ take_gil(tstate);
if (PyThreadState_Swap(tstate) != NULL)
Py_FatalError("ceval: orphan tstate");
-
- /* Check for thread interrupts */
-
- if (tstate->async_exc != NULL) {
- x = tstate->async_exc;
- tstate->async_exc = NULL;
- PyErr_SetNone(x);
- Py_DECREF(x);
- why = WHY_EXCEPTION;
- goto on_error;
- }
}
#endif
+ /* Check for asynchronous exceptions. */
+ if (tstate->async_exc != NULL) {
+ x = tstate->async_exc;
+ tstate->async_exc = NULL;
+ UNSIGNAL_ASYNC_EXC();
+ PyErr_SetNone(x);
+ Py_DECREF(x);
+ why = WHY_EXCEPTION;
+ goto on_error;
+ }
}
fast_next_opcode:
@@ -1335,50 +1439,21 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
SET_THIRD(v);
FAST_DISPATCH();
- TARGET(ROT_FOUR)
- u = TOP();
- v = SECOND();
- w = THIRD();
- x = FOURTH();
- SET_TOP(v);
- SET_SECOND(w);
- SET_THIRD(x);
- SET_FOURTH(u);
- FAST_DISPATCH();
-
TARGET(DUP_TOP)
v = TOP();
Py_INCREF(v);
PUSH(v);
FAST_DISPATCH();
- TARGET(DUP_TOPX)
- if (oparg == 2) {
- x = TOP();
- Py_INCREF(x);
- w = SECOND();
- Py_INCREF(w);
- STACKADJ(2);
- SET_TOP(x);
- SET_SECOND(w);
- FAST_DISPATCH();
- } else if (oparg == 3) {
- x = TOP();
- Py_INCREF(x);
- w = SECOND();
- Py_INCREF(w);
- v = THIRD();
- Py_INCREF(v);
- STACKADJ(3);
- SET_TOP(x);
- SET_SECOND(w);
- SET_THIRD(v);
- FAST_DISPATCH();
- }
- Py_FatalError("invalid argument to DUP_TOPX"
- " (bytecode corruption?)");
- /* Never returns, so don't bother to set why. */
- break;
+ TARGET(DUP_TOP_TWO)
+ x = TOP();
+ Py_INCREF(x);
+ w = SECOND();
+ Py_INCREF(w);
+ STACKADJ(2);
+ SET_TOP(x);
+ SET_SECOND(w);
+ FAST_DISPATCH();
TARGET(UNARY_POSITIVE)
v = TOP();
@@ -1566,7 +1641,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
TARGET(LIST_APPEND)
w = POP();
- v = stack_pointer[-oparg];
+ v = PEEK(oparg);
err = PyList_Append(v, w);
Py_DECREF(w);
if (err == 0) {
@@ -1841,15 +1916,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
created when the exception was caught, otherwise
the stack will be in an inconsistent state. */
PyTryBlock *b = PyFrame_BlockPop(f);
- if (b->b_type != EXCEPT_HANDLER) {
- PyErr_SetString(PyExc_SystemError,
- "popped block is not an except handler");
- why = WHY_EXCEPTION;
- }
- else {
- UNWIND_EXCEPT_HANDLER(b);
- why = WHY_NOT;
- }
+ assert(b->b_type == EXCEPT_HANDLER);
+ UNWIND_EXCEPT_HANDLER(b);
+ why = WHY_NOT;
}
}
else if (PyExceptionClass_Check(v)) {
@@ -1933,7 +2002,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
}
} else if (unpack_iterable(v, oparg, -1,
stack_pointer + oparg)) {
- stack_pointer += oparg;
+ STACKADJ(oparg);
} else {
/* unpack_iterable() raised an exception */
why = WHY_EXCEPTION;
@@ -2033,7 +2102,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
/* Inline the PyDict_GetItem() calls.
WARNING: this is an extreme speed hack.
Do not try this at home. */
- long hash = ((PyUnicodeObject *)w)->hash;
+ Py_hash_t hash = ((PyUnicodeObject *)w)->hash;
if (hash != -1) {
PyDictObject *d;
PyDictEntry *e;
@@ -2093,6 +2162,16 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
);
break;
+ TARGET(DELETE_DEREF)
+ x = freevars[oparg];
+ if (PyCell_GET(x) != NULL) {
+ PyCell_Set(x, NULL);
+ DISPATCH();
+ }
+ err = -1;
+ format_exc_unbound(co, oparg);
+ break;
+
TARGET(LOAD_CLOSURE)
x = freevars[oparg];
Py_INCREF(x);
@@ -2108,22 +2187,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
DISPATCH();
}
err = -1;
- /* Don't stomp existing exception */
- if (PyErr_Occurred())
- break;
- if (oparg < PyTuple_GET_SIZE(co->co_cellvars)) {
- v = PyTuple_GET_ITEM(co->co_cellvars,
- oparg);
- format_exc_check_arg(
- PyExc_UnboundLocalError,
- UNBOUNDLOCAL_ERROR_MSG,
- v);
- } else {
- v = PyTuple_GET_ITEM(co->co_freevars, oparg -
- PyTuple_GET_SIZE(co->co_cellvars));
- format_exc_check_arg(PyExc_NameError,
- UNBOUNDFREE_ERROR_MSG, v);
- }
+ format_exc_unbound(co, oparg);
break;
TARGET(STORE_DEREF)
@@ -2474,6 +2538,33 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
STACK_LEVEL());
DISPATCH();
+ TARGET(SETUP_WITH)
+ {
+ static PyObject *exit, *enter;
+ w = TOP();
+ x = special_lookup(w, "__exit__", &exit);
+ if (!x)
+ break;
+ SET_TOP(x);
+ u = special_lookup(w, "__enter__", &enter);
+ Py_DECREF(w);
+ if (!u) {
+ x = NULL;
+ break;
+ }
+ x = PyObject_CallFunctionObjArgs(u, NULL);
+ Py_DECREF(u);
+ if (!x)
+ break;
+ /* Setup the finally block before pushing the result
+ of __enter__ on the stack. */
+ PyFrame_BlockSetup(f, SETUP_FINALLY, INSTR_OFFSET() + oparg,
+ STACK_LEVEL());
+
+ PUSH(x);
+ DISPATCH();
+ }
+
TARGET(WITH_CLEANUP)
{
/* At the top of the stack are 1-3 values indicating
@@ -2482,33 +2573,70 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
- (TOP, SECOND) = (WHY_{RETURN,CONTINUE}), retval
- TOP = WHY_*; no retval below it
- (TOP, SECOND, THIRD) = exc_info()
+ (FOURTH, FITH, SIXTH) = previous exception for EXCEPT_HANDLER
Below them is EXIT, the context.__exit__ bound method.
In the last case, we must call
EXIT(TOP, SECOND, THIRD)
otherwise we must call
EXIT(None, None, None)
- In all cases, we remove EXIT from the stack, leaving
- the rest in the same order.
+ In the first two cases, we remove EXIT from the
+ stack, leaving the rest in the same order. In the
+ third case, we shift the bottom 3 values of the
+ stack down, and replace the empty spot with NULL.
In addition, if the stack represents an exception,
*and* the function call returns a 'true' value, we
- "zap" this information, to prevent END_FINALLY from
- re-raising the exception. (But non-local gotos
- should still be resumed.)
+ push WHY_SILENCED onto the stack. END_FINALLY will
+ then not re-raise the exception. (But non-local
+ gotos should still be resumed.)
*/
- PyObject *exit_func = POP();
+ PyObject *exit_func;
u = TOP();
if (u == Py_None) {
+ (void)POP();
+ exit_func = TOP();
+ SET_TOP(u);
v = w = Py_None;
}
else if (PyLong_Check(u)) {
+ (void)POP();
+ switch(PyLong_AsLong(u)) {
+ case WHY_RETURN:
+ case WHY_CONTINUE:
+ /* Retval in TOP. */
+ exit_func = SECOND();
+ SET_SECOND(TOP());
+ SET_TOP(u);
+ break;
+ default:
+ exit_func = TOP();
+ SET_TOP(u);
+ break;
+ }
u = v = w = Py_None;
}
else {
+ PyObject *tp, *exc, *tb;
+ PyTryBlock *block;
v = SECOND();
w = THIRD();
+ tp = FOURTH();
+ exc = PEEK(5);
+ tb = PEEK(6);
+ exit_func = PEEK(7);
+ SET_VALUE(7, tb);
+ SET_VALUE(6, exc);
+ SET_VALUE(5, tp);
+ /* UNWIND_EXCEPT_HANDLER will pop this off. */
+ SET_FOURTH(NULL);
+ /* We just shifted the stack down, so we have
+ to tell the except handler block that the
+ values are lower than it expects. */
+ block = &f->f_blockstack[f->f_iblock - 1];
+ assert(block->b_type == EXCEPT_HANDLER);
+ block->b_level--;
}
/* XXX Not the fastest way to call it... */
x = PyObject_CallFunctionObjArgs(exit_func, u, v, w,
@@ -2528,11 +2656,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
else if (err > 0) {
err = 0;
/* There was an exception and a True return */
- STACKADJ(-2);
- SET_TOP(PyLong_FromLong((long) WHY_SILENCED));
- Py_DECREF(u);
- Py_DECREF(v);
- Py_DECREF(w);
+ PUSH(PyLong_FromLong((long) WHY_SILENCED));
}
PREDICT(END_FINALLY);
break;
@@ -2717,13 +2841,13 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
oparg = oparg<<16 | NEXTARG();
goto dispatch_opcode;
-#ifdef USE_COMPUTED_GOTOS
+#if USE_COMPUTED_GOTOS
_unknown_opcode:
#endif
default:
fprintf(stderr,
"XXX lineno: %d, opcode: %d\n",
- PyCode_Addr2Line(f->f_code, f->f_lasti),
+ PyFrame_GetLineNumber(f),
opcode);
PyErr_SetString(PyExc_SystemError, "unknown opcode");
why = WHY_EXCEPTION;
@@ -2801,19 +2925,18 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
fast_block_end:
while (why != WHY_NOT && f->f_iblock > 0) {
- PyTryBlock *b = PyFrame_BlockPop(f);
+ /* Peek at the current block. */
+ PyTryBlock *b = &f->f_blockstack[f->f_iblock - 1];
assert(why != WHY_YIELD);
if (b->b_type == SETUP_LOOP && why == WHY_CONTINUE) {
- /* For a continue inside a try block,
- don't pop the block for the loop. */
- PyFrame_BlockSetup(f, b->b_type, b->b_handler,
- b->b_level);
why = WHY_NOT;
JUMPTO(PyLong_AS_LONG(retval));
Py_DECREF(retval);
break;
}
+ /* Now we have to pop the block. */
+ f->f_iblock--;
if (b->b_type == EXCEPT_HANDLER) {
UNWIND_EXCEPT_HANDLER(b);
@@ -2937,15 +3060,17 @@ exit_eval_frame:
the test in the if statements in Misc/gdbinit (pystack and pystackv). */
PyObject *
-PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
+PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
PyObject **args, int argcount, PyObject **kws, int kwcount,
PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure)
{
+ PyCodeObject* co = (PyCodeObject*)_co;
register PyFrameObject *f;
register PyObject *retval = NULL;
register PyObject **fastlocals, **freevars;
PyThreadState *tstate = PyThreadState_GET();
PyObject *x, *u;
+ int total_args = co->co_argcount + co->co_kwonlyargcount;
if (globals == NULL) {
PyErr_SetString(PyExc_SystemError,
@@ -2962,9 +3087,7 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
fastlocals = f->f_localsplus;
freevars = f->f_localsplus + co->co_nlocals;
- if (co->co_argcount > 0 ||
- co->co_kwonlyargcount > 0 ||
- co->co_flags & (CO_VARARGS | CO_VARKEYWORDS)) {
+ if (total_args || co->co_flags & (CO_VARARGS | CO_VARKEYWORDS)) {
int i;
int n = argcount;
PyObject *kwdict = NULL;
@@ -2972,7 +3095,7 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
kwdict = PyDict_New();
if (kwdict == NULL)
goto fail;
- i = co->co_argcount + co->co_kwonlyargcount;
+ i = total_args;
if (co->co_flags & CO_VARARGS)
i++;
SETLOCAL(i, kwdict);
@@ -2981,13 +3104,12 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
if (!(co->co_flags & CO_VARARGS)) {
PyErr_Format(PyExc_TypeError,
"%U() takes %s %d "
- "%spositional argument%s (%d given)",
+ "positional argument%s (%d given)",
co->co_name,
defcount ? "at most" : "exactly",
co->co_argcount,
- kwcount ? "non-keyword " : "",
co->co_argcount == 1 ? "" : "s",
- argcount);
+ argcount + kwcount);
goto fail;
}
n = co->co_argcount;
@@ -3001,7 +3123,7 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
u = PyTuple_New(argcount - n);
if (u == NULL)
goto fail;
- SETLOCAL(co->co_argcount + co->co_kwonlyargcount, u);
+ SETLOCAL(total_args, u);
for (i = n; i < argcount; i++) {
x = args[i];
Py_INCREF(x);
@@ -3021,18 +3143,14 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
}
/* Speed hack: do raw pointer compares. As names are
normally interned this should almost always hit. */
- co_varnames = PySequence_Fast_ITEMS(co->co_varnames);
- for (j = 0;
- j < co->co_argcount + co->co_kwonlyargcount;
- j++) {
+ co_varnames = ((PyTupleObject *)(co->co_varnames))->ob_item;
+ for (j = 0; j < total_args; j++) {
PyObject *nm = co_varnames[j];
if (nm == keyword)
goto kw_found;
}
/* Slow fallback, just in case */
- for (j = 0;
- j < co->co_argcount + co->co_kwonlyargcount;
- j++) {
+ for (j = 0; j < total_args; j++) {
PyObject *nm = co_varnames[j];
int cmp = PyObject_RichCompareBool(
keyword, nm, Py_EQ);
@@ -3041,22 +3159,17 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
else if (cmp < 0)
goto fail;
}
- /* Check errors from Compare */
- if (PyErr_Occurred())
+ if (j >= total_args && kwdict == NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "%U() got an unexpected "
+ "keyword argument '%S'",
+ co->co_name,
+ keyword);
goto fail;
- if (j >= co->co_argcount + co->co_kwonlyargcount) {
- if (kwdict == NULL) {
- PyErr_Format(PyExc_TypeError,
- "%U() got an unexpected "
- "keyword argument '%S'",
- co->co_name,
- keyword);
- goto fail;
- }
- PyDict_SetItem(kwdict, keyword, value);
- continue;
}
-kw_found:
+ PyDict_SetItem(kwdict, keyword, value);
+ continue;
+ kw_found:
if (GETLOCAL(j) != NULL) {
PyErr_Format(PyExc_TypeError,
"%U() got multiple "
@@ -3070,20 +3183,18 @@ kw_found:
SETLOCAL(j, value);
}
if (co->co_kwonlyargcount > 0) {
- for (i = co->co_argcount;
- i < co->co_argcount + co->co_kwonlyargcount;
- i++) {
- PyObject *name, *def;
+ for (i = co->co_argcount; i < total_args; i++) {
+ PyObject *name;
if (GETLOCAL(i) != NULL)
continue;
name = PyTuple_GET_ITEM(co->co_varnames, i);
- def = NULL;
- if (kwdefs != NULL)
- def = PyDict_GetItem(kwdefs, name);
- if (def != NULL) {
- Py_INCREF(def);
- SETLOCAL(i, def);
- continue;
+ if (kwdefs != NULL) {
+ PyObject *def = PyDict_GetItem(kwdefs, name);
+ if (def) {
+ Py_INCREF(def);
+ SETLOCAL(i, def);
+ continue;
+ }
}
PyErr_Format(PyExc_TypeError,
"%U() needs keyword-only argument %S",
@@ -3095,16 +3206,19 @@ kw_found:
int m = co->co_argcount - defcount;
for (i = argcount; i < m; i++) {
if (GETLOCAL(i) == NULL) {
+ int j, given = 0;
+ for (j = 0; j < co->co_argcount; j++)
+ if (GETLOCAL(j))
+ given++;
PyErr_Format(PyExc_TypeError,
"%U() takes %s %d "
- "%spositional argument%s "
+ "argument%s "
"(%d given)",
co->co_name,
((co->co_flags & CO_VARARGS) ||
defcount) ? "at least"
: "exactly",
- m, kwcount ? "non-keyword " : "",
- m == 1 ? "" : "s", i);
+ m, m == 1 ? "" : "s", given);
goto fail;
}
}
@@ -3121,14 +3235,12 @@ kw_found:
}
}
}
- else {
- if (argcount > 0 || kwcount > 0) {
- PyErr_Format(PyExc_TypeError,
- "%U() takes no arguments (%d given)",
- co->co_name,
- argcount + kwcount);
- goto fail;
- }
+ else if (argcount > 0 || kwcount > 0) {
+ PyErr_Format(PyExc_TypeError,
+ "%U() takes no arguments (%d given)",
+ co->co_name,
+ argcount + kwcount);
+ goto fail;
}
/* Allocate and initialize storage for cell vars, and copy free
vars into frame. This isn't too efficient right now. */
@@ -3137,7 +3249,7 @@ kw_found:
Py_UNICODE *cellname, *argname;
PyObject *c;
- nargs = co->co_argcount + co->co_kwonlyargcount;
+ nargs = total_args;
if (co->co_flags & CO_VARARGS)
nargs++;
if (co->co_flags & CO_VARKEYWORDS)
@@ -3213,6 +3325,19 @@ fail: /* Jump here from prelude on failure */
}
+static PyObject *
+special_lookup(PyObject *o, char *meth, PyObject **cache)
+{
+ PyObject *res;
+ res = _PyObject_LookupSpecial(o, meth, cache);
+ if (res == NULL && !PyErr_Occurred()) {
+ PyErr_SetObject(PyExc_AttributeError, *cache);
+ return NULL;
+ }
+ return res;
+}
+
+
/* Logic for the raise statement (too complicated for inlining).
This *consumes* a reference count to each of its arguments. */
static enum why_code
@@ -3343,7 +3468,8 @@ unpack_iterable(PyObject *v, int argcnt, int argcntafter, PyObject **sp)
return 1;
}
Py_DECREF(w);
- PyErr_SetString(PyExc_ValueError, "too many values to unpack");
+ PyErr_Format(PyExc_ValueError, "too many values to unpack "
+ "(expected %d)", argcnt);
goto Error;
}
@@ -3471,33 +3597,30 @@ _PyEval_CallTracing(PyObject *func, PyObject *args)
return result;
}
+/* See Objects/lnotab_notes.txt for a description of how tracing works. */
static int
maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
PyFrameObject *frame, int *instr_lb, int *instr_ub,
int *instr_prev)
{
int result = 0;
+ int line = frame->f_lineno;
/* If the last instruction executed isn't in the current
- instruction window, reset the window. If the last
- instruction happens to fall at the start of a line or if it
- represents a jump backwards, call the trace function.
+ instruction window, reset the window.
*/
- if ((frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub)) {
- int line;
+ if (frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub) {
PyAddrPair bounds;
-
- line = PyCode_CheckLineNumber(frame->f_code, frame->f_lasti,
- &bounds);
- if (line >= 0) {
- frame->f_lineno = line;
- result = call_trace(func, obj, frame,
- PyTrace_LINE, Py_None);
- }
+ line = _PyCode_CheckLineNumber(frame->f_code, frame->f_lasti,
+ &bounds);
*instr_lb = bounds.ap_lower;
*instr_ub = bounds.ap_upper;
}
- else if (frame->f_lasti <= *instr_prev) {
+ /* If the last instruction falls at the start of a line or if
+ it represents a jump backwards, update the frame's line
+ number and call the trace function. */
+ if (frame->f_lasti == *instr_lb || frame->f_lasti < *instr_prev) {
+ frame->f_lineno = line;
result = call_trace(func, obj, frame, PyTrace_LINE, Py_None);
}
*instr_prev = frame->f_lasti;
@@ -3602,18 +3725,7 @@ PyEval_MergeCompilerFlags(PyCompilerFlags *cf)
/* External interface to call any callable object.
- The arg must be a tuple or NULL. */
-
-#undef PyEval_CallObject
-/* for backward compatibility: export this interface */
-
-PyObject *
-PyEval_CallObject(PyObject *func, PyObject *arg)
-{
- return PyEval_CallObjectWithKeywords(func, arg, (PyObject *)NULL);
-}
-#define PyEval_CallObject(func,arg) \
- PyEval_CallObjectWithKeywords(func, arg, (PyObject *)NULL)
+ The arg must be a tuple or NULL. The kw must be a dict or NULL. */
PyObject *
PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw)
@@ -3858,7 +3970,7 @@ fast_function(PyObject *func, PyObject ***pp_stack, int n, int na, int nk)
d = &PyTuple_GET_ITEM(argdefs, 0);
nd = Py_SIZE(argdefs);
}
- return PyEval_EvalCodeEx(co, globals,
+ return PyEval_EvalCodeEx((PyObject*)co, globals,
(PyObject *)NULL, (*pp_stack)-n, na,
(*pp_stack)-2*nk, nk, d, nd, kwdefs,
PyFunction_GET_CLOSURE(func));
@@ -4255,6 +4367,28 @@ format_exc_check_arg(PyObject *exc, const char *format_str, PyObject *obj)
PyErr_Format(exc, format_str, obj_str);
}
+static void
+format_exc_unbound(PyCodeObject *co, int oparg)
+{
+ PyObject *name;
+ /* Don't stomp existing exception */
+ if (PyErr_Occurred())
+ return;
+ if (oparg < PyTuple_GET_SIZE(co->co_cellvars)) {
+ name = PyTuple_GET_ITEM(co->co_cellvars,
+ oparg);
+ format_exc_check_arg(
+ PyExc_UnboundLocalError,
+ UNBOUNDLOCAL_ERROR_MSG,
+ name);
+ } else {
+ name = PyTuple_GET_ITEM(co->co_freevars, oparg -
+ PyTuple_GET_SIZE(co->co_cellvars));
+ format_exc_check_arg(PyExc_NameError,
+ UNBOUNDFREE_ERROR_MSG, name);
+ }
+}
+
static PyObject *
unicode_concatenate(PyObject *v, PyObject *w,
PyFrameObject *f, unsigned char *next_instr)
@@ -4270,7 +4404,7 @@ unicode_concatenate(PyObject *v, PyObject *w,
return NULL;
}
- if (v->ob_refcnt == 2) {
+ if (Py_REFCNT(v) == 2) {
/* In the common case, there are 2 references to the value
* stored in 'variable' when the += is performed: one on the
* value stack (in 'v') and one still stored in the
@@ -4311,7 +4445,7 @@ unicode_concatenate(PyObject *v, PyObject *w,
}
}
- if (v->ob_refcnt == 1 && !PyUnicode_CHECK_INTERNED(v)) {
+ if (Py_REFCNT(v) == 1 && !PyUnicode_CHECK_INTERNED(v)) {
/* Now we own the last reference to 'v', so we can resize it
* in-place.
*/