Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 876b2f2

Browse files
committed
Merged revisions 72912,72920,72940 via svnmerge from
svn+ssh://[email protected]/python/trunk ........ r72912 | benjamin.peterson | 2009-05-25 08:13:44 -0500 (Mon, 25 May 2009) | 5 lines add a SETUP_WITH opcode It speeds up the with statement and correctly looks up the special methods involved. ........ r72920 | benjamin.peterson | 2009-05-25 15:12:57 -0500 (Mon, 25 May 2009) | 1 line take into account the fact that SETUP_WITH pushes a finally block ........ r72940 | benjamin.peterson | 2009-05-26 07:49:59 -0500 (Tue, 26 May 2009) | 1 line teach the peepholer about SETUP_WITH ........
1 parent d239775 commit 876b2f2

10 files changed

Lines changed: 105 additions & 84 deletions

File tree

Doc/library/dis.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,18 @@ the stack so that it is available for further iterations of the loop.
436436
by ``CALL_FUNCTION`` to construct a class.
437437

438438

439+
.. opcode:: SETUP_WITH (delta)
440+
441+
This opcode performs several operations before a with block starts. First,
442+
it loads :meth:`~object.__exit__` from the context manager and pushes it onto
443+
the stack for later use by :opcode:`WITH_CLEANUP`. Then,
444+
:meth:`~object.__enter__` is called, and a finally block pointing to *delta*
445+
is pushed. Finally, the result of calling the enter method is pushed onto
446+
the stack. The next opcode will either ignore it (:opcode:`POP_TOP`), or
447+
store it in (a) variable(s) (:opcode:`STORE_FAST`, :opcode:`STORE_NAME`, or
448+
:opcode:`UNPACK_SEQUENCE`).
449+
450+
439451
.. opcode:: WITH_CLEANUP ()
440452

441453
Cleans up the stack when a :keyword:`with` statement block exits. TOS is

Doc/reference/compound_stmts.rst

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,8 @@ The execution of the :keyword:`with` statement with one "item" proceeds as follo
354354

355355
#. The context expression is evaluated to obtain a context manager.
356356

357+
#. The context manager's :meth:`__exit__` is loaded for later use.
358+
357359
#. The context manager's :meth:`__enter__` method is invoked.
358360

359361
#. If a target was included in the :keyword:`with` statement, the return value
@@ -363,9 +365,9 @@ The execution of the :keyword:`with` statement with one "item" proceeds as follo
363365

364366
The :keyword:`with` statement guarantees that if the :meth:`__enter__`
365367
method returns without an error, then :meth:`__exit__` will always be
366-
called. Thus, if an error occurs during the assignment to the target
367-
list, it will be treated the same as an error occurring within the suite
368-
would be. See step 5 below.
368+
called. Thus, if an error occurs during the assignment to the target list,
369+
it will be treated the same as an error occurring within the suite would
370+
be. See step 6 below.
369371

370372
#. The suite is executed.
371373

Include/opcode.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,10 @@ extern "C" {
130130
#define CALL_FUNCTION_KW 141 /* #args + (#kwargs<<8) */
131131
#define CALL_FUNCTION_VAR_KW 142 /* #args + (#kwargs<<8) */
132132

133+
#define SETUP_WITH 143
134+
133135
/* Support for opargs more than 16 bits long */
134-
#define EXTENDED_ARG 143
136+
#define EXTENDED_ARG 144
135137

136138
#define LIST_APPEND 145
137139
#define SET_ADD 146

Lib/opcode.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,14 @@ def jabs_op(name, op):
166166
def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8)
167167
def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8)
168168
def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8)
169-
def_op('EXTENDED_ARG', 143)
170-
EXTENDED_ARG = 143
169+
170+
jrel_op('SETUP_WITH', 143)
171171

172172
def_op('LIST_APPEND', 145)
173173
def_op('SET_ADD', 146)
174174
def_op('MAP_ADD', 147)
175175

176+
def_op('EXTENDED_ARG', 144)
177+
EXTENDED_ARG = 144
176178

177179
del def_op, name_op, jrel_op, jabs_op

