Issue #19722: Added opcode.stack_effect(), which accurately
computes the stack effect of bytecode instructions.
This commit is contained in:
parent
8d0d369067
commit
3a9079742f
|
@ -217,6 +217,13 @@ object isn't useful:
|
||||||
Detect all offsets in the code object *code* which are jump targets, and
|
Detect all offsets in the code object *code* which are jump targets, and
|
||||||
return a list of these offsets.
|
return a list of these offsets.
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: stack_effect(opcode, [oparg])
|
||||||
|
|
||||||
|
Compute the stack effect of *opcode* with argument *oparg*.
|
||||||
|
|
||||||
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
.. _bytecodes:
|
.. _bytecodes:
|
||||||
|
|
||||||
Python Bytecode Instructions
|
Python Bytecode Instructions
|
||||||
|
|
|
@ -54,6 +54,9 @@ PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromASTObject(
|
||||||
/* _Py_Mangle is defined in compile.c */
|
/* _Py_Mangle is defined in compile.c */
|
||||||
PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -8,6 +8,19 @@ __all__ = ["cmp_op", "hasconst", "hasname", "hasjrel", "hasjabs",
|
||||||
"haslocal", "hascompare", "hasfree", "opname", "opmap",
|
"haslocal", "hascompare", "hasfree", "opname", "opmap",
|
||||||
"HAVE_ARGUMENT", "EXTENDED_ARG", "hasnargs"]
|
"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',
|
cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is',
|
||||||
'is not', 'exception match', 'BAD')
|
'is not', 'exception match', 'BAD')
|
||||||
|
|
||||||
|
|
|
@ -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()
|
|
@ -67,6 +67,8 @@ Core and Builtins
|
||||||
|
|
||||||
Library
|
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
|
- Issue #19735: Implement private function ssl._create_stdlib_context() to
|
||||||
create SSLContext objects in Python's stdlib module. It provides a single
|
create SSLContext objects in Python's stdlib module. It provides a single
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -853,8 +853,8 @@ compiler_set_lineno(struct compiler *c, int off)
|
||||||
b->b_instr[off].i_lineno = c->u->u_lineno;
|
b->b_instr[off].i_lineno = c->u->u_lineno;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
opcode_stack_effect(int opcode, int oparg)
|
PyCompile_OpcodeStackEffect(int opcode, int oparg)
|
||||||
{
|
{
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case POP_TOP:
|
case POP_TOP:
|
||||||
|
@ -1044,11 +1044,9 @@ opcode_stack_effect(int opcode, int oparg)
|
||||||
case DELETE_DEREF:
|
case DELETE_DEREF:
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "opcode = %d\n", opcode);
|
return PY_INVALID_STACK_EFFECT;
|
||||||
Py_FatalError("opcode_stack_effect()");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return 0; /* not reachable */
|
return PY_INVALID_STACK_EFFECT; /* not reachable */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add an opcode with no argument.
|
/* Add an opcode with no argument.
|
||||||
|
@ -3829,7 +3827,7 @@ dfs(struct compiler *c, basicblock *b, struct assembler *a)
|
||||||
static int
|
static int
|
||||||
stackdepth_walk(struct compiler *c, basicblock *b, int depth, int maxdepth)
|
stackdepth_walk(struct compiler *c, basicblock *b, int depth, int maxdepth)
|
||||||
{
|
{
|
||||||
int i, target_depth;
|
int i, target_depth, effect;
|
||||||
struct instr *instr;
|
struct instr *instr;
|
||||||
if (b->b_seen || b->b_startdepth >= depth)
|
if (b->b_seen || b->b_startdepth >= depth)
|
||||||
return maxdepth;
|
return maxdepth;
|
||||||
|
@ -3837,7 +3835,13 @@ stackdepth_walk(struct compiler *c, basicblock *b, int depth, int maxdepth)
|
||||||
b->b_startdepth = depth;
|
b->b_startdepth = depth;
|
||||||
for (i = 0; i < b->b_iused; i++) {
|
for (i = 0; i < b->b_iused; i++) {
|
||||||
instr = &b->b_instr[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)
|
if (depth > maxdepth)
|
||||||
maxdepth = depth;
|
maxdepth = depth;
|
||||||
assert(depth >= 0); /* invalid code or bug in stackdepth() */
|
assert(depth >= 0); /* invalid code or bug in stackdepth() */
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -596,6 +596,8 @@ class PyBuildExt(build_ext):
|
||||||
exts.append( Extension('_lsprof', ['_lsprof.c', 'rotatingtree.c']) )
|
exts.append( Extension('_lsprof', ['_lsprof.c', 'rotatingtree.c']) )
|
||||||
# static Unicode character database
|
# static Unicode character database
|
||||||
exts.append( Extension('unicodedata', ['unicodedata.c']) )
|
exts.append( Extension('unicodedata', ['unicodedata.c']) )
|
||||||
|
# _opcode module
|
||||||
|
exts.append( Extension('_opcode', ['_opcode.c']) )
|
||||||
|
|
||||||
# Modules with some UNIX dependencies -- on by default:
|
# Modules with some UNIX dependencies -- on by default:
|
||||||
# (If you have a really backward UNIX, select and socket may not be
|
# (If you have a really backward UNIX, select and socket may not be
|
||||||
|
|
Loading…
Reference in New Issue