summaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/src/Python/ceval.c
diff options
context:
space:
mode:
authorshadchin <[email protected]>2022-04-18 12:39:32 +0300
committershadchin <[email protected]>2022-04-18 12:39:32 +0300
commitd4be68e361f4258cf0848fc70018dfe37a2acc24 (patch)
tree153e294cd97ac8b5d7a989612704a0c1f58e8ad4 /contrib/tools/python3/src/Python/ceval.c
parent260c02f5ccf242d9d9b8a873afaf6588c00237d6 (diff)
IGNIETFERRO-1816 Update Python 3 from 3.9.12 to 3.10.4
ref:9f96be6d02ee8044fdd6f124b799b270c20ce641
Diffstat (limited to 'contrib/tools/python3/src/Python/ceval.c')
-rw-r--r--contrib/tools/python3/src/Python/ceval.c1876
1 files changed, 1341 insertions, 535 deletions
diff --git a/contrib/tools/python3/src/Python/ceval.c b/contrib/tools/python3/src/Python/ceval.c
index 9a61f8a3c3c..21674e0be13 100644
--- a/contrib/tools/python3/src/Python/ceval.c
+++ b/contrib/tools/python3/src/Python/ceval.c
@@ -1,4 +1,3 @@
-
/* Execute compiled code */
/* XXX TO DO:
@@ -7,21 +6,22 @@
*/
/* enable more aggressive intra-module optimizations, where available */
+/* affects both release and debug builds - see bpo-43271 */
#define PY_LOCAL_AGGRESSIVE
#include "Python.h"
#include "pycore_abstract.h" // _PyIndex_Check()
-#include "pycore_call.h"
-#include "pycore_ceval.h"
-#include "pycore_code.h"
-#include "pycore_initconfig.h"
-#include "pycore_object.h"
-#include "pycore_pyerrors.h"
-#include "pycore_pylifecycle.h"
+#include "pycore_call.h" // _PyObject_FastCallDictTstate()
+#include "pycore_ceval.h" // _PyEval_SignalAsyncExc()
+#include "pycore_code.h" // _PyCode_InitOpcache()
+#include "pycore_initconfig.h" // _PyStatus_OK()
+#include "pycore_object.h" // _PyObject_GC_TRACK()
+#include "pycore_pyerrors.h" // _PyErr_Fetch()
+#include "pycore_pylifecycle.h" // _PyErr_Print()
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
#include "pycore_pystate.h" // _PyInterpreterState_GET()
-#include "pycore_sysmodule.h"
-#include "pycore_tupleobject.h"
+#include "pycore_sysmodule.h" // _PySys_Audit()
+#include "pycore_tuple.h" // _PyTuple_ITEMS()
#include "code.h"
#include "dictobject.h"
@@ -29,9 +29,17 @@
#include "opcode.h"
#include "pydtrace.h"
#include "setobject.h"
+#include "structmember.h" // struct PyMemberDef, T_OFFSET_EX
#include <ctype.h>
+typedef struct {
+ PyCodeObject *code; // The code object for the bounds. May be NULL.
+ PyCodeAddressRange bounds; // Only valid if code != NULL.
+ CFrame cframe;
+} PyTraceInfo;
+
+
#ifdef Py_DEBUG
/* For debugging the interpreter: */
#define LLTRACE 1 /* Low-level trace feature */
@@ -46,10 +54,10 @@ _Py_IDENTIFIER(__name__);
/* Forward declarations */
Py_LOCAL_INLINE(PyObject *) call_function(
- PyThreadState *tstate, PyObject ***pp_stack,
+ PyThreadState *tstate, PyTraceInfo *, PyObject ***pp_stack,
Py_ssize_t oparg, PyObject *kwnames);
static PyObject * do_call_core(
- PyThreadState *tstate, PyObject *func,
+ PyThreadState *tstate, PyTraceInfo *, PyObject *func,
PyObject *callargs, PyObject *kwdict);
#ifdef LLTRACE
@@ -58,16 +66,19 @@ static int prtrace(PyThreadState *, PyObject *, const char *);
#endif
static int call_trace(Py_tracefunc, PyObject *,
PyThreadState *, PyFrameObject *,
+ PyTraceInfo *,
int, PyObject *);
static int call_trace_protected(Py_tracefunc, PyObject *,
PyThreadState *, PyFrameObject *,
+ PyTraceInfo *,
int, PyObject *);
static void call_exc_trace(Py_tracefunc, PyObject *,
- PyThreadState *, PyFrameObject *);
+ PyThreadState *, PyFrameObject *,
+ PyTraceInfo *trace_info);
static int maybe_call_line_trace(Py_tracefunc, PyObject *,
PyThreadState *, PyFrameObject *,
- int *, int *, int *);
-static void maybe_dtrace_line(PyFrameObject *, int *, int *, int *);
+ PyTraceInfo *, int);
+static void maybe_dtrace_line(PyFrameObject *, PyTraceInfo *, int);
static void dtrace_function_entry(PyFrameObject *);
static void dtrace_function_return(PyFrameObject *);
@@ -103,16 +114,19 @@ static long dxp[256];
#endif
/* per opcode cache */
-#ifdef Py_DEBUG
-// --with-pydebug is used to find memory leak. opcache makes it harder.
-// So we disable opcache when Py_DEBUG is defined.
-// See bpo-37146
-#define OPCACHE_MIN_RUNS 0 /* disable opcache */
-#else
-#define OPCACHE_MIN_RUNS 1024 /* create opcache when code executed this time */
-#endif
+static int opcache_min_runs = 1024; /* create opcache when code executed this many times */
+#define OPCODE_CACHE_MAX_TRIES 20
#define OPCACHE_STATS 0 /* Enable stats */
+// This function allows to deactivate the opcode cache. As different cache mechanisms may hold
+// references, this can mess with the reference leak detector functionality so the cache needs
+// to be deactivated in such scenarios to avoid false positives. See bpo-3714 for more information.
+void
+_PyEval_DeactivateOpCache(void)
+{
+ opcache_min_runs = 0;
+}
+
#if OPCACHE_STATS
static size_t opcache_code_objects = 0;
static size_t opcache_code_objects_extra_mem = 0;
@@ -120,6 +134,12 @@ static size_t opcache_code_objects_extra_mem = 0;
static size_t opcache_global_opts = 0;
static size_t opcache_global_hits = 0;
static size_t opcache_global_misses = 0;
+
+static size_t opcache_attr_opts = 0;
+static size_t opcache_attr_hits = 0;
+static size_t opcache_attr_misses = 0;
+static size_t opcache_attr_deopts = 0;
+static size_t opcache_attr_total = 0;
#endif
@@ -254,8 +274,22 @@ _Py_FatalError_TstateNULL(const char *func)
"(the current Python thread state is NULL)");
}
+#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
+int
+_PyEval_ThreadsInitialized(PyInterpreterState *interp)
+{
+ return gil_created(&interp->ceval.gil);
+}
int
+PyEval_ThreadsInitialized(void)
+{
+ // Fatal error if there is no current interpreter
+ PyInterpreterState *interp = PyInterpreterState_Get();
+ return _PyEval_ThreadsInitialized(interp);
+}
+#else
+int
_PyEval_ThreadsInitialized(_PyRuntimeState *runtime)
{
return gil_created(&runtime->ceval.gil);
@@ -267,18 +301,25 @@ PyEval_ThreadsInitialized(void)
_PyRuntimeState *runtime = &_PyRuntime;
return _PyEval_ThreadsInitialized(runtime);
}
+#endif
PyStatus
_PyEval_InitGIL(PyThreadState *tstate)
{
- if (!_Py_IsMainInterpreter(tstate)) {
+#ifndef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
+ if (!_Py_IsMainInterpreter(tstate->interp)) {
/* Currently, the GIL is shared by all interpreters,
and only the main interpreter is responsible to create
and destroy it. */
return _PyStatus_OK();
}
+#endif
+#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
+ struct _gil_runtime_state *gil = &tstate->interp->ceval.gil;
+#else
struct _gil_runtime_state *gil = &tstate->interp->runtime->ceval.gil;
+#endif
assert(!gil_created(gil));
PyThread_init_thread();
@@ -291,16 +332,22 @@ _PyEval_InitGIL(PyThreadState *tstate)
}
void
-_PyEval_FiniGIL(PyThreadState *tstate)
+_PyEval_FiniGIL(PyInterpreterState *interp)
{
- if (!_Py_IsMainInterpreter(tstate)) {
+#ifndef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
+ if (!_Py_IsMainInterpreter(interp)) {
/* Currently, the GIL is shared by all interpreters,
and only the main interpreter is responsible to create
and destroy it. */
return;
}
+#endif
- struct _gil_runtime_state *gil = &tstate->interp->runtime->ceval.gil;
+#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
+ struct _gil_runtime_state *gil = &interp->ceval.gil;
+#else
+ struct _gil_runtime_state *gil = &interp->runtime->ceval.gil;
+#endif
if (!gil_created(gil)) {
/* First Py_InitializeFromConfig() call: the GIL doesn't exist
yet: do nothing. */
@@ -343,6 +390,25 @@ _PyEval_Fini(void)
opcache_global_opts);
fprintf(stderr, "\n");
+
+ fprintf(stderr, "-- Opcode cache LOAD_ATTR hits = %zd (%d%%)\n",
+ opcache_attr_hits,
+ (int) (100.0 * opcache_attr_hits /
+ opcache_attr_total));
+
+ fprintf(stderr, "-- Opcode cache LOAD_ATTR misses = %zd (%d%%)\n",
+ opcache_attr_misses,
+ (int) (100.0 * opcache_attr_misses /
+ opcache_attr_total));
+
+ fprintf(stderr, "-- Opcode cache LOAD_ATTR opts = %zd\n",
+ opcache_attr_opts);
+
+ fprintf(stderr, "-- Opcode cache LOAD_ATTR deopts = %zd\n",
+ opcache_attr_deopts);
+
+ fprintf(stderr, "-- Opcode cache LOAD_ATTR total = %zd\n",
+ opcache_attr_total);
#endif
}
@@ -385,9 +451,13 @@ PyEval_AcquireThread(PyThreadState *tstate)
take_gil(tstate);
struct _gilstate_runtime_state *gilstate = &tstate->interp->runtime->gilstate;
+#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
+ (void)_PyThreadState_Swap(gilstate, tstate);
+#else
if (_PyThreadState_Swap(gilstate, tstate) != NULL) {
Py_FatalError("non-NULL old thread state");
}
+#endif
}
void
@@ -407,19 +477,20 @@ PyEval_ReleaseThread(PyThreadState *tstate)
#ifdef HAVE_FORK
/* This function is called from PyOS_AfterFork_Child to destroy all threads
- * which are not running in the child process, and clear internal locks
- * which might be held by those threads.
- */
-
-void
-_PyEval_ReInitThreads(_PyRuntimeState *runtime)
+ which are not running in the child process, and clear internal locks
+ which might be held by those threads. */
+PyStatus
+_PyEval_ReInitThreads(PyThreadState *tstate)
{
- PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
- _Py_EnsureTstateNotNULL(tstate);
+ _PyRuntimeState *runtime = tstate->interp->runtime;
+#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
+ struct _gil_runtime_state *gil = &tstate->interp->ceval.gil;
+#else
struct _gil_runtime_state *gil = &runtime->ceval.gil;
+#endif
if (!gil_created(gil)) {
- return;
+ return _PyStatus_OK();
}
recreate_gil(gil);
@@ -427,11 +498,12 @@ _PyEval_ReInitThreads(_PyRuntimeState *runtime)
struct _pending_calls *pending = &tstate->interp->ceval.pending;
if (_PyThread_at_fork_reinit(&pending->lock) < 0) {
- Py_FatalError("Can't initialize threads for pending calls");
+ return _PyStatus_ERR("Can't reinitialize pending calls lock");
}
/* Destroy all threads except the current one */
_PyThreadState_DeleteExcept(runtime, tstate);
+ return _PyStatus_OK();
}
#endif
@@ -439,22 +511,30 @@ _PyEval_ReInitThreads(_PyRuntimeState *runtime)
raised. */
void
-_PyEval_SignalAsyncExc(PyThreadState *tstate)
+_PyEval_SignalAsyncExc(PyInterpreterState *interp)
{
- assert(is_tstate_valid(tstate));
- SIGNAL_ASYNC_EXC(tstate->interp);
+ SIGNAL_ASYNC_EXC(interp);
}
PyThreadState *
PyEval_SaveThread(void)
{
_PyRuntimeState *runtime = &_PyRuntime;
+#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
+ PyThreadState *old_tstate = _PyThreadState_GET();
+ PyThreadState *tstate = _PyThreadState_Swap(&runtime->gilstate, old_tstate);
+#else
PyThreadState *tstate = _PyThreadState_Swap(&runtime->gilstate, NULL);
+#endif
_Py_EnsureTstateNotNULL(tstate);
struct _ceval_runtime_state *ceval = &runtime->ceval;
struct _ceval_state *ceval2 = &tstate->interp->ceval;
+#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
+ assert(gil_created(&ceval2->gil));
+#else
assert(gil_created(&ceval->gil));
+#endif
drop_gil(ceval, ceval2, tstate);
return tstate;
}
@@ -618,10 +698,8 @@ handle_signals(PyThreadState *tstate)
}
static int
-make_pending_calls(PyThreadState *tstate)
+make_pending_calls(PyInterpreterState *interp)
{
- assert(is_tstate_valid(tstate));
-
/* only execute pending calls on main thread */
if (!_Py_ThreadCanHandlePendingCalls()) {
return 0;
@@ -636,11 +714,11 @@ make_pending_calls(PyThreadState *tstate)
/* unsignal before starting to call callbacks, so that any callback
added in-between re-signals */
- UNSIGNAL_PENDING_CALLS(tstate->interp);
+ UNSIGNAL_PENDING_CALLS(interp);
int res = 0;
/* perform a bounded number of calls, in case of recursion */
- struct _pending_calls *pending = &tstate->interp->ceval.pending;
+ struct _pending_calls *pending = &interp->ceval.pending;
for (int i=0; i<NPENDINGCALLS; i++) {
int (*func)(void *) = NULL;
void *arg = NULL;
@@ -665,7 +743,7 @@ make_pending_calls(PyThreadState *tstate)
error:
busy = 0;
- SIGNAL_PENDING_CALLS(tstate->interp);
+ SIGNAL_PENDING_CALLS(interp);
return res;
}
@@ -673,6 +751,7 @@ void
_Py_FinishPendingCalls(PyThreadState *tstate)
{
assert(PyGILState_Check());
+ assert(is_tstate_valid(tstate));
struct _pending_calls *pending = &tstate->interp->ceval.pending;
@@ -680,7 +759,7 @@ _Py_FinishPendingCalls(PyThreadState *tstate)
return;
}
- if (make_pending_calls(tstate) < 0) {
+ if (make_pending_calls(tstate->interp) < 0) {
PyObject *exc, *val, *tb;
_PyErr_Fetch(tstate, &exc, &val, &tb);
PyErr_BadInternalCall();
@@ -697,6 +776,7 @@ Py_MakePendingCalls(void)
assert(PyGILState_Check());
PyThreadState *tstate = _PyThreadState_GET();
+ assert(is_tstate_valid(tstate));
/* Python signal handler doesn't really queue a callback: it only signals
that a signal was received, see _PyEval_SignalReceived(). */
@@ -705,7 +785,7 @@ Py_MakePendingCalls(void)
return res;
}
- res = make_pending_calls(tstate);
+ res = make_pending_calls(tstate->interp);
if (res != 0) {
return res;
}
@@ -716,16 +796,15 @@ Py_MakePendingCalls(void)
/* The interpreter's recursion limit */
#ifndef Py_DEFAULT_RECURSION_LIMIT
-#define Py_DEFAULT_RECURSION_LIMIT 1000
+# define Py_DEFAULT_RECURSION_LIMIT 1000
#endif
-int _Py_CheckRecursionLimit = Py_DEFAULT_RECURSION_LIMIT;
-
void
_PyEval_InitRuntimeState(struct _ceval_runtime_state *ceval)
{
- _Py_CheckRecursionLimit = Py_DEFAULT_RECURSION_LIMIT;
+#ifndef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
_gil_initialize(&ceval->gil);
+#endif
}
int
@@ -741,6 +820,10 @@ _PyEval_InitState(struct _ceval_state *ceval)
return -1;
}
+#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
+ _gil_initialize(&ceval->gil);
+#endif
+
return 0;
}
@@ -757,8 +840,8 @@ _PyEval_FiniState(struct _ceval_state *ceval)
int
Py_GetRecursionLimit(void)
{
- PyThreadState *tstate = _PyThreadState_GET();
- return tstate->interp->ceval.recursion_limit;
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ return interp->ceval.recursion_limit;
}
void
@@ -766,14 +849,11 @@ Py_SetRecursionLimit(int new_limit)
{
PyThreadState *tstate = _PyThreadState_GET();
tstate->interp->ceval.recursion_limit = new_limit;
- if (_Py_IsMainInterpreter(tstate)) {
- _Py_CheckRecursionLimit = new_limit;
- }
}
/* The function _Py_EnterRecursiveCall() only calls _Py_CheckRecursiveCall()
- if the recursion_depth reaches _Py_CheckRecursionLimit.
- If USE_STACKCHECK, the macro decrements _Py_CheckRecursionLimit
+ if the recursion_depth reaches recursion_limit.
+ If USE_STACKCHECK, the macro decrements recursion_limit
to guarantee that _Py_CheckRecursiveCall() is regularly called.
Without USE_STACKCHECK, there is no need for this. */
int
@@ -788,27 +868,20 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
_PyErr_SetString(tstate, PyExc_MemoryError, "Stack overflow");
return -1;
}
- if (_Py_IsMainInterpreter(tstate)) {
- /* Needed for ABI backwards-compatibility (see bpo-31857) */
- _Py_CheckRecursionLimit = recursion_limit;
- }
#endif
- if (tstate->recursion_critical)
- /* Somebody asked that we don't check for recursion. */
- return 0;
- if (tstate->overflowed) {
- if (tstate->recursion_depth > recursion_limit + 50 || tstate->overflowed > 50) {
+ if (tstate->recursion_headroom) {
+ if (tstate->recursion_depth > recursion_limit + 50) {
/* Overflowing while handling an overflow. Give up. */
Py_FatalError("Cannot recover from stack overflow.");
}
}
else {
if (tstate->recursion_depth > recursion_limit) {
- tstate->overflowed++;
+ tstate->recursion_headroom++;
_PyErr_Format(tstate, PyExc_RecursionError,
"maximum recursion depth exceeded%s",
where);
- tstate->overflowed--;
+ tstate->recursion_headroom--;
--tstate->recursion_depth;
return -1;
}
@@ -816,21 +889,249 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
return 0;
}
+
+// PEP 634: Structural Pattern Matching
+
+
+// Return a tuple of values corresponding to keys, with error checks for
+// duplicate/missing keys.
+static PyObject*
+match_keys(PyThreadState *tstate, PyObject *map, PyObject *keys)
+{
+ assert(PyTuple_CheckExact(keys));
+ Py_ssize_t nkeys = PyTuple_GET_SIZE(keys);
+ if (!nkeys) {
+ // No keys means no items.
+ return PyTuple_New(0);
+ }
+ PyObject *seen = NULL;
+ PyObject *dummy = NULL;
+ PyObject *values = NULL;
+ // We use the two argument form of map.get(key, default) for two reasons:
+ // - Atomically check for a key and get its value without error handling.
+ // - Don't cause key creation or resizing in dict subclasses like
+ // collections.defaultdict that define __missing__ (or similar).
+ _Py_IDENTIFIER(get);
+ PyObject *get = _PyObject_GetAttrId(map, &PyId_get);
+ if (get == NULL) {
+ goto fail;
+ }
+ seen = PySet_New(NULL);
+ if (seen == NULL) {
+ goto fail;
+ }
+ // dummy = object()
+ dummy = _PyObject_CallNoArg((PyObject *)&PyBaseObject_Type);
+ if (dummy == NULL) {
+ goto fail;
+ }
+ values = PyList_New(0);
+ if (values == NULL) {
+ goto fail;
+ }
+ for (Py_ssize_t i = 0; i < nkeys; i++) {
+ PyObject *key = PyTuple_GET_ITEM(keys, i);
+ if (PySet_Contains(seen, key) || PySet_Add(seen, key)) {
+ if (!_PyErr_Occurred(tstate)) {
+ // Seen it before!
+ _PyErr_Format(tstate, PyExc_ValueError,
+ "mapping pattern checks duplicate key (%R)", key);
+ }
+ goto fail;
+ }
+ PyObject *value = PyObject_CallFunctionObjArgs(get, key, dummy, NULL);
+ if (value == NULL) {
+ goto fail;
+ }
+ if (value == dummy) {
+ // key not in map!
+ Py_DECREF(value);
+ Py_DECREF(values);
+ // Return None:
+ Py_INCREF(Py_None);
+ values = Py_None;
+ goto done;
+ }
+ PyList_Append(values, value);
+ Py_DECREF(value);
+ }
+ Py_SETREF(values, PyList_AsTuple(values));
+ // Success:
+done:
+ Py_DECREF(get);
+ Py_DECREF(seen);
+ Py_DECREF(dummy);
+ return values;
+fail:
+ Py_XDECREF(get);
+ Py_XDECREF(seen);
+ Py_XDECREF(dummy);
+ Py_XDECREF(values);
+ return NULL;
+}
+
+// Extract a named attribute from the subject, with additional bookkeeping to
+// raise TypeErrors for repeated lookups. On failure, return NULL (with no
+// error set). Use _PyErr_Occurred(tstate) to disambiguate.
+static PyObject*
+match_class_attr(PyThreadState *tstate, PyObject *subject, PyObject *type,
+ PyObject *name, PyObject *seen)
+{
+ assert(PyUnicode_CheckExact(name));
+ assert(PySet_CheckExact(seen));
+ if (PySet_Contains(seen, name) || PySet_Add(seen, name)) {
+ if (!_PyErr_Occurred(tstate)) {
+ // Seen it before!
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "%s() got multiple sub-patterns for attribute %R",
+ ((PyTypeObject*)type)->tp_name, name);
+ }
+ return NULL;
+ }
+ PyObject *attr = PyObject_GetAttr(subject, name);
+ if (attr == NULL && _PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) {
+ _PyErr_Clear(tstate);
+ }
+ return attr;
+}
+
+// On success (match), return a tuple of extracted attributes. On failure (no
+// match), return NULL. Use _PyErr_Occurred(tstate) to disambiguate.
+static PyObject*
+match_class(PyThreadState *tstate, PyObject *subject, PyObject *type,
+ Py_ssize_t nargs, PyObject *kwargs)
+{
+ if (!PyType_Check(type)) {
+ const char *e = "called match pattern must be a type";
+ _PyErr_Format(tstate, PyExc_TypeError, e);
+ return NULL;
+ }
+ assert(PyTuple_CheckExact(kwargs));
+ // First, an isinstance check:
+ if (PyObject_IsInstance(subject, type) <= 0) {
+ return NULL;
+ }
+ // So far so good:
+ PyObject *seen = PySet_New(NULL);
+ if (seen == NULL) {
+ return NULL;
+ }
+ PyObject *attrs = PyList_New(0);
+ if (attrs == NULL) {
+ Py_DECREF(seen);
+ return NULL;
+ }
+ // NOTE: From this point on, goto fail on failure:
+ PyObject *match_args = NULL;
+ // First, the positional subpatterns:
+ if (nargs) {
+ int match_self = 0;
+ match_args = PyObject_GetAttrString(type, "__match_args__");
+ if (match_args) {
+ if (!PyTuple_CheckExact(match_args)) {
+ const char *e = "%s.__match_args__ must be a tuple (got %s)";
+ _PyErr_Format(tstate, PyExc_TypeError, e,
+ ((PyTypeObject *)type)->tp_name,
+ Py_TYPE(match_args)->tp_name);
+ goto fail;
+ }
+ }
+ else if (_PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) {
+ _PyErr_Clear(tstate);
+ // _Py_TPFLAGS_MATCH_SELF is only acknowledged if the type does not
+ // define __match_args__. This is natural behavior for subclasses:
+ // it's as if __match_args__ is some "magic" value that is lost as
+ // soon as they redefine it.
+ match_args = PyTuple_New(0);
+ match_self = PyType_HasFeature((PyTypeObject*)type,
+ _Py_TPFLAGS_MATCH_SELF);
+ }
+ else {
+ goto fail;
+ }
+ assert(PyTuple_CheckExact(match_args));
+ Py_ssize_t allowed = match_self ? 1 : PyTuple_GET_SIZE(match_args);
+ if (allowed < nargs) {
+ const char *plural = (allowed == 1) ? "" : "s";
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "%s() accepts %d positional sub-pattern%s (%d given)",
+ ((PyTypeObject*)type)->tp_name,
+ allowed, plural, nargs);
+ goto fail;
+ }
+ if (match_self) {
+ // Easy. Copy the subject itself, and move on to kwargs.
+ PyList_Append(attrs, subject);
+ }
+ else {
+ for (Py_ssize_t i = 0; i < nargs; i++) {
+ PyObject *name = PyTuple_GET_ITEM(match_args, i);
+ if (!PyUnicode_CheckExact(name)) {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "__match_args__ elements must be strings "
+ "(got %s)", Py_TYPE(name)->tp_name);
+ goto fail;
+ }
+ PyObject *attr = match_class_attr(tstate, subject, type, name,
+ seen);
+ if (attr == NULL) {
+ goto fail;
+ }
+ PyList_Append(attrs, attr);
+ Py_DECREF(attr);
+ }
+ }
+ Py_CLEAR(match_args);
+ }
+ // Finally, the keyword subpatterns:
+ for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(kwargs); i++) {
+ PyObject *name = PyTuple_GET_ITEM(kwargs, i);
+ PyObject *attr = match_class_attr(tstate, subject, type, name, seen);
+ if (attr == NULL) {
+ goto fail;
+ }
+ PyList_Append(attrs, attr);
+ Py_DECREF(attr);
+ }
+ Py_SETREF(attrs, PyList_AsTuple(attrs));
+ Py_DECREF(seen);
+ return attrs;
+fail:
+ // We really don't care whether an error was raised or not... that's our
+ // caller's problem. All we know is that the match failed.
+ Py_XDECREF(match_args);
+ Py_DECREF(seen);
+ Py_DECREF(attrs);
+ return NULL;
+}
+
+
static int do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause);
static int unpack_iterable(PyThreadState *, PyObject *, int, int, PyObject **);
-#define _Py_TracingPossible(ceval) ((ceval)->tracing_possible)
-
PyObject *
PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals)
{
- return PyEval_EvalCodeEx(co,
- globals, locals,
- (PyObject **)NULL, 0,
- (PyObject **)NULL, 0,
- (PyObject **)NULL, 0,
- NULL, NULL);
+ PyThreadState *tstate = PyThreadState_GET();
+ if (locals == NULL) {
+ locals = globals;
+ }
+ PyObject *builtins = _PyEval_BuiltinsFromGlobals(tstate, globals); // borrowed ref
+ if (builtins == NULL) {
+ return NULL;
+ }
+ PyFrameConstructor desc = {
+ .fc_globals = globals,
+ .fc_builtins = builtins,
+ .fc_name = ((PyCodeObject *)co)->co_name,
+ .fc_qualname = ((PyCodeObject *)co)->co_name,
+ .fc_code = co,
+ .fc_defaults = NULL,
+ .fc_kwdefaults = NULL,
+ .fc_closure = NULL
+ };
+ return _PyEval_Vector(tstate, &desc, locals, NULL, 0, NULL);
}
@@ -870,7 +1171,7 @@ eval_frame_handle_pending(PyThreadState *tstate)
/* Pending calls */
struct _ceval_state *ceval2 = &tstate->interp->ceval;
if (_Py_atomic_load_relaxed(&ceval2->pending.calls_to_do)) {
- if (make_pending_calls(tstate) != 0) {
+ if (make_pending_calls(tstate->interp) != 0) {
return -1;
}
}
@@ -887,9 +1188,13 @@ eval_frame_handle_pending(PyThreadState *tstate)
take_gil(tstate);
+#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
+ (void)_PyThreadState_Swap(&runtime->gilstate, tstate);
+#else
if (_PyThreadState_Swap(&runtime->gilstate, tstate) != NULL) {
Py_FatalError("orphan tstate");
}
+#endif
}
/* Check for asynchronous exception. */
@@ -916,41 +1221,6 @@ eval_frame_handle_pending(PyThreadState *tstate)
return 0;
}
-PyObject* _Py_HOT_FUNCTION
-_PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
-{
- _Py_EnsureTstateNotNULL(tstate);
-
-#ifdef DXPAIRS
- int lastopcode = 0;
-#endif
- PyObject **stack_pointer; /* Next free slot in value stack */
- const _Py_CODEUNIT *next_instr;
- int opcode; /* Current opcode */
- int oparg; /* Current opcode argument, if any */
- PyObject **fastlocals, **freevars;
- PyObject *retval = NULL; /* Return value */
- struct _ceval_state * const ceval2 = &tstate->interp->ceval;
- _Py_atomic_int * const eval_breaker = &ceval2->eval_breaker;
- PyCodeObject *co;
-
- /* when tracing we set things up so that
-
- not (instr_lb <= current_bytecode_offset < instr_ub)
-
- is true when the line being executed has changed. The
- initial values are such as to make this false the first
- time it is tested. */
- int instr_ub = -1, instr_lb = 0, instr_prev = -1;
-
- const _Py_CODEUNIT *first_instr;
- PyObject *names;
- PyObject *consts;
- _PyOpcache *co_opcache;
-
-#ifdef LLTRACE
- _Py_IDENTIFIER(__ltrace__);
-#endif
/* Computed GOTOs, or
the-optimization-commonly-but-improperly-known-as-"threaded code"
@@ -991,6 +1261,23 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
-fno-crossjumping).
*/
+/* Use macros rather than inline functions, to make it as clear as possible
+ * to the C compiler that the tracing check is a simple test then branch.
+ * We want to be sure that the compiler knows this before it generates
+ * the CFG.
+ */
+#ifdef LLTRACE
+#define OR_LLTRACE || lltrace
+#else
+#define OR_LLTRACE
+#endif
+
+#ifdef WITH_DTRACE
+#define OR_DTRACE_LINE || PyDTrace_LINE_ENABLED()
+#else
+#define OR_DTRACE_LINE
+#endif
+
#ifdef DYNAMIC_EXECUTION_PROFILE
#undef USE_COMPUTED_GOTOS
#define USE_COMPUTED_GOTOS 0
@@ -1009,49 +1296,27 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
#endif
#if USE_COMPUTED_GOTOS
-/* Import the static jump table */
-#include "opcode_targets.h"
-
-#define TARGET(op) \
- op: \
- TARGET_##op
-
-#ifdef LLTRACE
-#define FAST_DISPATCH() \
+#define TARGET(op) op: TARGET_##op
+#define DISPATCH() \
{ \
- if (!lltrace && !_Py_TracingPossible(ceval2) && !PyDTrace_LINE_ENABLED()) { \
- f->f_lasti = INSTR_OFFSET(); \
- NEXTOPARG(); \
- goto *opcode_targets[opcode]; \
+ if (trace_info.cframe.use_tracing OR_DTRACE_LINE OR_LLTRACE) { \
+ goto tracing_dispatch; \
} \
- goto fast_next_opcode; \
+ f->f_lasti = INSTR_OFFSET(); \
+ NEXTOPARG(); \
+ goto *opcode_targets[opcode]; \
}
#else
-#define FAST_DISPATCH() \
- { \
- if (!_Py_TracingPossible(ceval2) && !PyDTrace_LINE_ENABLED()) { \
- f->f_lasti = INSTR_OFFSET(); \
- NEXTOPARG(); \
- goto *opcode_targets[opcode]; \
- } \
- goto fast_next_opcode; \
- }
+#define TARGET(op) op
+#define DISPATCH() goto predispatch;
#endif
-#define DISPATCH() \
- { \
- if (!_Py_atomic_load_relaxed(eval_breaker)) { \
- FAST_DISPATCH(); \
- } \
+
+#define CHECK_EVAL_BREAKER() \
+ if (_Py_atomic_load_relaxed(eval_breaker)) { \
continue; \
}
-#else
-#define TARGET(op) op
-#define FAST_DISPATCH() goto fast_next_opcode
-#define DISPATCH() continue
-#endif
-
/* Tuple access macros */
@@ -1064,16 +1329,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
/* Code access macros */
/* The integer overflow is checked by an assertion below. */
-#define INSTR_OFFSET() \
- (sizeof(_Py_CODEUNIT) * (int)(next_instr - first_instr))
+#define INSTR_OFFSET() ((int)(next_instr - first_instr))
#define NEXTOPARG() do { \
_Py_CODEUNIT word = *next_instr; \
opcode = _Py_OPCODE(word); \
oparg = _Py_OPARG(word); \
next_instr++; \
} while (0)
-#define JUMPTO(x) (next_instr = first_instr + (x) / sizeof(_Py_CODEUNIT))
-#define JUMPBY(x) (next_instr += (x) / sizeof(_Py_CODEUNIT))
+#define JUMPTO(x) (next_instr = first_instr + (x))
+#define JUMPBY(x) (next_instr += (x))
/* OpCode prediction macros
Some opcodes tend to come in pairs thus making it possible to
@@ -1135,7 +1399,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
#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)
@@ -1216,16 +1479,43 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
do { \
co_opcache = NULL; \
if (co->co_opcache != NULL) { \
- unsigned char co_opt_offset = \
+ unsigned char co_opcache_offset = \
co->co_opcache_map[next_instr - first_instr]; \
- if (co_opt_offset > 0) { \
- assert(co_opt_offset <= co->co_opcache_size); \
- co_opcache = &co->co_opcache[co_opt_offset - 1]; \
+ if (co_opcache_offset > 0) { \
+ assert(co_opcache_offset <= co->co_opcache_size); \
+ co_opcache = &co->co_opcache[co_opcache_offset - 1]; \
assert(co_opcache != NULL); \
} \
} \
} while (0)
+#define OPCACHE_DEOPT() \
+ do { \
+ if (co_opcache != NULL) { \
+ co_opcache->optimized = -1; \
+ unsigned char co_opcache_offset = \
+ co->co_opcache_map[next_instr - first_instr]; \
+ assert(co_opcache_offset <= co->co_opcache_size); \
+ co->co_opcache_map[co_opcache_offset] = 0; \
+ co_opcache = NULL; \
+ } \
+ } while (0)
+
+#define OPCACHE_DEOPT_LOAD_ATTR() \
+ do { \
+ if (co_opcache != NULL) { \
+ OPCACHE_STAT_ATTR_DEOPT(); \
+ OPCACHE_DEOPT(); \
+ } \
+ } while (0)
+
+#define OPCACHE_MAYBE_DEOPT_LOAD_ATTR() \
+ do { \
+ if (co_opcache != NULL && --co_opcache->optimized <= 0) { \
+ OPCACHE_DEOPT_LOAD_ATTR(); \
+ } \
+ } while (0)
+
#if OPCACHE_STATS
#define OPCACHE_STAT_GLOBAL_HIT() \
@@ -1243,24 +1533,99 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
if (co->co_opcache != NULL) opcache_global_opts++; \
} while (0)
+#define OPCACHE_STAT_ATTR_HIT() \
+ do { \
+ if (co->co_opcache != NULL) opcache_attr_hits++; \
+ } while (0)
+
+#define OPCACHE_STAT_ATTR_MISS() \
+ do { \
+ if (co->co_opcache != NULL) opcache_attr_misses++; \
+ } while (0)
+
+#define OPCACHE_STAT_ATTR_OPT() \
+ do { \
+ if (co->co_opcache!= NULL) opcache_attr_opts++; \
+ } while (0)
+
+#define OPCACHE_STAT_ATTR_DEOPT() \
+ do { \
+ if (co->co_opcache != NULL) opcache_attr_deopts++; \
+ } while (0)
+
+#define OPCACHE_STAT_ATTR_TOTAL() \
+ do { \
+ if (co->co_opcache != NULL) opcache_attr_total++; \
+ } while (0)
+
#else /* OPCACHE_STATS */
#define OPCACHE_STAT_GLOBAL_HIT()
#define OPCACHE_STAT_GLOBAL_MISS()
#define OPCACHE_STAT_GLOBAL_OPT()
+#define OPCACHE_STAT_ATTR_HIT()
+#define OPCACHE_STAT_ATTR_MISS()
+#define OPCACHE_STAT_ATTR_OPT()
+#define OPCACHE_STAT_ATTR_DEOPT()
+#define OPCACHE_STAT_ATTR_TOTAL()
+
#endif
-/* Start of code */
- /* push frame */
+PyObject* _Py_HOT_FUNCTION
+_PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
+{
+ _Py_EnsureTstateNotNULL(tstate);
+
+#if USE_COMPUTED_GOTOS
+/* Import the static jump table */
+#include "opcode_targets.h"
+#endif
+
+#ifdef DXPAIRS
+ int lastopcode = 0;
+#endif
+ PyObject **stack_pointer; /* Next free slot in value stack */
+ const _Py_CODEUNIT *next_instr;
+ int opcode; /* Current opcode */
+ int oparg; /* Current opcode argument, if any */
+ PyObject **fastlocals, **freevars;
+ PyObject *retval = NULL; /* Return value */
+ _Py_atomic_int * const eval_breaker = &tstate->interp->ceval.eval_breaker;
+ PyCodeObject *co;
+
+ const _Py_CODEUNIT *first_instr;
+ PyObject *names;
+ PyObject *consts;
+ _PyOpcache *co_opcache;
+
+#ifdef LLTRACE
+ _Py_IDENTIFIER(__ltrace__);
+#endif
+
if (_Py_EnterRecursiveCall(tstate, "")) {
return NULL;
}
+ PyTraceInfo trace_info;
+ /* Mark trace_info as uninitialized */
+ trace_info.code = NULL;
+
+ /* WARNING: Because the CFrame lives on the C stack,
+ * but can be accessed from a heap allocated object (tstate)
+ * strict stack discipline must be maintained.
+ */
+ CFrame *prev_cframe = tstate->cframe;
+ trace_info.cframe.use_tracing = prev_cframe->use_tracing;
+ trace_info.cframe.previous = prev_cframe;
+ tstate->cframe = &trace_info.cframe;
+
+ /* push frame */
tstate->frame = f;
+ co = f->f_code;
- if (tstate->use_tracing) {
+ if (trace_info.cframe.use_tracing) {
if (tstate->c_tracefunc != NULL) {
/* tstate->c_tracefunc, if defined, is a
function that will be called on *every* entry
@@ -1277,7 +1642,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
whenever an exception is detected. */
if (call_trace_protected(tstate->c_tracefunc,
tstate->c_traceobj,
- tstate, f, PyTrace_CALL, Py_None)) {
+ tstate, f, &trace_info,
+ PyTrace_CALL, Py_None)) {
/* Trace function raised an error */
goto exit_eval_frame;
}
@@ -1287,7 +1653,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
return itself and isn't called for "line" events */
if (call_trace_protected(tstate->c_profilefunc,
tstate->c_profileobj,
- tstate, f, PyTrace_CALL, Py_None)) {
+ tstate, f, &trace_info,
+ PyTrace_CALL, Py_None)) {
/* Profile function raised an error */
goto exit_eval_frame;
}
@@ -1297,7 +1664,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
if (PyDTrace_FUNCTION_ENTRY_ENABLED())
dtrace_function_entry(f);
- co = f->f_code;
names = co->co_names;
consts = co->co_consts;
fastlocals = f->f_localsplus;
@@ -1323,19 +1689,20 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
to the beginning of the combined pair.)
*/
assert(f->f_lasti >= -1);
- next_instr = first_instr;
- if (f->f_lasti >= 0) {
- assert(f->f_lasti % sizeof(_Py_CODEUNIT) == 0);
- next_instr += f->f_lasti / sizeof(_Py_CODEUNIT) + 1;
- }
- stack_pointer = f->f_stacktop;
- assert(stack_pointer != NULL);
- f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */
- f->f_executing = 1;
+ next_instr = first_instr + f->f_lasti + 1;
+ stack_pointer = f->f_valuestack + f->f_stackdepth;
+ /* Set f->f_stackdepth to -1.
+ * Update when returning or calling trace function.
+ Having f_stackdepth <= 0 ensures that invalid
+ values are not visible to the cycle GC.
+ We choose -1 rather than 0 to assist debugging.
+ */
+ f->f_stackdepth = -1;
+ f->f_state = FRAME_EXECUTING;
- if (co->co_opcache_flag < OPCACHE_MIN_RUNS) {
+ if (co->co_opcache_flag < opcache_min_runs) {
co->co_opcache_flag++;
- if (co->co_opcache_flag == OPCACHE_MIN_RUNS) {
+ if (co->co_opcache_flag == opcache_min_runs) {
if (_PyCode_InitOpcache(co) < 0) {
goto exit_eval_frame;
}
@@ -1349,11 +1716,18 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
}
#ifdef LLTRACE
- lltrace = _PyDict_GetItemId(f->f_globals, &PyId___ltrace__) != NULL;
+ {
+ int r = _PyDict_ContainsId(f->f_globals, &PyId___ltrace__);
+ if (r < 0) {
+ goto exit_eval_frame;
+ }
+ lltrace = r;
+ }
#endif
- if (throwflag) /* support for generator.throw() */
+ if (throwflag) { /* support for generator.throw() */
goto error;
+ }
#ifdef Py_DEBUG
/* _PyEval_EvalFrameDefault() must not be called with an exception set,
@@ -1378,10 +1752,10 @@ main_loop:
if (_Py_atomic_load_relaxed(eval_breaker)) {
opcode = _Py_OPCODE(*next_instr);
- if (opcode == SETUP_FINALLY ||
- opcode == SETUP_WITH ||
- opcode == BEFORE_ASYNC_WITH ||
- opcode == YIELD_FROM) {
+ if (opcode != SETUP_FINALLY &&
+ opcode != SETUP_WITH &&
+ opcode != BEFORE_ASYNC_WITH &&
+ opcode != YIELD_FROM) {
/* Few cases where we skip running signal handlers and other
pending calls:
- If we're about to enter the 'with:'. It will prevent
@@ -1398,55 +1772,45 @@ main_loop:
running the signal handler and raising KeyboardInterrupt
(see bpo-30039).
*/
- goto fast_next_opcode;
- }
-
- if (eval_frame_handle_pending(tstate) != 0) {
- goto error;
- }
+ if (eval_frame_handle_pending(tstate) != 0) {
+ goto error;
+ }
+ }
}
- fast_next_opcode:
+ tracing_dispatch:
+ {
+ int instr_prev = f->f_lasti;
f->f_lasti = INSTR_OFFSET();
+ NEXTOPARG();
if (PyDTrace_LINE_ENABLED())
- maybe_dtrace_line(f, &instr_lb, &instr_ub, &instr_prev);
+ maybe_dtrace_line(f, &trace_info, instr_prev);
/* line-by-line tracing support */
- if (_Py_TracingPossible(ceval2) &&
+ if (trace_info.cframe.use_tracing &&
tstate->c_tracefunc != NULL && !tstate->tracing) {
int err;
- /* see maybe_call_line_trace
+ /* see maybe_call_line_trace()
for expository comments */
- f->f_stacktop = stack_pointer;
+ f->f_stackdepth = (int)(stack_pointer - f->f_valuestack);
err = maybe_call_line_trace(tstate->c_tracefunc,
tstate->c_traceobj,
tstate, f,
- &instr_lb, &instr_ub, &instr_prev);
+ &trace_info, instr_prev);
/* Reload possibly changed frame fields */
JUMPTO(f->f_lasti);
- if (f->f_stacktop != NULL) {
- stack_pointer = f->f_stacktop;
- f->f_stacktop = NULL;
- }
- if (err)
+ stack_pointer = f->f_valuestack+f->f_stackdepth;
+ f->f_stackdepth = -1;
+ if (err) {
/* trace function raised an exception */
goto error;
+ }
+ NEXTOPARG();
}
-
- /* Extract opcode and argument */
-
- NEXTOPARG();
- dispatch_opcode:
-#ifdef DYNAMIC_EXECUTION_PROFILE
-#ifdef DXPAIRS
- dxpairs[lastopcode][opcode]++;
- lastopcode = opcode;
-#endif
- dxp[opcode]++;
-#endif
+ }
#ifdef LLTRACE
/* Instruction tracing */
@@ -1462,15 +1826,33 @@ main_loop:
}
}
#endif
+#if USE_COMPUTED_GOTOS == 0
+ goto dispatch_opcode;
+
+ predispatch:
+ if (trace_info.cframe.use_tracing OR_DTRACE_LINE OR_LLTRACE) {
+ goto tracing_dispatch;
+ }
+ f->f_lasti = INSTR_OFFSET();
+ NEXTOPARG();
+#endif
+ dispatch_opcode:
+#ifdef DYNAMIC_EXECUTION_PROFILE
+#ifdef DXPAIRS
+ dxpairs[lastopcode][opcode]++;
+ lastopcode = opcode;
+#endif
+ dxp[opcode]++;
+#endif
switch (opcode) {
/* BEWARE!
It is essential that any operation that fails must goto error
- and that all operation that succeed call [FAST_]DISPATCH() ! */
+ and that all operation that succeed call DISPATCH() ! */
case TARGET(NOP): {
- FAST_DISPATCH();
+ DISPATCH();
}
case TARGET(LOAD_FAST): {
@@ -1483,7 +1865,7 @@ main_loop:
}
Py_INCREF(value);
PUSH(value);
- FAST_DISPATCH();
+ DISPATCH();
}
case TARGET(LOAD_CONST): {
@@ -1491,20 +1873,20 @@ main_loop:
PyObject *value = GETITEM(consts, oparg);
Py_INCREF(value);
PUSH(value);
- FAST_DISPATCH();
+ DISPATCH();
}
case TARGET(STORE_FAST): {
PREDICTED(STORE_FAST);
PyObject *value = POP();
SETLOCAL(oparg, value);
- FAST_DISPATCH();
+ DISPATCH();
}
case TARGET(POP_TOP): {
PyObject *value = POP();
Py_DECREF(value);
- FAST_DISPATCH();
+ DISPATCH();
}
case TARGET(ROT_TWO): {
@@ -1512,7 +1894,7 @@ main_loop:
PyObject *second = SECOND();
SET_TOP(second);
SET_SECOND(top);
- FAST_DISPATCH();
+ DISPATCH();
}
case TARGET(ROT_THREE): {
@@ -1522,7 +1904,7 @@ main_loop:
SET_TOP(second);
SET_SECOND(third);
SET_THIRD(top);
- FAST_DISPATCH();
+ DISPATCH();
}
case TARGET(ROT_FOUR): {
@@ -1534,14 +1916,14 @@ main_loop:
SET_SECOND(third);
SET_THIRD(fourth);
SET_FOURTH(top);
- FAST_DISPATCH();
+ DISPATCH();
}
case TARGET(DUP_TOP): {
PyObject *top = TOP();
Py_INCREF(top);
PUSH(top);
- FAST_DISPATCH();
+ DISPATCH();
}
case TARGET(DUP_TOP_TWO): {
@@ -1552,7 +1934,7 @@ main_loop:
STACK_GROW(2);
SET_TOP(top);
SET_SECOND(second);
- FAST_DISPATCH();
+ DISPATCH();
}
case TARGET(UNARY_POSITIVE): {
@@ -1687,7 +2069,7 @@ main_loop:
PyObject *right = POP();
PyObject *left = TOP();
PyObject *sum;
- /* NOTE(haypo): Please don't try to micro-optimize int+int on
+ /* NOTE(vstinner): Please don't try to micro-optimize int+int on
CPython using bytecode, it is simply worthless.
See http://bugs.python.org/issue21955 and
http://bugs.python.org/issue10044 for the discussion. In short,
@@ -2055,6 +2437,8 @@ main_loop:
retval = POP();
assert(f->f_iblock == 0);
assert(EMPTY());
+ f->f_state = FRAME_RETURNED;
+ f->f_stackdepth = 0;
goto exiting;
}
@@ -2197,34 +2581,52 @@ main_loop:
case TARGET(YIELD_FROM): {
PyObject *v = POP();
PyObject *receiver = TOP();
- int err;
- if (PyGen_CheckExact(receiver) || PyCoro_CheckExact(receiver)) {
- retval = _PyGen_Send((PyGenObject *)receiver, v);
+ PySendResult gen_status;
+ if (tstate->c_tracefunc == NULL) {
+ gen_status = PyIter_Send(receiver, v, &retval);
} else {
_Py_IDENTIFIER(send);
- if (v == Py_None)
+ if (Py_IsNone(v) && PyIter_Check(receiver)) {
retval = Py_TYPE(receiver)->tp_iternext(receiver);
- else
+ }
+ else {
retval = _PyObject_CallMethodIdOneArg(receiver, &PyId_send, v);
+ }
+ if (retval == NULL) {
+ if (tstate->c_tracefunc != NULL
+ && _PyErr_ExceptionMatches(tstate, PyExc_StopIteration))
+ call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f, &trace_info);
+ if (_PyGen_FetchStopIterationValue(&retval) == 0) {
+ gen_status = PYGEN_RETURN;
+ }
+ else {
+ gen_status = PYGEN_ERROR;
+ }
+ }
+ else {
+ gen_status = PYGEN_NEXT;
+ }
}
Py_DECREF(v);
- if (retval == NULL) {
- PyObject *val;
- if (tstate->c_tracefunc != NULL
- && _PyErr_ExceptionMatches(tstate, PyExc_StopIteration))
- call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f);
- err = _PyGen_FetchStopIterationValue(&val);
- if (err < 0)
- goto error;
+ if (gen_status == PYGEN_ERROR) {
+ assert (retval == NULL);
+ goto error;
+ }
+ if (gen_status == PYGEN_RETURN) {
+ assert (retval != NULL);
+
Py_DECREF(receiver);
- SET_TOP(val);
+ SET_TOP(retval);
+ retval = NULL;
DISPATCH();
}
+ assert (gen_status == PYGEN_NEXT);
/* receiver remains on stack, retval is value to be yielded */
- f->f_stacktop = stack_pointer;
/* and repeat... */
- assert(f->f_lasti >= (int)sizeof(_Py_CODEUNIT));
- f->f_lasti -= sizeof(_Py_CODEUNIT);
+ assert(f->f_lasti > 0);
+ f->f_lasti -= 1;
+ f->f_state = FRAME_SUSPENDED;
+ f->f_stackdepth = (int)(stack_pointer - f->f_valuestack);
goto exiting;
}
@@ -2240,11 +2642,19 @@ main_loop:
}
retval = w;
}
-
- f->f_stacktop = stack_pointer;
+ f->f_state = FRAME_SUSPENDED;
+ f->f_stackdepth = (int)(stack_pointer - f->f_valuestack);
goto exiting;
}
+ case TARGET(GEN_START): {
+ PyObject *none = POP();
+ assert(none == Py_None);
+ assert(oparg < 3);
+ Py_DECREF(none);
+ DISPATCH();
+ }
+
case TARGET(POP_EXCEPT): {
PyObject *type, *value, *traceback;
_PyErr_StackItem *exc_info;
@@ -2270,12 +2680,15 @@ main_loop:
}
case TARGET(POP_BLOCK): {
- PREDICTED(POP_BLOCK);
PyFrame_BlockPop(f);
DISPATCH();
}
case TARGET(RERAISE): {
+ assert(f->f_iblock > 0);
+ if (oparg) {
+ f->f_lasti = f->f_blockstack[f->f_iblock-1].b_handler;
+ }
PyObject *exc = POP();
PyObject *val = POP();
PyObject *tb = POP();
@@ -2294,7 +2707,7 @@ main_loop:
UNWIND_EXCEPT_HANDLER(b);
Py_DECREF(POP());
JUMPBY(oparg);
- FAST_DISPATCH();
+ DISPATCH();
}
else {
PyObject *val = POP();
@@ -2308,7 +2721,7 @@ main_loop:
PyObject *value = PyExc_AssertionError;
Py_INCREF(value);
PUSH(value);
- FAST_DISPATCH();
+ DISPATCH();
}
case TARGET(LOAD_BUILD_CLASS): {
@@ -2571,7 +2984,7 @@ main_loop:
(PyDictObject *)f->f_builtins,
name);
if (v == NULL) {
- if (!_PyErr_OCCURRED()) {
+ if (!_PyErr_Occurred(tstate)) {
/* _PyDict_LoadGlobal() returns NULL without raising
* an exception if the key doesn't exist */
format_exc_check_arg(tstate, PyExc_NameError,
@@ -2993,7 +3406,190 @@ main_loop:
case TARGET(LOAD_ATTR): {
PyObject *name = GETITEM(names, oparg);
PyObject *owner = TOP();
- PyObject *res = PyObject_GetAttr(owner, name);
+
+ PyTypeObject *type = Py_TYPE(owner);
+ PyObject *res;
+ PyObject **dictptr;
+ PyObject *dict;
+ _PyOpCodeOpt_LoadAttr *la;
+
+ OPCACHE_STAT_ATTR_TOTAL();
+
+ OPCACHE_CHECK();
+ if (co_opcache != NULL && PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
+ {
+ if (co_opcache->optimized > 0) {
+ // Fast path -- cache hit makes LOAD_ATTR ~30% faster.
+ la = &co_opcache->u.la;
+ if (la->type == type && la->tp_version_tag == type->tp_version_tag)
+ {
+ // Hint >= 0 is a dict index; hint == -1 is a dict miss.
+ // Hint < -1 is an inverted slot offset: offset is strictly > 0,
+ // so ~offset is strictly < -1 (assuming 2's complement).
+ if (la->hint < -1) {
+ // Even faster path -- slot hint.
+ Py_ssize_t offset = ~la->hint;
+ // fprintf(stderr, "Using hint for offset %zd\n", offset);
+ char *addr = (char *)owner + offset;
+ res = *(PyObject **)addr;
+ if (res != NULL) {
+ Py_INCREF(res);
+ SET_TOP(res);
+ Py_DECREF(owner);
+ DISPATCH();
+ }
+ // Else slot is NULL. Fall through to slow path to raise AttributeError(name).
+ // Don't DEOPT, since the slot is still there.
+ } else {
+ // Fast path for dict.
+ assert(type->tp_dict != NULL);
+ assert(type->tp_dictoffset > 0);
+
+ dictptr = (PyObject **) ((char *)owner + type->tp_dictoffset);
+ dict = *dictptr;
+ if (dict != NULL && PyDict_CheckExact(dict)) {
+ Py_ssize_t hint = la->hint;
+ Py_INCREF(dict);
+ res = NULL;
+ assert(!_PyErr_Occurred(tstate));
+ la->hint = _PyDict_GetItemHint((PyDictObject*)dict, name, hint, &res);
+ if (res != NULL) {
+ assert(la->hint >= 0);
+ if (la->hint == hint && hint >= 0) {
+ // Our hint has helped -- cache hit.
+ OPCACHE_STAT_ATTR_HIT();
+ } else {
+ // The hint we provided didn't work.
+ // Maybe next time?
+ OPCACHE_MAYBE_DEOPT_LOAD_ATTR();
+ }
+
+ Py_INCREF(res);
+ SET_TOP(res);
+ Py_DECREF(owner);
+ Py_DECREF(dict);
+ DISPATCH();
+ }
+ else {
+ _PyErr_Clear(tstate);
+ // This attribute can be missing sometimes;
+ // we don't want to optimize this lookup.
+ OPCACHE_DEOPT_LOAD_ATTR();
+ Py_DECREF(dict);
+ }
+ }
+ else {
+ // There is no dict, or __dict__ doesn't satisfy PyDict_CheckExact.
+ OPCACHE_DEOPT_LOAD_ATTR();
+ }
+ }
+ }
+ else {
+ // The type of the object has either been updated,
+ // or is different. Maybe it will stabilize?
+ OPCACHE_MAYBE_DEOPT_LOAD_ATTR();
+ }
+ OPCACHE_STAT_ATTR_MISS();
+ }
+
+ if (co_opcache != NULL && // co_opcache can be NULL after a DEOPT() call.
+ type->tp_getattro == PyObject_GenericGetAttr)
+ {
+ if (type->tp_dict == NULL) {
+ if (PyType_Ready(type) < 0) {
+ Py_DECREF(owner);
+ SET_TOP(NULL);
+ goto error;
+ }
+ }
+ PyObject *descr = _PyType_Lookup(type, name);
+ if (descr != NULL) {
+ // We found an attribute with a data-like descriptor.
+ PyTypeObject *dtype = Py_TYPE(descr);
+ if (dtype == &PyMemberDescr_Type) { // It's a slot
+ PyMemberDescrObject *member = (PyMemberDescrObject *)descr;
+ struct PyMemberDef *dmem = member->d_member;
+ if (dmem->type == T_OBJECT_EX) {
+ Py_ssize_t offset = dmem->offset;
+ assert(offset > 0); // 0 would be confused with dict hint == -1 (miss).
+
+ if (co_opcache->optimized == 0) {
+ // First time we optimize this opcode.
+ OPCACHE_STAT_ATTR_OPT();
+ co_opcache->optimized = OPCODE_CACHE_MAX_TRIES;
+ // fprintf(stderr, "Setting hint for %s, offset %zd\n", dmem->name, offset);
+ }
+
+ la = &co_opcache->u.la;
+ la->type = type;
+ la->tp_version_tag = type->tp_version_tag;
+ la->hint = ~offset;
+
+ char *addr = (char *)owner + offset;
+ res = *(PyObject **)addr;
+ if (res != NULL) {
+ Py_INCREF(res);
+ Py_DECREF(owner);
+ SET_TOP(res);
+
+ DISPATCH();
+ }
+ // Else slot is NULL. Fall through to slow path to raise AttributeError(name).
+ }
+ // Else it's a slot of a different type. We don't handle those.
+ }
+ // Else it's some other kind of descriptor that we don't handle.
+ OPCACHE_DEOPT_LOAD_ATTR();
+ }
+ else if (type->tp_dictoffset > 0) {
+ // We found an instance with a __dict__.
+ dictptr = (PyObject **) ((char *)owner + type->tp_dictoffset);
+ dict = *dictptr;
+
+ if (dict != NULL && PyDict_CheckExact(dict)) {
+ Py_INCREF(dict);
+ res = NULL;
+ assert(!_PyErr_Occurred(tstate));
+ Py_ssize_t hint = _PyDict_GetItemHint((PyDictObject*)dict, name, -1, &res);
+ if (res != NULL) {
+ Py_INCREF(res);
+ Py_DECREF(dict);
+ Py_DECREF(owner);
+ SET_TOP(res);
+
+ if (co_opcache->optimized == 0) {
+ // First time we optimize this opcode.
+ OPCACHE_STAT_ATTR_OPT();
+ co_opcache->optimized = OPCODE_CACHE_MAX_TRIES;
+ }
+
+ la = &co_opcache->u.la;
+ la->type = type;
+ la->tp_version_tag = type->tp_version_tag;
+ assert(hint >= 0);
+ la->hint = hint;
+
+ DISPATCH();
+ }
+ else {
+ _PyErr_Clear(tstate);
+ }
+ Py_DECREF(dict);
+ } else {
+ // There is no dict, or __dict__ doesn't satisfy PyDict_CheckExact.
+ OPCACHE_DEOPT_LOAD_ATTR();
+ }
+ } else {
+ // The object's class does not have a tp_dictoffset we can use.
+ OPCACHE_DEOPT_LOAD_ATTR();
+ }
+ } else if (type->tp_getattro != PyObject_GenericGetAttr) {
+ OPCACHE_DEOPT_LOAD_ATTR();
+ }
+ }
+
+ // Slow path.
+ res = PyObject_GetAttr(owner, name);
Py_DECREF(owner);
SET_TOP(res);
if (res == NULL)
@@ -3019,7 +3615,7 @@ main_loop:
case TARGET(IS_OP): {
PyObject *right = POP();
PyObject *left = TOP();
- int res = (left == right)^oparg;
+ int res = Py_Is(left, right) ^ oparg;
PyObject *b = res ? Py_True : Py_False;
Py_INCREF(b);
SET_TOP(b);
@@ -3027,7 +3623,7 @@ main_loop:
Py_DECREF(right);
PREDICT(POP_JUMP_IF_FALSE);
PREDICT(POP_JUMP_IF_TRUE);
- FAST_DISPATCH();
+ DISPATCH();
}
case TARGET(CONTAINS_OP): {
@@ -3044,7 +3640,7 @@ main_loop:
PUSH(b);
PREDICT(POP_JUMP_IF_FALSE);
PREDICT(POP_JUMP_IF_TRUE);
- FAST_DISPATCH();
+ DISPATCH();
}
#define CANNOT_CATCH_MSG "catching classes that do not inherit from "\
@@ -3141,28 +3737,31 @@ main_loop:
case TARGET(JUMP_FORWARD): {
JUMPBY(oparg);
- FAST_DISPATCH();
+ DISPATCH();
}
case TARGET(POP_JUMP_IF_FALSE): {
PREDICTED(POP_JUMP_IF_FALSE);
PyObject *cond = POP();
int err;
- if (cond == Py_True) {
+ if (Py_IsTrue(cond)) {
Py_DECREF(cond);
- FAST_DISPATCH();
+ DISPATCH();
}
- if (cond == Py_False) {
+ if (Py_IsFalse(cond)) {
Py_DECREF(cond);
JUMPTO(oparg);
- FAST_DISPATCH();
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
}
err = PyObject_IsTrue(cond);
Py_DECREF(cond);
if (err > 0)
;
- else if (err == 0)
+ else if (err == 0) {
JUMPTO(oparg);
+ CHECK_EVAL_BREAKER();
+ }
else
goto error;
DISPATCH();
@@ -3172,19 +3771,21 @@ main_loop:
PREDICTED(POP_JUMP_IF_TRUE);
PyObject *cond = POP();
int err;
- if (cond == Py_False) {
+ if (Py_IsFalse(cond)) {
Py_DECREF(cond);
- FAST_DISPATCH();
+ DISPATCH();
}
- if (cond == Py_True) {
+ if (Py_IsTrue(cond)) {
Py_DECREF(cond);
JUMPTO(oparg);
- FAST_DISPATCH();
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
}
err = PyObject_IsTrue(cond);
Py_DECREF(cond);
if (err > 0) {
JUMPTO(oparg);
+ CHECK_EVAL_BREAKER();
}
else if (err == 0)
;
@@ -3196,14 +3797,14 @@ main_loop:
case TARGET(JUMP_IF_FALSE_OR_POP): {
PyObject *cond = TOP();
int err;
- if (cond == Py_True) {
+ if (Py_IsTrue(cond)) {
STACK_SHRINK(1);
Py_DECREF(cond);
- FAST_DISPATCH();
+ DISPATCH();
}
- if (cond == Py_False) {
+ if (Py_IsFalse(cond)) {
JUMPTO(oparg);
- FAST_DISPATCH();
+ DISPATCH();
}
err = PyObject_IsTrue(cond);
if (err > 0) {
@@ -3220,14 +3821,14 @@ main_loop:
case TARGET(JUMP_IF_TRUE_OR_POP): {
PyObject *cond = TOP();
int err;
- if (cond == Py_False) {
+ if (Py_IsFalse(cond)) {
STACK_SHRINK(1);
Py_DECREF(cond);
- FAST_DISPATCH();
+ DISPATCH();
}
- if (cond == Py_True) {
+ if (Py_IsTrue(cond)) {
JUMPTO(oparg);
- FAST_DISPATCH();
+ DISPATCH();
}
err = PyObject_IsTrue(cond);
if (err > 0) {
@@ -3245,18 +3846,110 @@ main_loop:
case TARGET(JUMP_ABSOLUTE): {
PREDICTED(JUMP_ABSOLUTE);
JUMPTO(oparg);
-#if FAST_LOOPS
- /* Enabling this path speeds-up all while and for-loops by bypassing
- the per-loop checks for signals. By default, this should be turned-off
- because it prevents detection of a control-break in tight loops like
- "while 1: pass". Compile with this option turned-on when you need
- the speed-up and do not need break checking inside tight loops (ones
- that contain only instructions ending with FAST_DISPATCH).
- */
- FAST_DISPATCH();
-#else
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
+ }
+
+ case TARGET(GET_LEN): {
+ // PUSH(len(TOS))
+ Py_ssize_t len_i = PyObject_Length(TOP());
+ if (len_i < 0) {
+ goto error;
+ }
+ PyObject *len_o = PyLong_FromSsize_t(len_i);
+ if (len_o == NULL) {
+ goto error;
+ }
+ PUSH(len_o);
+ DISPATCH();
+ }
+
+ case TARGET(MATCH_CLASS): {
+ // Pop TOS. On success, set TOS to True and TOS1 to a tuple of
+ // attributes. On failure, set TOS to False.
+ PyObject *names = POP();
+ PyObject *type = TOP();
+ PyObject *subject = SECOND();
+ assert(PyTuple_CheckExact(names));
+ PyObject *attrs = match_class(tstate, subject, type, oparg, names);
+ Py_DECREF(names);
+ if (attrs) {
+ // Success!
+ assert(PyTuple_CheckExact(attrs));
+ Py_DECREF(subject);
+ SET_SECOND(attrs);
+ }
+ else if (_PyErr_Occurred(tstate)) {
+ goto error;
+ }
+ Py_DECREF(type);
+ SET_TOP(PyBool_FromLong(!!attrs));
+ DISPATCH();
+ }
+
+ case TARGET(MATCH_MAPPING): {
+ PyObject *subject = TOP();
+ int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING;
+ PyObject *res = match ? Py_True : Py_False;
+ Py_INCREF(res);
+ PUSH(res);
+ DISPATCH();
+ }
+
+ case TARGET(MATCH_SEQUENCE): {
+ PyObject *subject = TOP();
+ int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE;
+ PyObject *res = match ? Py_True : Py_False;
+ Py_INCREF(res);
+ PUSH(res);
+ DISPATCH();
+ }
+
+ case TARGET(MATCH_KEYS): {
+ // On successful match for all keys, PUSH(values) and PUSH(True).
+ // Otherwise, PUSH(None) and PUSH(False).
+ PyObject *keys = TOP();
+ PyObject *subject = SECOND();
+ PyObject *values_or_none = match_keys(tstate, subject, keys);
+ if (values_or_none == NULL) {
+ goto error;
+ }
+ PUSH(values_or_none);
+ if (Py_IsNone(values_or_none)) {
+ Py_INCREF(Py_False);
+ PUSH(Py_False);
+ DISPATCH();
+ }
+ assert(PyTuple_CheckExact(values_or_none));
+ Py_INCREF(Py_True);
+ PUSH(Py_True);
+ DISPATCH();
+ }
+
+ case TARGET(COPY_DICT_WITHOUT_KEYS): {
+ // rest = dict(TOS1)
+ // for key in TOS:
+ // del rest[key]
+ // SET_TOP(rest)
+ PyObject *keys = TOP();
+ PyObject *subject = SECOND();
+ PyObject *rest = PyDict_New();
+ if (rest == NULL || PyDict_Update(rest, subject)) {
+ Py_XDECREF(rest);
+ goto error;
+ }
+ // This may seem a bit inefficient, but keys is rarely big enough to
+ // actually impact runtime.
+ assert(PyTuple_CheckExact(keys));
+ for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(keys); i++) {
+ if (PyDict_DelItem(rest, PyTuple_GET_ITEM(keys, i))) {
+ Py_DECREF(rest);
+ goto error;
+ }
+ }
+ Py_DECREF(keys);
+ SET_TOP(rest);
DISPATCH();
-#endif
}
case TARGET(GET_ITER): {
@@ -3317,7 +4010,7 @@ main_loop:
goto error;
}
else if (tstate->c_tracefunc != NULL) {
- call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f);
+ call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f, &trace_info);
}
_PyErr_Clear(tstate);
}
@@ -3325,7 +4018,6 @@ main_loop:
STACK_SHRINK(1);
Py_DECREF(iter);
JUMPBY(oparg);
- PREDICT(POP_BLOCK);
DISPATCH();
}
@@ -3414,7 +4106,7 @@ main_loop:
exc = TOP();
val = SECOND();
tb = THIRD();
- assert(exc != Py_None);
+ assert(!Py_IsNone(exc));
assert(!PyLong_Check(exc));
exit_func = PEEK(7);
PyObject *stack[4] = {NULL, exc, val, tb};
@@ -3486,7 +4178,7 @@ main_loop:
`callable` will be POPed by call_function.
NULL will will be POPed manually later.
*/
- res = call_function(tstate, &sp, oparg, NULL);
+ res = call_function(tstate, &trace_info, &sp, oparg, NULL);
stack_pointer = sp;
(void)POP(); /* POP the NULL. */
}
@@ -3503,13 +4195,14 @@ main_loop:
We'll be passing `oparg + 1` to call_function, to
make it accept the `self` as a first argument.
*/
- res = call_function(tstate, &sp, oparg + 1, NULL);
+ res = call_function(tstate, &trace_info, &sp, oparg + 1, NULL);
stack_pointer = sp;
}
PUSH(res);
if (res == NULL)
goto error;
+ CHECK_EVAL_BREAKER();
DISPATCH();
}
@@ -3517,12 +4210,13 @@ main_loop:
PREDICTED(CALL_FUNCTION);
PyObject **sp, *res;
sp = stack_pointer;
- res = call_function(tstate, &sp, oparg, NULL);
+ res = call_function(tstate, &trace_info, &sp, oparg, NULL);
stack_pointer = sp;
PUSH(res);
if (res == NULL) {
goto error;
}
+ CHECK_EVAL_BREAKER();
DISPATCH();
}
@@ -3534,7 +4228,7 @@ main_loop:
assert(PyTuple_GET_SIZE(names) <= oparg);
/* We assume without checking that names contains only strings */
sp = stack_pointer;
- res = call_function(tstate, &sp, oparg, names);
+ res = call_function(tstate, &trace_info, &sp, oparg, names);
stack_pointer = sp;
PUSH(res);
Py_DECREF(names);
@@ -3542,6 +4236,7 @@ main_loop:
if (res == NULL) {
goto error;
}
+ CHECK_EVAL_BREAKER();
DISPATCH();
}
@@ -3579,7 +4274,7 @@ main_loop:
}
assert(PyTuple_CheckExact(callargs));
- result = do_call_core(tstate, func, callargs, kwargs);
+ result = do_call_core(tstate, &trace_info, func, callargs, kwargs);
Py_DECREF(func);
Py_DECREF(callargs);
Py_XDECREF(kwargs);
@@ -3588,6 +4283,7 @@ main_loop:
if (result == NULL) {
goto error;
}
+ CHECK_EVAL_BREAKER();
DISPATCH();
}
@@ -3605,10 +4301,10 @@ main_loop:
if (oparg & 0x08) {
assert(PyTuple_CheckExact(TOP()));
- func ->func_closure = POP();
+ func->func_closure = POP();
}
if (oparg & 0x04) {
- assert(PyDict_CheckExact(TOP()));
+ assert(PyTuple_CheckExact(TOP()));
func->func_annotations = POP();
}
if (oparg & 0x02) {
@@ -3702,6 +4398,14 @@ main_loop:
DISPATCH();
}
+ case TARGET(ROT_N): {
+ PyObject *top = TOP();
+ memmove(&PEEK(oparg - 1), &PEEK(oparg),
+ sizeof(PyObject*) * (oparg - 1));
+ PEEK(oparg) = top;
+ DISPATCH();
+ }
+
case TARGET(EXTENDED_ARG): {
int oldoparg = oparg;
NEXTOPARG();
@@ -3741,11 +4445,15 @@ error:
/* Log traceback info. */
PyTraceBack_Here(f);
- if (tstate->c_tracefunc != NULL)
+ if (tstate->c_tracefunc != NULL) {
+ /* Make sure state is set to FRAME_EXECUTING for tracing */
+ assert(f->f_state == FRAME_EXECUTING);
+ f->f_state = FRAME_UNWINDING;
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj,
- tstate, f);
-
+ tstate, f, &trace_info);
+ }
exception_unwind:
+ f->f_state = FRAME_UNWINDING;
/* Unwind stacks if an exception occurred */
while (f->f_iblock > 0) {
/* Pop the current block. */
@@ -3761,7 +4469,7 @@ exception_unwind:
int handler = b->b_handler;
_PyErr_StackItem *exc_info = tstate->exc_info;
/* Beware, this invalidates all b->b_* fields */
- PyFrame_BlockSetup(f, EXCEPT_HANDLER, -1, STACK_LEVEL());
+ PyFrame_BlockSetup(f, EXCEPT_HANDLER, f->f_lasti, STACK_LEVEL());
PUSH(exc_info->exc_traceback);
PUSH(exc_info->exc_value);
if (exc_info->exc_type != NULL) {
@@ -3793,17 +4501,8 @@ exception_unwind:
PUSH(val);
PUSH(exc);
JUMPTO(handler);
- if (_Py_TracingPossible(ceval2)) {
- int needs_new_execution_window = (f->f_lasti < instr_lb || f->f_lasti >= instr_ub);
- int needs_line_update = (f->f_lasti == instr_lb || f->f_lasti < instr_prev);
- /* Make sure that we trace line after exception if we are in a new execution
- * window or we don't need a line update and we are not in the first instruction
- * of the line. */
- if (needs_new_execution_window || (!needs_line_update && instr_lb > 0)) {
- instr_prev = INT_MAX;
- }
- }
/* Resume normal execution */
+ f->f_state = FRAME_EXECUTING;
goto main_loop;
}
} /* unwind stack */
@@ -3820,18 +4519,19 @@ exception_unwind:
PyObject *o = POP();
Py_XDECREF(o);
}
-
+ f->f_stackdepth = 0;
+ f->f_state = FRAME_RAISED;
exiting:
- if (tstate->use_tracing) {
+ if (trace_info.cframe.use_tracing) {
if (tstate->c_tracefunc) {
if (call_trace_protected(tstate->c_tracefunc, tstate->c_traceobj,
- tstate, f, PyTrace_RETURN, retval)) {
+ tstate, f, &trace_info, PyTrace_RETURN, retval)) {
Py_CLEAR(retval);
}
}
if (tstate->c_profilefunc) {
if (call_trace_protected(tstate->c_profilefunc, tstate->c_profileobj,
- tstate, f, PyTrace_RETURN, retval)) {
+ tstate, f, &trace_info, PyTrace_RETURN, retval)) {
Py_CLEAR(retval);
}
}
@@ -3839,10 +4539,13 @@ exiting:
/* pop frame */
exit_eval_frame:
+ /* Restore previous cframe */
+ tstate->cframe = trace_info.cframe.previous;
+ tstate->cframe->use_tracing = trace_info.cframe.use_tracing;
+
if (PyDTrace_FUNCTION_RETURN_ENABLED())
dtrace_function_return(f);
_Py_LeaveRecursiveCall(tstate);
- f->f_executing = 0;
tstate->frame = f->f_back;
return _Py_CheckFunctionResult(tstate, NULL, retval, __func__);
@@ -3850,7 +4553,7 @@ exit_eval_frame:
static void
format_missing(PyThreadState *tstate, const char *kind,
- PyCodeObject *co, PyObject *names)
+ PyCodeObject *co, PyObject *names, PyObject *qualname)
{
int err;
Py_ssize_t len = PyList_GET_SIZE(names);
@@ -3903,7 +4606,7 @@ format_missing(PyThreadState *tstate, const char *kind,
return;
_PyErr_Format(tstate, PyExc_TypeError,
"%U() missing %i required %s argument%s: %U",
- co->co_name,
+ qualname,
len,
kind,
len == 1 ? "" : "s",
@@ -3914,7 +4617,7 @@ format_missing(PyThreadState *tstate, const char *kind,
static void
missing_arguments(PyThreadState *tstate, PyCodeObject *co,
Py_ssize_t missing, Py_ssize_t defcount,
- PyObject **fastlocals)
+ PyObject **fastlocals, PyObject *qualname)
{
Py_ssize_t i, j = 0;
Py_ssize_t start, end;
@@ -3946,14 +4649,14 @@ missing_arguments(PyThreadState *tstate, PyCodeObject *co,
}
}
assert(j == missing);
- format_missing(tstate, kind, co, missing_names);
+ format_missing(tstate, kind, co, missing_names, qualname);
Py_DECREF(missing_names);
}
static void
too_many_positional(PyThreadState *tstate, PyCodeObject *co,
- Py_ssize_t given, Py_ssize_t defcount,
- PyObject **fastlocals)
+ Py_ssize_t given, PyObject *defaults,
+ PyObject **fastlocals, PyObject *qualname)
{
int plural;
Py_ssize_t kwonly_given = 0;
@@ -3968,6 +4671,7 @@ too_many_positional(PyThreadState *tstate, PyCodeObject *co,
kwonly_given++;
}
}
+ Py_ssize_t defcount = defaults == NULL ? 0 : PyTuple_GET_SIZE(defaults);
if (defcount) {
Py_ssize_t atleast = co_argcount - defcount;
plural = 1;
@@ -3997,7 +4701,7 @@ too_many_positional(PyThreadState *tstate, PyCodeObject *co,
}
_PyErr_Format(tstate, PyExc_TypeError,
"%U() takes %U positional argument%s but %zd%U %s given",
- co->co_name,
+ qualname,
sig,
plural ? "s" : "",
given,
@@ -4009,7 +4713,8 @@ too_many_positional(PyThreadState *tstate, PyCodeObject *co,
static int
positional_only_passed_as_keyword(PyThreadState *tstate, PyCodeObject *co,
- Py_ssize_t kwcount, PyObject* const* kwnames)
+ Py_ssize_t kwcount, PyObject* kwnames,
+ PyObject *qualname)
{
int posonly_conflicts = 0;
PyObject* posonly_names = PyList_New(0);
@@ -4019,7 +4724,7 @@ positional_only_passed_as_keyword(PyThreadState *tstate, PyCodeObject *co,
for (int k2=0; k2<kwcount; k2++){
/* Compare the pointers first and fallback to PyObject_RichCompareBool*/
- PyObject* kwname = kwnames[k2];
+ PyObject* kwname = PyTuple_GET_ITEM(kwnames, k2);
if (kwname == posonly_name){
if(PyList_Append(posonly_names, kwname) != 0) {
goto fail;
@@ -4054,7 +4759,7 @@ positional_only_passed_as_keyword(PyThreadState *tstate, PyCodeObject *co,
_PyErr_Format(tstate, PyExc_TypeError,
"%U() got some positional-only arguments passed"
" as keyword arguments: '%U'",
- co->co_name, error_names);
+ qualname, error_names);
Py_DECREF(error_names);
goto fail;
}
@@ -4068,46 +4773,30 @@ fail:
}
-/* This is gonna seem *real weird*, but if you put some other code between
- PyEval_EvalFrame() and _PyEval_EvalFrameDefault() you will need to adjust
- the test in the if statements in Misc/gdbinit (pystack and pystackv). */
-PyObject *
-_PyEval_EvalCode(PyThreadState *tstate,
- PyObject *_co, PyObject *globals, PyObject *locals,
+PyFrameObject *
+_PyEval_MakeFrameVector(PyThreadState *tstate,
+ PyFrameConstructor *con, PyObject *locals,
PyObject *const *args, Py_ssize_t argcount,
- PyObject *const *kwnames, PyObject *const *kwargs,
- Py_ssize_t kwcount, int kwstep,
- PyObject *const *defs, Py_ssize_t defcount,
- PyObject *kwdefs, PyObject *closure,
- PyObject *name, PyObject *qualname)
+ PyObject *kwnames)
{
assert(is_tstate_valid(tstate));
- PyCodeObject* co = (PyCodeObject*)_co;
- PyFrameObject *f;
- PyObject *retval = NULL;
- PyObject **fastlocals, **freevars;
- PyObject *x, *u;
+ PyCodeObject *co = (PyCodeObject*)con->fc_code;
+ assert(con->fc_defaults == NULL || PyTuple_CheckExact(con->fc_defaults));
const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount;
- Py_ssize_t i, j, n;
- PyObject *kwdict;
-
- if (globals == NULL) {
- _PyErr_SetString(tstate, PyExc_SystemError,
- "PyEval_EvalCodeEx: NULL globals");
- return NULL;
- }
/* Create the frame */
- f = _PyFrame_New_NoTrack(tstate, co, globals, locals);
+ PyFrameObject *f = _PyFrame_New_NoTrack(tstate, con, locals);
if (f == NULL) {
return NULL;
}
- fastlocals = f->f_localsplus;
- freevars = f->f_localsplus + co->co_nlocals;
+ PyObject **fastlocals = f->f_localsplus;
+ PyObject **freevars = f->f_localsplus + co->co_nlocals;
/* Create a dictionary for keyword parameters (**kwags) */
+ PyObject *kwdict;
+ Py_ssize_t i;
if (co->co_flags & CO_VARKEYWORDS) {
kwdict = PyDict_New();
if (kwdict == NULL)
@@ -4123,6 +4812,7 @@ _PyEval_EvalCode(PyThreadState *tstate,
}
/* Copy all positional arguments into local variables */
+ Py_ssize_t j, n;
if (argcount > co->co_argcount) {
n = co->co_argcount;
}
@@ -4130,97 +4820,102 @@ _PyEval_EvalCode(PyThreadState *tstate,
n = argcount;
}
for (j = 0; j < n; j++) {
- x = args[j];
+ PyObject *x = args[j];
Py_INCREF(x);
SETLOCAL(j, x);
}
/* Pack other positional arguments into the *args argument */
if (co->co_flags & CO_VARARGS) {
- u = _PyTuple_FromArray(args + n, argcount - n);
+ PyObject *u = _PyTuple_FromArray(args + n, argcount - n);
if (u == NULL) {
goto fail;
}
SETLOCAL(total_args, u);
}
- /* Handle keyword arguments passed as two strided arrays */
- kwcount *= kwstep;
- for (i = 0; i < kwcount; i += kwstep) {
- PyObject **co_varnames;
- PyObject *keyword = kwnames[i];
- PyObject *value = kwargs[i];
- Py_ssize_t j;
+ /* Handle keyword arguments */
+ if (kwnames != NULL) {
+ Py_ssize_t kwcount = PyTuple_GET_SIZE(kwnames);
+ for (i = 0; i < kwcount; i++) {
+ PyObject **co_varnames;
+ PyObject *keyword = PyTuple_GET_ITEM(kwnames, i);
+ PyObject *value = args[i+argcount];
+ Py_ssize_t j;
- if (keyword == NULL || !PyUnicode_Check(keyword)) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "%U() keywords must be strings",
- co->co_name);
- goto fail;
- }
-
- /* Speed hack: do raw pointer compares. As names are
- normally interned this should almost always hit. */
- co_varnames = ((PyTupleObject *)(co->co_varnames))->ob_item;
- for (j = co->co_posonlyargcount; j < total_args; j++) {
- PyObject *name = co_varnames[j];
- if (name == keyword) {
- goto kw_found;
+ if (keyword == NULL || !PyUnicode_Check(keyword)) {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "%U() keywords must be strings",
+ con->fc_qualname);
+ goto fail;
}
- }
- /* Slow fallback, just in case */
- for (j = co->co_posonlyargcount; j < total_args; j++) {
- PyObject *name = co_varnames[j];
- int cmp = PyObject_RichCompareBool( keyword, name, Py_EQ);
- if (cmp > 0) {
- goto kw_found;
+ /* Speed hack: do raw pointer compares. As names are
+ normally interned this should almost always hit. */
+ co_varnames = ((PyTupleObject *)(co->co_varnames))->ob_item;
+ for (j = co->co_posonlyargcount; j < total_args; j++) {
+ PyObject *varname = co_varnames[j];
+ if (varname == keyword) {
+ goto kw_found;
+ }
}
- else if (cmp < 0) {
- goto fail;
+
+ /* Slow fallback, just in case */
+ for (j = co->co_posonlyargcount; j < total_args; j++) {
+ PyObject *varname = co_varnames[j];
+ int cmp = PyObject_RichCompareBool( keyword, varname, Py_EQ);
+ if (cmp > 0) {
+ goto kw_found;
+ }
+ else if (cmp < 0) {
+ goto fail;
+ }
}
- }
- assert(j >= total_args);
- if (kwdict == NULL) {
+ assert(j >= total_args);
+ if (kwdict == NULL) {
- if (co->co_posonlyargcount
- && positional_only_passed_as_keyword(tstate, co,
- kwcount, kwnames))
- {
+ if (co->co_posonlyargcount
+ && positional_only_passed_as_keyword(tstate, co,
+ kwcount, kwnames,
+ con->fc_qualname))
+ {
+ goto fail;
+ }
+
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "%U() got an unexpected keyword argument '%S'",
+ con->fc_qualname, keyword);
goto fail;
}
- _PyErr_Format(tstate, PyExc_TypeError,
- "%U() got an unexpected keyword argument '%S'",
- co->co_name, keyword);
- goto fail;
- }
-
- if (PyDict_SetItem(kwdict, keyword, value) == -1) {
- goto fail;
- }
- continue;
+ if (PyDict_SetItem(kwdict, keyword, value) == -1) {
+ goto fail;
+ }
+ continue;
- kw_found:
- if (GETLOCAL(j) != NULL) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "%U() got multiple values for argument '%S'",
- co->co_name, keyword);
- goto fail;
+ kw_found:
+ if (GETLOCAL(j) != NULL) {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "%U() got multiple values for argument '%S'",
+ con->fc_qualname, keyword);
+ goto fail;
+ }
+ Py_INCREF(value);
+ SETLOCAL(j, value);
}
- Py_INCREF(value);
- SETLOCAL(j, value);
}
/* Check the number of positional arguments */
if ((argcount > co->co_argcount) && !(co->co_flags & CO_VARARGS)) {
- too_many_positional(tstate, co, argcount, defcount, fastlocals);
+ too_many_positional(tstate, co, argcount, con->fc_defaults, fastlocals,
+ con->fc_qualname);
goto fail;
}
/* Add missing positional arguments (copy default values from defs) */
if (argcount < co->co_argcount) {
+ Py_ssize_t defcount = con->fc_defaults == NULL ? 0 : PyTuple_GET_SIZE(con->fc_defaults);
Py_ssize_t m = co->co_argcount - defcount;
Py_ssize_t missing = 0;
for (i = argcount; i < m; i++) {
@@ -4229,18 +4924,22 @@ _PyEval_EvalCode(PyThreadState *tstate,
}
}
if (missing) {
- missing_arguments(tstate, co, missing, defcount, fastlocals);
+ missing_arguments(tstate, co, missing, defcount, fastlocals,
+ con->fc_qualname);
goto fail;
}
if (n > m)
i = n - m;
else
i = 0;
- for (; i < defcount; i++) {
- if (GETLOCAL(m+i) == NULL) {
- PyObject *def = defs[i];
- Py_INCREF(def);
- SETLOCAL(m+i, def);
+ if (defcount) {
+ PyObject **defs = &PyTuple_GET_ITEM(con->fc_defaults, 0);
+ for (; i < defcount; i++) {
+ if (GETLOCAL(m+i) == NULL) {
+ PyObject *def = defs[i];
+ Py_INCREF(def);
+ SETLOCAL(m+i, def);
+ }
}
}
}
@@ -4249,12 +4948,11 @@ _PyEval_EvalCode(PyThreadState *tstate,
if (co->co_kwonlyargcount > 0) {
Py_ssize_t missing = 0;
for (i = co->co_argcount; i < total_args; i++) {
- PyObject *name;
if (GETLOCAL(i) != NULL)
continue;
- name = PyTuple_GET_ITEM(co->co_varnames, i);
- if (kwdefs != NULL) {
- PyObject *def = PyDict_GetItemWithError(kwdefs, name);
+ PyObject *varname = PyTuple_GET_ITEM(co->co_varnames, i);
+ if (con->fc_kwdefaults != NULL) {
+ PyObject *def = PyDict_GetItemWithError(con->fc_kwdefaults, varname);
if (def) {
Py_INCREF(def);
SETLOCAL(i, def);
@@ -4267,7 +4965,8 @@ _PyEval_EvalCode(PyThreadState *tstate,
missing++;
}
if (missing) {
- missing_arguments(tstate, co, missing, -1, fastlocals);
+ missing_arguments(tstate, co, missing, -1, fastlocals,
+ con->fc_qualname);
goto fail;
}
}
@@ -4294,41 +4993,76 @@ _PyEval_EvalCode(PyThreadState *tstate,
/* Copy closure variables to free variables */
for (i = 0; i < PyTuple_GET_SIZE(co->co_freevars); ++i) {
- PyObject *o = PyTuple_GET_ITEM(closure, i);
+ PyObject *o = PyTuple_GET_ITEM(con->fc_closure, i);
Py_INCREF(o);
freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o;
}
- /* Handle generator/coroutine/asynchronous generator */
- if (co->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) {
- PyObject *gen;
- int is_coro = co->co_flags & CO_COROUTINE;
+ return f;
- /* Don't need to keep the reference to f_back, it will be set
- * when the generator is resumed. */
- Py_CLEAR(f->f_back);
-
- /* Create a new generator that owns the ready to run frame
- * and return that as the value. */
- if (is_coro) {
- gen = PyCoro_New(f, name, qualname);
- } else if (co->co_flags & CO_ASYNC_GENERATOR) {
- gen = PyAsyncGen_New(f, name, qualname);
- } else {
- gen = PyGen_NewWithQualName(f, name, qualname);
- }
- if (gen == NULL) {
- return NULL;
- }
+fail: /* Jump here from prelude on failure */
+ /* decref'ing the frame can cause __del__ methods to get invoked,
+ which can call back into Python. While we're done with the
+ current Python frame (f), the associated C stack is still in use,
+ so recursion_depth must be boosted for the duration.
+ */
+ if (Py_REFCNT(f) > 1) {
+ Py_DECREF(f);
_PyObject_GC_TRACK(f);
+ }
+ else {
+ ++tstate->recursion_depth;
+ Py_DECREF(f);
+ --tstate->recursion_depth;
+ }
+ return NULL;
+}
- return gen;
+static PyObject *
+make_coro(PyFrameConstructor *con, PyFrameObject *f)
+{
+ assert (((PyCodeObject *)con->fc_code)->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR));
+ PyObject *gen;
+ int is_coro = ((PyCodeObject *)con->fc_code)->co_flags & CO_COROUTINE;
+
+ /* Don't need to keep the reference to f_back, it will be set
+ * when the generator is resumed. */
+ Py_CLEAR(f->f_back);
+
+ /* Create a new generator that owns the ready to run frame
+ * and return that as the value. */
+ if (is_coro) {
+ gen = PyCoro_New(f, con->fc_name, con->fc_qualname);
+ } else if (((PyCodeObject *)con->fc_code)->co_flags & CO_ASYNC_GENERATOR) {
+ gen = PyAsyncGen_New(f, con->fc_name, con->fc_qualname);
+ } else {
+ gen = PyGen_NewWithQualName(f, con->fc_name, con->fc_qualname);
+ }
+ if (gen == NULL) {
+ return NULL;
}
- retval = _PyEval_EvalFrame(tstate, f, 0);
+ _PyObject_GC_TRACK(f);
-fail: /* Jump here from prelude on failure */
+ return gen;
+}
+
+PyObject *
+_PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con,
+ PyObject *locals,
+ PyObject* const* args, size_t argcount,
+ PyObject *kwnames)
+{
+ PyFrameObject *f = _PyEval_MakeFrameVector(
+ tstate, con, locals, args, argcount, kwnames);
+ if (f == NULL) {
+ return NULL;
+ }
+ if (((PyCodeObject *)con->fc_code)->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) {
+ return make_coro(con, f);
+ }
+ PyObject *retval = _PyEval_EvalFrame(tstate, f, 0);
/* decref'ing the frame can cause __del__ methods to get invoked,
which can call back into Python. While we're done with the
@@ -4347,26 +5081,7 @@ fail: /* Jump here from prelude on failure */
return retval;
}
-
-PyObject *
-_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
- PyObject *const *args, Py_ssize_t argcount,
- PyObject *const *kwnames, PyObject *const *kwargs,
- Py_ssize_t kwcount, int kwstep,
- PyObject *const *defs, Py_ssize_t defcount,
- PyObject *kwdefs, PyObject *closure,
- PyObject *name, PyObject *qualname)
-{
- PyThreadState *tstate = _PyThreadState_GET();
- return _PyEval_EvalCode(tstate, _co, globals, locals,
- args, argcount,
- kwnames, kwargs,
- kwcount, kwstep,
- defs, defcount,
- kwdefs, closure,
- name, qualname);
-}
-
+/* Legacy API */
PyObject *
PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
PyObject *const *args, int argcount,
@@ -4374,15 +5089,70 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
PyObject *const *defs, int defcount,
PyObject *kwdefs, PyObject *closure)
{
- return _PyEval_EvalCodeWithName(_co, globals, locals,
- args, argcount,
- kws, kws != NULL ? kws + 1 : NULL,
- kwcount, 2,
- defs, defcount,
- kwdefs, closure,
- NULL, NULL);
+ PyThreadState *tstate = _PyThreadState_GET();
+ PyObject *res = NULL;
+ PyObject *defaults = _PyTuple_FromArray(defs, defcount);
+ if (defaults == NULL) {
+ return NULL;
+ }
+ PyObject *builtins = _PyEval_BuiltinsFromGlobals(tstate, globals); // borrowed ref
+ if (builtins == NULL) {
+ Py_DECREF(defaults);
+ return NULL;
+ }
+ if (locals == NULL) {
+ locals = globals;
+ }
+ PyObject *kwnames = NULL;
+ PyObject *const *allargs;
+ PyObject **newargs = NULL;
+ if (kwcount == 0) {
+ allargs = args;
+ }
+ else {
+ kwnames = PyTuple_New(kwcount);
+ if (kwnames == NULL) {
+ goto fail;
+ }
+ newargs = PyMem_Malloc(sizeof(PyObject *)*(kwcount+argcount));
+ if (newargs == NULL) {
+ goto fail;
+ }
+ for (int i = 0; i < argcount; i++) {
+ newargs[i] = args[i];
+ }
+ for (int i = 0; i < kwcount; i++) {
+ Py_INCREF(kws[2*i]);
+ PyTuple_SET_ITEM(kwnames, i, kws[2*i]);
+ newargs[argcount+i] = kws[2*i+1];
+ }
+ allargs = newargs;
+ }
+ for (int i = 0; i < kwcount; i++) {
+ Py_INCREF(kws[2*i]);
+ PyTuple_SET_ITEM(kwnames, i, kws[2*i]);
+ }
+ PyFrameConstructor constr = {
+ .fc_globals = globals,
+ .fc_builtins = builtins,
+ .fc_name = ((PyCodeObject *)_co)->co_name,
+ .fc_qualname = ((PyCodeObject *)_co)->co_name,
+ .fc_code = _co,
+ .fc_defaults = defaults,
+ .fc_kwdefaults = kwdefs,
+ .fc_closure = closure
+ };
+ res = _PyEval_Vector(tstate, &constr, locals,
+ allargs, argcount,
+ kwnames);
+fail:
+ Py_XDECREF(kwnames);
+ PyMem_Free(newargs);
+ Py_DECREF(defaults);
+ return res;
}
+
static PyObject *
special_lookup(PyThreadState *tstate, PyObject *o, _Py_Identifier *id)
{
@@ -4410,7 +5180,7 @@ do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause)
type = exc_info->exc_type;
value = exc_info->exc_value;
tb = exc_info->exc_traceback;
- if (type == Py_None || type == NULL) {
+ if (Py_IsNone(type) || type == NULL) {
_PyErr_SetString(tstate, PyExc_RuntimeError,
"No active exception to reraise");
return 0;
@@ -4468,7 +5238,7 @@ do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause)
else if (PyExceptionInstance_Check(cause)) {
fixed_cause = cause;
}
- else if (cause == Py_None) {
+ else if (Py_IsNone(cause)) {
Py_DECREF(cause);
fixed_cause = NULL;
}
@@ -4594,7 +5364,6 @@ Error:
return 0;
}
-
#ifdef LLTRACE
static int
prtrace(PyThreadState *tstate, PyObject *v, const char *str)
@@ -4614,7 +5383,9 @@ prtrace(PyThreadState *tstate, PyObject *v, const char *str)
static void
call_exc_trace(Py_tracefunc func, PyObject *self,
- PyThreadState *tstate, PyFrameObject *f)
+ PyThreadState *tstate,
+ PyFrameObject *f,
+ PyTraceInfo *trace_info)
{
PyObject *type, *value, *traceback, *orig_traceback, *arg;
int err;
@@ -4630,7 +5401,7 @@ call_exc_trace(Py_tracefunc func, PyObject *self,
_PyErr_Restore(tstate, type, value, orig_traceback);
return;
}
- err = call_trace(func, self, tstate, f, PyTrace_EXCEPTION, arg);
+ err = call_trace(func, self, tstate, f, trace_info, PyTrace_EXCEPTION, arg);
Py_DECREF(arg);
if (err == 0) {
_PyErr_Restore(tstate, type, value, orig_traceback);
@@ -4645,12 +5416,13 @@ call_exc_trace(Py_tracefunc func, PyObject *self,
static int
call_trace_protected(Py_tracefunc func, PyObject *obj,
PyThreadState *tstate, PyFrameObject *frame,
+ PyTraceInfo *trace_info,
int what, PyObject *arg)
{
PyObject *type, *value, *traceback;
int err;
_PyErr_Fetch(tstate, &type, &value, &traceback);
- err = call_trace(func, obj, tstate, frame, what, arg);
+ err = call_trace(func, obj, tstate, frame, trace_info, what, arg);
if (err == 0)
{
_PyErr_Restore(tstate, type, value, traceback);
@@ -4664,18 +5436,36 @@ call_trace_protected(Py_tracefunc func, PyObject *obj,
}
}
+static void
+initialize_trace_info(PyTraceInfo *trace_info, PyFrameObject *frame)
+{
+ if (trace_info->code != frame->f_code) {
+ trace_info->code = frame->f_code;
+ _PyCode_InitAddressRange(frame->f_code, &trace_info->bounds);
+ }
+}
+
static int
call_trace(Py_tracefunc func, PyObject *obj,
PyThreadState *tstate, PyFrameObject *frame,
+ PyTraceInfo *trace_info,
int what, PyObject *arg)
{
int result;
if (tstate->tracing)
return 0;
tstate->tracing++;
- tstate->use_tracing = 0;
+ tstate->cframe->use_tracing = 0;
+ if (frame->f_lasti < 0) {
+ frame->f_lineno = frame->f_code->co_firstlineno;
+ }
+ else {
+ initialize_trace_info(trace_info, frame);
+ frame->f_lineno = _PyCode_CheckLineNumber(frame->f_lasti*sizeof(_Py_CODEUNIT), &trace_info->bounds);
+ }
result = func(obj, frame, what, arg);
- tstate->use_tracing = ((tstate->c_tracefunc != NULL)
+ frame->f_lineno = 0;
+ tstate->cframe->use_tracing = ((tstate->c_tracefunc != NULL)
|| (tstate->c_profilefunc != NULL));
tstate->tracing--;
return result;
@@ -4686,15 +5476,15 @@ _PyEval_CallTracing(PyObject *func, PyObject *args)
{
PyThreadState *tstate = _PyThreadState_GET();
int save_tracing = tstate->tracing;
- int save_use_tracing = tstate->use_tracing;
+ int save_use_tracing = tstate->cframe->use_tracing;
PyObject *result;
tstate->tracing = 0;
- tstate->use_tracing = ((tstate->c_tracefunc != NULL)
+ tstate->cframe->use_tracing = ((tstate->c_tracefunc != NULL)
|| (tstate->c_profilefunc != NULL));
result = PyObject_Call(func, args, NULL);
tstate->tracing = save_tracing;
- tstate->use_tracing = save_use_tracing;
+ tstate->cframe->use_tracing = save_use_tracing;
return result;
}
@@ -4702,36 +5492,27 @@ _PyEval_CallTracing(PyObject *func, PyObject *args)
static int
maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
PyThreadState *tstate, PyFrameObject *frame,
- int *instr_lb, int *instr_ub, int *instr_prev)
+ PyTraceInfo *trace_info, 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 (frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub) {
- PyAddrPair bounds;
- line = _PyCode_CheckLineNumber(frame->f_code, frame->f_lasti,
- &bounds);
- *instr_lb = bounds.ap_lower;
- *instr_ub = bounds.ap_upper;
- }
/* 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
then call the trace function if we're tracing source lines.
*/
- if ((frame->f_lasti == *instr_lb || frame->f_lasti < *instr_prev)) {
- frame->f_lineno = line;
- if (frame->f_trace_lines) {
- result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None);
+ initialize_trace_info(trace_info, frame);
+ int lastline = _PyCode_CheckLineNumber(instr_prev*sizeof(_Py_CODEUNIT), &trace_info->bounds);
+ int line = _PyCode_CheckLineNumber(frame->f_lasti*sizeof(_Py_CODEUNIT), &trace_info->bounds);
+ if (line != -1 && frame->f_trace_lines) {
+ /* Trace backward edges or if line number has changed */
+ if (frame->f_lasti < instr_prev || line != lastline) {
+ result = call_trace(func, obj, tstate, frame, trace_info, PyTrace_LINE, Py_None);
}
}
/* Always emit an opcode event if we're tracing all opcodes. */
if (frame->f_trace_opcodes) {
- result = call_trace(func, obj, tstate, frame, PyTrace_OPCODE, Py_None);
+ result = call_trace(func, obj, tstate, frame, trace_info, PyTrace_OPCODE, Py_None);
}
- *instr_prev = frame->f_lasti;
return result;
}
@@ -4754,7 +5535,7 @@ _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
tstate->c_profilefunc = NULL;
tstate->c_profileobj = NULL;
/* Must make sure that tracing is not ignored if 'profileobj' is freed */
- tstate->use_tracing = tstate->c_tracefunc != NULL;
+ tstate->cframe->use_tracing = tstate->c_tracefunc != NULL;
Py_XDECREF(profileobj);
Py_XINCREF(arg);
@@ -4762,7 +5543,7 @@ _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
tstate->c_profilefunc = func;
/* Flag that tracing or profiling is turned on */
- tstate->use_tracing = (func != NULL) || (tstate->c_tracefunc != NULL);
+ tstate->cframe->use_tracing = (func != NULL) || (tstate->c_tracefunc != NULL);
return 0;
}
@@ -4790,14 +5571,12 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
return -1;
}
- struct _ceval_state *ceval2 = &tstate->interp->ceval;
PyObject *traceobj = tstate->c_traceobj;
- ceval2->tracing_possible += (func != NULL) - (tstate->c_tracefunc != NULL);
tstate->c_tracefunc = NULL;
tstate->c_traceobj = NULL;
/* Must make sure that profiling is not ignored if 'traceobj' is freed */
- tstate->use_tracing = (tstate->c_profilefunc != NULL);
+ tstate->cframe->use_tracing = (tstate->c_profilefunc != NULL);
Py_XDECREF(traceobj);
Py_XINCREF(arg);
@@ -4805,7 +5584,7 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
tstate->c_tracefunc = func;
/* Flag that tracing or profiling is turned on */
- tstate->use_tracing = ((func != NULL)
+ tstate->cframe->use_tracing = ((func != NULL)
|| (tstate->c_profilefunc != NULL));
return 0;
@@ -4886,14 +5665,20 @@ PyEval_GetFrame(void)
}
PyObject *
+_PyEval_GetBuiltins(PyThreadState *tstate)
+{
+ PyFrameObject *frame = tstate->frame;
+ if (frame != NULL) {
+ return frame->f_builtins;
+ }
+ return tstate->interp->builtins;
+}
+
+PyObject *
PyEval_GetBuiltins(void)
{
PyThreadState *tstate = _PyThreadState_GET();
- PyFrameObject *current_frame = tstate->frame;
- if (current_frame == NULL)
- return tstate->interp->builtins;
- else
- return current_frame->f_builtins;
+ return _PyEval_GetBuiltins(tstate);
}
/* Convenience function to get a builtin from its name */
@@ -4994,9 +5779,9 @@ PyEval_GetFuncDesc(PyObject *func)
}
#define C_TRACE(x, call) \
-if (tstate->use_tracing && tstate->c_profilefunc) { \
+if (trace_info->cframe.use_tracing && tstate->c_profilefunc) { \
if (call_trace(tstate->c_profilefunc, tstate->c_profileobj, \
- tstate, tstate->frame, \
+ tstate, tstate->frame, trace_info, \
PyTrace_C_CALL, func)) { \
x = NULL; \
} \
@@ -5006,13 +5791,13 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \
if (x == NULL) { \
call_trace_protected(tstate->c_profilefunc, \
tstate->c_profileobj, \
- tstate, tstate->frame, \
+ tstate, tstate->frame, trace_info, \
PyTrace_C_EXCEPTION, func); \
/* XXX should pass (type, value, tb) */ \
} else { \
if (call_trace(tstate->c_profilefunc, \
tstate->c_profileobj, \
- tstate, tstate->frame, \
+ tstate, tstate->frame, trace_info, \
PyTrace_C_RETURN, func)) { \
Py_DECREF(x); \
x = NULL; \
@@ -5027,6 +5812,7 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \
static PyObject *
trace_call_function(PyThreadState *tstate,
+ PyTraceInfo *trace_info,
PyObject *func,
PyObject **args, Py_ssize_t nargs,
PyObject *kwnames)
@@ -5061,7 +5847,11 @@ trace_call_function(PyThreadState *tstate,
/* Issue #29227: Inline call_function() into _PyEval_EvalFrameDefault()
to reduce the stack consumption. */
Py_LOCAL_INLINE(PyObject *) _Py_HOT_FUNCTION
-call_function(PyThreadState *tstate, PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames)
+call_function(PyThreadState *tstate,
+ PyTraceInfo *trace_info,
+ PyObject ***pp_stack,
+ Py_ssize_t oparg,
+ PyObject *kwnames)
{
PyObject **pfunc = (*pp_stack) - oparg - 1;
PyObject *func = *pfunc;
@@ -5070,8 +5860,8 @@ call_function(PyThreadState *tstate, PyObject ***pp_stack, Py_ssize_t oparg, PyO
Py_ssize_t nargs = oparg - nkwargs;
PyObject **stack = (*pp_stack) - nargs - nkwargs;
- if (tstate->use_tracing) {
- x = trace_call_function(tstate, func, stack, nargs, kwnames);
+ if (trace_info->cframe.use_tracing) {
+ x = trace_call_function(tstate, trace_info, func, stack, nargs, kwnames);
}
else {
x = PyObject_Vectorcall(func, stack, nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames);
@@ -5089,7 +5879,11 @@ call_function(PyThreadState *tstate, PyObject ***pp_stack, Py_ssize_t oparg, PyO
}
static PyObject *
-do_call_core(PyThreadState *tstate, PyObject *func, PyObject *callargs, PyObject *kwdict)
+do_call_core(PyThreadState *tstate,
+ PyTraceInfo *trace_info,
+ PyObject *func,
+ PyObject *callargs,
+ PyObject *kwdict)
{
PyObject *result;
@@ -5099,7 +5893,7 @@ do_call_core(PyThreadState *tstate, PyObject *func, PyObject *callargs, PyObject
}
else if (Py_IS_TYPE(func, &PyMethodDescr_Type)) {
Py_ssize_t nargs = PyTuple_GET_SIZE(callargs);
- if (nargs > 0 && tstate->use_tracing) {
+ if (nargs > 0 && trace_info->cframe.use_tracing) {
/* We need to create a temporary bound method as argument
for profiling.
@@ -5135,7 +5929,7 @@ int
_PyEval_SliceIndex(PyObject *v, Py_ssize_t *pi)
{
PyThreadState *tstate = _PyThreadState_GET();
- if (v != Py_None) {
+ if (!Py_IsNone(v)) {
Py_ssize_t x;
if (_PyIndex_Check(v)) {
x = PyNumber_AsSsize_t(v, NULL);
@@ -5459,6 +6253,23 @@ format_exc_check_arg(PyThreadState *tstate, PyObject *exc,
return;
_PyErr_Format(tstate, exc, format_str, obj_str);
+
+ if (exc == PyExc_NameError) {
+ // Include the name in the NameError exceptions to offer suggestions later.
+ _Py_IDENTIFIER(name);
+ PyObject *type, *value, *traceback;
+ PyErr_Fetch(&type, &value, &traceback);
+ PyErr_NormalizeException(&type, &value, &traceback);
+ if (PyErr_GivenExceptionMatches(value, PyExc_NameError)) {
+ PyNameErrorObject* exc = (PyNameErrorObject*) value;
+ if (exc->name == NULL) {
+ // We do not care if this fails because we are going to restore the
+ // NameError anyway.
+ (void)_PyObject_SetAttrId(value, &PyId_name, obj);
+ }
+ }
+ PyErr_Restore(type, value, traceback);
+ }
}
static void
@@ -5626,7 +6437,7 @@ dtrace_function_entry(PyFrameObject *f)
PyCodeObject *code = f->f_code;
filename = PyUnicode_AsUTF8(code->co_filename);
funcname = PyUnicode_AsUTF8(code->co_name);
- lineno = PyCode_Addr2Line(code, f->f_lasti);
+ lineno = PyFrame_GetLineNumber(f);
PyDTrace_FUNCTION_ENTRY(filename, funcname, lineno);
}
@@ -5641,7 +6452,7 @@ dtrace_function_return(PyFrameObject *f)
PyCodeObject *code = f->f_code;
filename = PyUnicode_AsUTF8(code->co_filename);
funcname = PyUnicode_AsUTF8(code->co_name);
- lineno = PyCode_Addr2Line(code, f->f_lasti);
+ lineno = PyFrame_GetLineNumber(f);
PyDTrace_FUNCTION_RETURN(filename, funcname, lineno);
}
@@ -5649,35 +6460,30 @@ dtrace_function_return(PyFrameObject *f)
/* DTrace equivalent of maybe_call_line_trace. */
static void
maybe_dtrace_line(PyFrameObject *frame,
- int *instr_lb, int *instr_ub, int *instr_prev)
+ PyTraceInfo *trace_info, int instr_prev)
{
- int line = frame->f_lineno;
const char *co_filename, *co_name;
/* If the last instruction executed isn't in the current
instruction window, reset the window.
*/
- if (frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub) {
- PyAddrPair bounds;
- line = _PyCode_CheckLineNumber(frame->f_code, frame->f_lasti,
- &bounds);
- *instr_lb = bounds.ap_lower;
- *instr_ub = bounds.ap_upper;
- }
+ initialize_trace_info(trace_info, frame);
+ int line = _PyCode_CheckLineNumber(frame->f_lasti*sizeof(_Py_CODEUNIT), &trace_info->bounds);
/* 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;
- co_filename = PyUnicode_AsUTF8(frame->f_code->co_filename);
- if (!co_filename)
- co_filename = "?";
- co_name = PyUnicode_AsUTF8(frame->f_code->co_name);
- if (!co_name)
- co_name = "?";
- PyDTrace_LINE(co_filename, co_name, line);
- }
- *instr_prev = frame->f_lasti;
+ if (line != frame->f_lineno || frame->f_lasti < instr_prev) {
+ if (line != -1) {
+ frame->f_lineno = line;
+ co_filename = PyUnicode_AsUTF8(frame->f_code->co_filename);
+ if (!co_filename)
+ co_filename = "?";
+ co_name = PyUnicode_AsUTF8(frame->f_code->co_name);
+ if (!co_name)
+ co_name = "?";
+ PyDTrace_LINE(co_filename, co_name, line);
+ }
+ }
}