[Python-checkins] cpython: Issue #19722: Added opcode.stack_effect(), which accurately

larry.hastings python-checkins at python.org
Sat Nov 23 23:49:59 CET 2013


http://hg.python.org/cpython/rev/5fe72b9ed48e
changeset:   87465:5fe72b9ed48e
user:        Larry Hastings <larry at hastings.org>
date:        Sat Nov 23 14:49:22 2013 -0800
summary:
  Issue #19722: Added opcode.stack_effect(), which accurately
computes the stack effect of bytecode instructions.

files:
  Doc/library/dis.rst      |    7 +
  Include/compile.h        |    3 +
  Lib/opcode.py            |   13 +++
  Lib/test/test__opcode.py |   22 +++++
  Misc/NEWS                |    2 +
  Modules/_opcode.c        |  116 +++++++++++++++++++++++++++
  Python/compile.c         |   20 ++-
  setup.py                 |    2 +
  8 files changed, 177 insertions(+), 8 deletions(-)


diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -217,6 +217,13 @@
    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
diff --git a/Include/compile.h b/Include/compile.h
--- a/Include/compile.h
+++ b/Include/compile.h
@@ -54,6 +54,9 @@
 /* _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
diff --git a/Lib/opcode.py b/Lib/opcode.py
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -8,6 +8,19 @@
            "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')
 
diff --git a/Lib/test/test__opcode.py b/Lib/test/test__opcode.py
new file mode 100644
--- /dev/null
+++ b/Lib/test/test__opcode.py
@@ -0,0 +1,22 @@
+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()
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -67,6 +67,8 @@
 
 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
diff --git a/Modules/_opcode.c b/Modules/_opcode.c
new file mode 100644
--- /dev/null
+++ b/Modules/_opcode.c
@@ -0,0 +1,116 @@
+#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);
+}
diff --git a/Python/compile.c b/Python/compile.c
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -853,8 +853,8 @@
     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:
@@ -1044,11 +1044,9 @@
         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.
@@ -3829,7 +3827,7 @@
 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;
@@ -3837,7 +3835,13 @@
     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() */
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -596,6 +596,8 @@
         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

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list