Kaydet (Commit) 0240b92a authored tarafından Guido van Rossum's avatar Guido van Rossum

Two more patches by Tony Lownds (SF# 1607548).

(1)
Combines the code paths for MAKE_FUNCTION and MAKE_CLOSURE.
Fixes a crash where functions with closures and either annotations or
keyword-only arguments result in MAKE_CLOSURE, but only
MAKE_FUNCTION has the code to handle annotations or keyword-only
arguments.
Includes enough tests to trigger the bug.

(2)
Change peepholer to not bail in the presence of EXTENDED_ARG +
MAKE_FUNCTION.
Enforce the natural 16-bit limit of annotations in compile.c.

Also update Misc/NEWS with the "input = raw_input" change.
üst f74225d6
...@@ -393,6 +393,19 @@ if 1: ...@@ -393,6 +393,19 @@ if 1:
del d[..., ...] del d[..., ...]
self.assertEqual((Ellipsis, Ellipsis) in d, False) self.assertEqual((Ellipsis, Ellipsis) in d, False)
def test_annotation_limit(self):
# 16 bits are available for # of annotations, and the
# tuple of annotations names is counted, hence 65534
# is the max. Ensure the result of too many annotations is a
# SyntaxError.
s = "def f((%s)): pass"
s %= ', '.join('a%d:%d' % (i,i) for i in xrange(65535))
self.assertRaises(SyntaxError, compile, s, '?', 'exec')
# Test that the max # of annotations compiles.
s = "def f((%s)): pass"
s %= ', '.join('a%d:%d' % (i,i) for i in xrange(65534))
compile(s, '?', 'exec')
def test_main(): def test_main():
test_support.run_unittest(TestSpecifics) test_support.run_unittest(TestSpecifics)
......
...@@ -321,6 +321,13 @@ class GrammarTests(unittest.TestCase): ...@@ -321,6 +321,13 @@ class GrammarTests(unittest.TestCase):
self.assertEquals(f.__annotations__, self.assertEquals(f.__annotations__,
{'b': 1, 'c': 2, 'e': 3, 'g': 6, 'h': 7, 'j': 9, {'b': 1, 'c': 2, 'e': 3, 'g': 6, 'h': 7, 'j': 9,
'k': 11, 'return': 12}) 'k': 11, 'return': 12})
# test MAKE_CLOSURE with a variety of oparg's
closure = 1
def f(): return closure
def f(x=1): return closure
def f(*, k=1): return closure
def f() -> int: return closure
def testLambdef(self): def testLambdef(self):
### lambdef: 'lambda' [varargslist] ':' test ### lambdef: 'lambda' [varargslist] ':' test
......
...@@ -195,6 +195,14 @@ class TestTranforms(unittest.TestCase): ...@@ -195,6 +195,14 @@ class TestTranforms(unittest.TestCase):
# There should be one jump for the while loop. # There should be one jump for the while loop.
self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1) self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1)
self.assertEqual(asm.split().count('RETURN_VALUE'), 2) self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
def test_make_function_doesnt_bail(self):
def f():
def g()->1+1:
pass
return g
asm = disassemble(f)
self.assert_('BINARY_ADD' not in asm)
def test_main(verbose=None): def test_main(verbose=None):
......
...@@ -28,6 +28,10 @@ TO DO ...@@ -28,6 +28,10 @@ TO DO
Core and Builtins Core and Builtins
----------------- -----------------
- input() becomes raw_input(): the name input() now implements the
functionality formerly known as raw_input(); the name raw_input()
is no longer defined.
- Objects listed in an 'except' clause must inherit from BaseException. - Objects listed in an 'except' clause must inherit from BaseException.
- PEP 3106: dict.iterkeys(), .iteritems(), .itervalues() are now gone; - PEP 3106: dict.iterkeys(), .iteritems(), .itervalues() are now gone;
...@@ -82,7 +86,7 @@ Core and Builtins ...@@ -82,7 +86,7 @@ Core and Builtins
backticks (`x`), <> backticks (`x`), <>
- Removed these Python builtins: - Removed these Python builtins:
apply(), coerce(), input(), raw_input() apply(), coerce()
- Removed these Python methods: - Removed these Python methods:
{}.has_key {}.has_key
......
...@@ -2236,6 +2236,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) ...@@ -2236,6 +2236,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
break; break;
} }
case MAKE_CLOSURE:
case MAKE_FUNCTION: case MAKE_FUNCTION:
{ {
int posdefaults = oparg & 0xff; int posdefaults = oparg & 0xff;
...@@ -2245,6 +2246,12 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) ...@@ -2245,6 +2246,12 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
v = POP(); /* code object */ v = POP(); /* code object */
x = PyFunction_New(v, f->f_globals); x = PyFunction_New(v, f->f_globals);
Py_DECREF(v); Py_DECREF(v);
if (x != NULL && opcode == MAKE_CLOSURE) {
v = POP();
err = PyFunction_SetClosure(x, v);
Py_DECREF(v);
}
if (x != NULL && num_annotations > 0) { if (x != NULL && num_annotations > 0) {
Py_ssize_t name_ix; Py_ssize_t name_ix;
...@@ -2308,34 +2315,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) ...@@ -2308,34 +2315,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
break; break;
} }
case MAKE_CLOSURE:
{
v = POP(); /* code object */
x = PyFunction_New(v, f->f_globals);
Py_DECREF(v);
if (x != NULL) {
v = POP();
err = PyFunction_SetClosure(x, v);
Py_DECREF(v);
}
if (x != NULL && oparg > 0) {
v = PyTuple_New(oparg);
if (v == NULL) {
Py_DECREF(x);
x = NULL;
break;
}
while (--oparg >= 0) {
w = POP();
PyTuple_SET_ITEM(v, oparg, w);
}
err = PyFunction_SetDefaults(x, v);
Py_DECREF(v);
}
PUSH(x);
break;
}
case BUILD_SLICE: case BUILD_SLICE:
if (oparg == 3) if (oparg == 3)
w = POP(); w = POP();
......
...@@ -836,6 +836,8 @@ opcode_stack_effect(int opcode, int oparg) ...@@ -836,6 +836,8 @@ opcode_stack_effect(int opcode, int oparg)
return -NARGS(oparg)-2; return -NARGS(oparg)-2;
case MAKE_FUNCTION: case MAKE_FUNCTION:
return -NARGS(oparg) - ((oparg >> 16) & 0xffff); return -NARGS(oparg) - ((oparg >> 16) & 0xffff);
case MAKE_CLOSURE:
return -1 - NARGS(oparg) - ((oparg >> 16) & 0xffff);
#undef NARGS #undef NARGS
case BUILD_SLICE: case BUILD_SLICE:
if (oparg == 3) if (oparg == 3)
...@@ -843,8 +845,6 @@ opcode_stack_effect(int opcode, int oparg) ...@@ -843,8 +845,6 @@ opcode_stack_effect(int opcode, int oparg)
else else
return -1; return -1;
case MAKE_CLOSURE:
return -oparg;
case LOAD_CLOSURE: case LOAD_CLOSURE:
return 1; return 1;
case LOAD_DEREF: case LOAD_DEREF:
...@@ -1367,8 +1367,12 @@ static int ...@@ -1367,8 +1367,12 @@ static int
compiler_visit_annotations(struct compiler *c, arguments_ty args, compiler_visit_annotations(struct compiler *c, arguments_ty args,
expr_ty returns) expr_ty returns)
{ {
/* push arg annotations and a list of the argument names. return the # /* Push arg annotations and a list of the argument names. Return the #
of items pushed. this is out-of-order wrt the source code. */ of items pushed. The expressions are evaluated out-of-order wrt the
source code.
More than 2^16-1 annotations is a SyntaxError. Returns -1 on error.
*/
static identifier return_str; static identifier return_str;
PyObject *names; PyObject *names;
int len; int len;
...@@ -1399,6 +1403,12 @@ compiler_visit_annotations(struct compiler *c, arguments_ty args, ...@@ -1399,6 +1403,12 @@ compiler_visit_annotations(struct compiler *c, arguments_ty args,
} }
len = PyList_GET_SIZE(names); len = PyList_GET_SIZE(names);
if (len > 65534) {
/* len must fit in 16 bits, and len is incremented below */
PyErr_SetString(PyExc_SyntaxError,
"too many annotations");
goto error;
}
if (len) { if (len) {
/* convert names to a tuple and place on stack */ /* convert names to a tuple and place on stack */
PyObject *elt; PyObject *elt;
...@@ -1449,6 +1459,9 @@ compiler_function(struct compiler *c, stmt_ty s) ...@@ -1449,6 +1459,9 @@ compiler_function(struct compiler *c, stmt_ty s)
if (args->defaults) if (args->defaults)
VISIT_SEQ(c, expr, args->defaults); VISIT_SEQ(c, expr, args->defaults);
num_annotations = compiler_visit_annotations(c, args, returns); num_annotations = compiler_visit_annotations(c, args, returns);
if (num_annotations < 0)
return 0;
assert((num_annotations & 0xFFFF) == num_annotations);
if (!compiler_enter_scope(c, s->v.FunctionDef.name, (void *)s, if (!compiler_enter_scope(c, s->v.FunctionDef.name, (void *)s,
s->lineno)) s->lineno))
......
...@@ -261,10 +261,12 @@ markblocks(unsigned char *code, int len) ...@@ -261,10 +261,12 @@ markblocks(unsigned char *code, int len)
The consts object should still be in list form to allow new constants The consts object should still be in list form to allow new constants
to be appended. to be appended.
To keep the optimizer simple, it bails out (does nothing) for code To keep the optimizer simple, it bails out (does nothing) for code that
containing extended arguments or that has a length over 32,700. That has a length over 32,700, and does not calculate extended arguments.
allows us to avoid overflow and sign issues. Likewise, it bails when That allows us to avoid overflow and sign issues. Likewise, it bails when
the lineno table has complex encoding for gaps >= 255. the lineno table has complex encoding for gaps >= 255. EXTENDED_ARG can
appear before MAKE_FUNCTION; in this case both opcodes are skipped.
EXTENDED_ARG preceding any other opcode causes the optimizer to bail.
Optimizations are restricted to simple transformations occuring within a Optimizations are restricted to simple transformations occuring within a
single basic block. All transformations keep the code size the same or single basic block. All transformations keep the code size the same or
...@@ -535,7 +537,11 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names, ...@@ -535,7 +537,11 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
break; break;
case EXTENDED_ARG: case EXTENDED_ARG:
goto exitUnchanged; if (codestr[i+3] != MAKE_FUNCTION)
goto exitUnchanged;
/* don't visit MAKE_FUNCTION as GETARG will be wrong */
i += 3;
break;
/* Replace RETURN LOAD_CONST None RETURN with just RETURN */ /* Replace RETURN LOAD_CONST None RETURN with just RETURN */
/* Remove unreachable JUMPs after RETURN */ /* Remove unreachable JUMPs after RETURN */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment