computes the stack effect of bytecode instructions.
Detect all offsets in the code object *code* which are jump targets, and
return a list of these offsets.
+
+.. function:: stack_effect(opcode, [oparg])
+
+ Compute the stack effect of *opcode* with argument *oparg*.
+
+ .. versionadded:: 3.4
+
.. _bytecodes:
Python Bytecode Instructions
/* _Py_Mangle is defined in compile.c */
PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name);
+#define PY_INVALID_STACK_EFFECT INT_MAX
+PyAPI_FUNC(int) PyCompile_OpcodeStackEffect(int opcode, int oparg);
+
#ifdef __cplusplus
}
#endif
"haslocal", "hascompare", "hasfree", "opname", "opmap",
"HAVE_ARGUMENT", "EXTENDED_ARG", "hasnargs"]
+# It's a chicken-and-egg I'm afraid:
+# We're imported before _opcode's made.
+# With exception unheeded
+# (stack_effect is not needed)
+# Both our chickens and eggs are allayed.
+# --Larry Hastings, 2013/11/23
+
+try:
+ from _opcode import stack_effect
+ __all__.append('stack_effect')
+except ImportError:
+ pass
+
cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is',
'is not', 'exception match', 'BAD')
--- /dev/null
+import dis
+import _opcode
+from test.support import run_unittest
+import unittest
+
+class OpcodeTests(unittest.TestCase):
+
+ def test_stack_effect(self):
+ self.assertEqual(_opcode.stack_effect(dis.opmap['POP_TOP']), -1)
+ self.assertEqual(_opcode.stack_effect(dis.opmap['DUP_TOP_TWO']), 2)
+ self.assertEqual(_opcode.stack_effect(dis.opmap['BUILD_SLICE'], 0), -1)
+ self.assertEqual(_opcode.stack_effect(dis.opmap['BUILD_SLICE'], 1), -1)
+ self.assertEqual(_opcode.stack_effect(dis.opmap['BUILD_SLICE'], 3), -2)
+ self.assertRaises(ValueError, _opcode.stack_effect, 30000)
+ self.assertRaises(ValueError, _opcode.stack_effect, dis.opmap['BUILD_SLICE'])
+ self.assertRaises(ValueError, _opcode.stack_effect, dis.opmap['POP_TOP'], 0)
+
+def test_main():
+ run_unittest(OpcodeTests)
+
+if __name__ == "__main__":
+ test_main()
Library
-------
+- Issue #19722: Added opcode.stack_effect(), which
+ computes the stack effect of bytecode instructions.
- Issue #19735: Implement private function ssl._create_stdlib_context() to
create SSLContext objects in Python's stdlib module. It provides a single
--- /dev/null
+#include "Python.h"
+#include "opcode.h"
+
+
+/*[clinic]
+
+module _opcode
+
+_opcode.stack_effect -> int
+
+ opcode: int
+
+ [
+ oparg: int
+ ]
+ /
+
+Compute the stack effect of the opcode.
+[clinic]*/
+
+PyDoc_STRVAR(_opcode_stack_effect__doc__,
+"Compute the stack effect of the opcode.\n"
+"\n"
+"_opcode.stack_effect(opcode, [oparg])");
+
+#define _OPCODE_STACK_EFFECT_METHODDEF \
+ {"stack_effect", (PyCFunction)_opcode_stack_effect, METH_VARARGS, _opcode_stack_effect__doc__},
+
+static int
+_opcode_stack_effect_impl(PyObject *module, int opcode, int group_right_1, int oparg);
+
+static PyObject *
+_opcode_stack_effect(PyObject *module, PyObject *args)
+{
+ PyObject *return_value = NULL;
+ int opcode;
+ int group_right_1 = 0;
+ int oparg = 0;
+ int _return_value;
+
+ switch (PyTuple_Size(args)) {
+ case 1:
+ if (!PyArg_ParseTuple(args, "i:stack_effect", &opcode))
+ return NULL;
+ break;
+ case 2:
+ if (!PyArg_ParseTuple(args, "ii:stack_effect", &opcode, &oparg))
+ return NULL;
+ group_right_1 = 1;
+ break;
+ default:
+ PyErr_SetString(PyExc_TypeError, "_opcode.stack_effect requires 1 to 2 arguments");
+ return NULL;
+ }
+ _return_value = _opcode_stack_effect_impl(module, opcode, group_right_1, oparg);
+ if ((_return_value == -1) && PyErr_Occurred())
+ goto exit;
+ return_value = PyLong_FromLong((long)_return_value);
+
+exit:
+ return return_value;
+}
+
+static int
+_opcode_stack_effect_impl(PyObject *module, int opcode, int group_right_1, int oparg)
+/*[clinic checksum: 2312ded40abc9bcbce718942de21f53e61a2dfd3]*/
+{
+ int effect;
+ if (HAS_ARG(opcode)) {
+ if (!group_right_1) {
+ PyErr_SetString(PyExc_ValueError,
+ "stack_effect: opcode requires oparg but oparg was not specified");
+ return -1;
+ }
+ }
+ else if (group_right_1) {
+ PyErr_SetString(PyExc_ValueError,
+ "stack_effect: opcode does not permit oparg but oparg was specified");
+ return -1;
+ }
+ effect = PyCompile_OpcodeStackEffect(opcode, oparg);
+ if (effect == PY_INVALID_STACK_EFFECT) {
+ PyErr_SetString(PyExc_ValueError,
+ "invalid opcode or oparg");
+ return -1;
+ }
+ return effect;
+}
+
+
+
+
+static PyMethodDef
+opcode_functions[] = {
+ _OPCODE_STACK_EFFECT_METHODDEF
+ {NULL, NULL, 0, NULL}
+};
+
+
+static struct PyModuleDef opcodemodule = {
+ PyModuleDef_HEAD_INIT,
+ "_opcode",
+ "Opcode support module.",
+ -1,
+ opcode_functions,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+PyMODINIT_FUNC
+PyInit__opcode(void)
+{
+ return PyModule_Create(&opcodemodule);
+}
b->b_instr[off].i_lineno = c->u->u_lineno;
}
-static int
-opcode_stack_effect(int opcode, int oparg)
+int
+PyCompile_OpcodeStackEffect(int opcode, int oparg)
{
switch (opcode) {
case POP_TOP:
case DELETE_DEREF:
return 0;
default:
- fprintf(stderr, "opcode = %d\n", opcode);
- Py_FatalError("opcode_stack_effect()");
-
+ return PY_INVALID_STACK_EFFECT;
}
- return 0; /* not reachable */
+ return PY_INVALID_STACK_EFFECT; /* not reachable */
}
/* Add an opcode with no argument.
static int
stackdepth_walk(struct compiler *c, basicblock *b, int depth, int maxdepth)
{
- int i, target_depth;
+ int i, target_depth, effect;
struct instr *instr;
if (b->b_seen || b->b_startdepth >= depth)
return maxdepth;
b->b_startdepth = depth;
for (i = 0; i < b->b_iused; i++) {
instr = &b->b_instr[i];
- depth += opcode_stack_effect(instr->i_opcode, instr->i_oparg);
+ effect = PyCompile_OpcodeStackEffect(instr->i_opcode, instr->i_oparg);
+ if (effect == PY_INVALID_STACK_EFFECT) {
+ fprintf(stderr, "opcode = %d\n", instr->i_opcode);
+ Py_FatalError("PyCompile_OpcodeStackEffect()");
+ }
+ depth += effect;
+
if (depth > maxdepth)
maxdepth = depth;
assert(depth >= 0); /* invalid code or bug in stackdepth() */
exts.append( Extension('_lsprof', ['_lsprof.c', 'rotatingtree.c']) )
# static Unicode character database
exts.append( Extension('unicodedata', ['unicodedata.c']) )
+ # _opcode module
+ exts.append( Extension('_opcode', ['_opcode.c']) )
# Modules with some UNIX dependencies -- on by default:
# (If you have a really backward UNIX, select and socket may not be