Lib/test/test_descr.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,6 +1569,7 @@ class DictSub(checker.__class__, dict):
15691569
def some_number(self_, key):
15701570
self.assertEqual(key, "hi")
15711571
return 4
1572+
def swallow(*args): pass
15721573

15731574
# It would be nice to have every special method tested here, but I'm
15741575
# only listing the ones I can remember outside of typeobject.c, since it
@@ -1584,11 +1585,8 @@ def some_number(self_, key):
15841585
set(("__class__",)), {}),
15851586
("__subclasscheck__", do_issubclass, return_true,
15861587
set(("__bases__",)), {}),
1587-
# These two fail because the compiler generates LOAD_ATTR to look
1588-
# them up. We'd have to add a new opcode to fix this, and it's
1589-
# probably not worth it.
1590-
# ("__enter__", run_context, iden),
1591-
# ("__exit__", run_context, iden),
1588+
("__enter__", run_context, iden, set(), {"__exit__" : swallow}),
1589+
("__exit__", run_context, swallow, set(), {"__enter__" : iden}),
15921590
]
15931591

15941592
class Checker(object):

Python/ceval.c

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ static int import_all_from(PyObject *, PyObject *);
119119
static void format_exc_check_arg(PyObject *, const char *, PyObject *);
120120
static PyObject * unicode_concatenate(PyObject *, PyObject *,
121121
PyFrameObject *, unsigned char *);
122+
static PyObject * special_lookup(PyObject *, char *, PyObject **);
122123

123124
#define NAME_ERROR_MSG \
124125
"name '%.200s' is not defined"
@@ -2455,6 +2456,33 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
24552456
STACK_LEVEL());
24562457
DISPATCH();
24572458

2459+
TARGET(SETUP_WITH)
2460+
{
2461+
static PyObject *exit, *enter;
2462+
w = TOP();
2463+
x = special_lookup(w, "__exit__", &exit);
2464+
if (!x)
2465+
break;
2466+
SET_TOP(x);
2467+
u = special_lookup(w, "__enter__", &enter);
2468+
Py_DECREF(w);
2469+
if (!u) {
2470+
x = NULL;
2471+
break;
2472+
}
2473+
x = PyObject_CallFunctionObjArgs(u, NULL);
2474+
Py_DECREF(u);
2475+
if (!x)
2476+
break;
2477+
/* Setup the finally block before pushing the result
2478+
of __enter__ on the stack. */
2479+
PyFrame_BlockSetup(f, SETUP_FINALLY, INSTR_OFFSET() + oparg,
2480+
STACK_LEVEL());
2481+
2482+
PUSH(x);
2483+
DISPATCH();
2484+
}
2485+
24582486
TARGET(WITH_CLEANUP)
24592487
{
24602488
/* At the top of the stack are 1-3 values indicating
@@ -2479,17 +2507,36 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
24792507
should still be resumed.)
24802508
*/
24812509

2482-
PyObject *exit_func = POP();
2510+
PyObject *exit_func;
24832511
u = TOP();
24842512
if (u == Py_None) {
2513+
POP();
2514+
exit_func = TOP();
2515+
SET_TOP(u);
24852516
v = w = Py_None;
24862517
}
24872518
else if (PyLong_Check(u)) {
2519+
POP();
2520+
switch(PyLong_AsLong(u)) {
2521+
case WHY_RETURN:
2522+
case WHY_CONTINUE:
2523+
/* Retval in TOP. */
2524+
exit_func = SECOND();
2525+
SET_SECOND(TOP());
2526+
SET_TOP(u);
2527+
break;
2528+
default:
2529+
exit_func = TOP();
2530+
SET_TOP(u);
2531+
break;
2532+
}
24882533
u = v = w = Py_None;
24892534
}
24902535
else {
2491-
v = SECOND();
2536+
v = SECOND();
24922537
w = THIRD();
2538+
exit_func = stack_pointer[-7];
2539+
stack_pointer[-7] = NULL;
24932540
}
24942541
/* XXX Not the fastest way to call it... */
24952542
x = PyObject_CallFunctionObjArgs(exit_func, u, v, w,
@@ -2509,11 +2556,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
25092556
else if (err > 0) {
25102557
err = 0;
25112558
/* There was an exception and a True return */
2512-
STACKADJ(-2);
2513-
SET_TOP(PyLong_FromLong((long) WHY_SILENCED));
2514-
Py_DECREF(u);
2515-
Py_DECREF(v);
2516-
Py_DECREF(w);
2559+
PUSH(PyLong_FromLong((long) WHY_SILENCED));
25172560
}
25182561
PREDICT(END_FINALLY);
25192562
break;
@@ -3194,6 +3237,19 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
31943237
}
31953238

31963239

3240+
static PyObject *
3241+
special_lookup(PyObject *o, char *meth, PyObject **cache)
3242+
{
3243+
PyObject *res;
3244+
res = _PyObject_LookupSpecial(o, meth, cache);
3245+
if (res == NULL && !PyErr_Occurred()) {
3246+
PyErr_SetObject(PyExc_AttributeError, *cache);
3247+
return NULL;
3248+
}
3249+
return res;
3250+
}
3251+
3252+
31973253
/* Logic for the raise statement (too complicated for inlining).
31983254
This *consumes* a reference count to each of its arguments. */
31993255
static enum why_code

Python/compile.c

Lines changed: 9 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,8 @@ opcode_stack_effect(int opcode, int oparg)
761761
return -1;
762762
case BREAK_LOOP:
763763
return 0;
764+
case SETUP_WITH:
765+
return 7;
764766
case WITH_CLEANUP:
765767
return -1; /* XXX Sometimes more */
766768
case STORE_LOCALS:
@@ -3085,85 +3087,31 @@ expr_constant(expr_ty e)
30853087
static int
30863088
compiler_with(struct compiler *c, stmt_ty s)
30873089
{
3088-
static identifier enter_attr, exit_attr;
30893090
basicblock *block, *finally;
3090-
identifier tmpvalue = NULL, tmpexit = NULL;
30913091

30923092
assert(s->kind == With_kind);
30933093

3094-
if (!enter_attr) {
3095-
enter_attr = PyUnicode_InternFromString("__enter__");
3096-
if (!enter_attr)
3097-
return 0;
3098-
}
3099-
if (!exit_attr) {
3100-
exit_attr = PyUnicode_InternFromString("__exit__");
3101-
if (!exit_attr)
3102-
return 0;
3103-
}
3104-
31053094
block = compiler_new_block(c);
31063095
finally = compiler_new_block(c);
31073096
if (!block || !finally)
31083097
return 0;
31093098

3110-
if (s->v.With.optional_vars) {
3111-
/* Create a temporary variable to hold context.__enter__().
3112-
We need to do this rather than preserving it on the stack
3113-
because SETUP_FINALLY remembers the stack level.
3114-
We need to do the assignment *inside* the try/finally
3115-
so that context.__exit__() is called when the assignment
3116-
fails. But we need to call context.__enter__() *before*
3117-
the try/finally so that if it fails we won't call
3118-
context.__exit__().
3119-
*/
3120-
tmpvalue = compiler_new_tmpname(c);
3121-
if (tmpvalue == NULL)
3122-
return 0;
3123-
PyArena_AddPyObject(c->c_arena, tmpvalue);
3124-
}
3125-
tmpexit = compiler_new_tmpname(c);
3126-
if (tmpexit == NULL)
3127-
return 0;
3128-
PyArena_AddPyObject(c->c_arena, tmpexit);
3129-
31303099
/* Evaluate EXPR */
31313100
VISIT(c, expr, s->v.With.context_expr);
3101+
ADDOP_JREL(c, SETUP_WITH, finally);
31323102

3133-
/* Squirrel away context.__exit__ by stuffing it under context */
3134-
ADDOP(c, DUP_TOP);
3135-
ADDOP_O(c, LOAD_ATTR, exit_attr, names);
3136-
if (!compiler_nameop(c, tmpexit, Store))
3137-
return 0;
3138-
3139-
/* Call context.__enter__() */
3140-
ADDOP_O(c, LOAD_ATTR, enter_attr, names);
3141-
ADDOP_I(c, CALL_FUNCTION, 0);
3142-
3143-
if (s->v.With.optional_vars) {
3144-
/* Store it in tmpvalue */
3145-
if (!compiler_nameop(c, tmpvalue, Store))
3146-
return 0;
3147-
}
3148-
else {
3149-
/* Discard result from context.__enter__() */
3150-
ADDOP(c, POP_TOP);
3151-
}
3152-
3153-
/* Start the try block */
3154-
ADDOP_JREL(c, SETUP_FINALLY, finally);
3155-
3103+
/* SETUP_WITH pushes a finally block. */
31563104
compiler_use_next_block(c, block);
31573105
if (!compiler_push_fblock(c, FINALLY_TRY, block)) {
31583106
return 0;
31593107
}
31603108

31613109
if (s->v.With.optional_vars) {
3162-
/* Bind saved result of context.__enter__() to VAR */
3163-
if (!compiler_nameop(c, tmpvalue, Load) ||
3164-
!compiler_nameop(c, tmpvalue, Del))
3165-
return 0;
3166-
VISIT(c, expr, s->v.With.optional_vars);
3110+
VISIT(c, expr, s->v.With.optional_vars);
3111+
}
3112+
else {
3113+
/* Discard result from context.__enter__() */
3114+
ADDOP(c, POP_TOP);
31673115
}
31683116

31693117
/* BLOCK code */
@@ -3181,9 +3129,6 @@ compiler_with(struct compiler *c, stmt_ty s)
31813129
/* Finally block starts; context.__exit__ is on the stack under
31823130
the exception or return information. Just issue our magic
31833131
opcode. */
3184-
if (!compiler_nameop(c, tmpexit, Load) ||
3185-
!compiler_nameop(c, tmpexit, Del))
3186-
return 0;
31873132
ADDOP(c, WITH_CLEANUP);
31883133

31893134
/* Finally block ends. */

Python/import.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,10 @@ typedef unsigned short mode_t;
8989
change LIST_APPEND and SET_ADD, add MAP_ADD)
9090
Python 3.1a0: 3150 (optimize conditional branches:
9191
introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE)
92+
Python 3.2a0: 3160 (add SETUP_WITH)
9293
*/
93-
#define MAGIC (3150 | ((long)'\r'<<16) | ((long)'\n'<<24))
9494

95+
#define MAGIC (3160 | ((long)'\r'<<16) | ((long)'\n'<<24))
9596
/* Magic word as global; note that _PyImport_Init() can change the
9697
value of this global to accommodate for alterations of how the
9798
compiler works which are enabled by command line switches. */

Python/opcode_targets.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ static void *opcode_targets[256] = {
142142
&&TARGET_CALL_FUNCTION_VAR,
143143
&&TARGET_CALL_FUNCTION_KW,
144144
&&TARGET_CALL_FUNCTION_VAR_KW,
145+
&&TARGET_SETUP_WITH,
145146
&&TARGET_EXTENDED_ARG,
146-
&&_unknown_opcode,
147147
&&TARGET_LIST_APPEND,
148148
&&TARGET_SET_ADD,
149149
&&TARGET_MAP_ADD,

Python/peephole.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ markblocks(unsigned char *code, Py_ssize_t len)
251251
case SETUP_LOOP:
252252
case SETUP_EXCEPT:
253253
case SETUP_FINALLY:
254+
case SETUP_WITH:
254255
j = GETJUMPTGT(code, i);
255256
blocks[j] = 1;
256257
break;
@@ -566,6 +567,7 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
566567
case SETUP_LOOP:
567568
case SETUP_EXCEPT:
568569
case SETUP_FINALLY:
570+
case SETUP_WITH:
569571
tgt = GETJUMPTGT(codestr, i);
570572
/* Replace JUMP_* to a RETURN into just a RETURN */
571573
if (UNCONDITIONAL_JUMP(opcode) &&
@@ -648,6 +650,7 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
648650
case SETUP_LOOP:
649651
case SETUP_EXCEPT:
650652
case SETUP_FINALLY:
653+
case SETUP_WITH:
651654
j = addrmap[GETARG(codestr, i) + i + 3] - addrmap[i] - 3;
652655
SETARG(codestr, i, j);
653656
break;

0 commit comments

Comments
 (0)