add a SETUP_WITH opcode

It speeds up the with statement and correctly looks up the special
methods involved.
diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index 6b7ed1e..9bd65dd 100644
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -532,6 +532,18 @@
    the names of the base classes, and TOS2 the class name.
 
 
+.. opcode:: SETUP_WITH (delta)
+
+   This opcode performs several operations before a with block starts.  First,
+   it loads :meth:`~object.__exit__` from the context manager and pushes it onto
+   the stack for later use by :opcode:`WITH_CLEANUP`.  Then,
+   :meth:`~object.__enter__` is called, and a finally block pointing to *delta*
+   is pushed.  Finally, the result of calling the enter method is pushed onto
+   the stack.  The next opcode will either ignore it (:opcode:`POP_TOP`), or
+   store it in (a) variable(s) (:opcode:`STORE_FAST`, :opcode:`STORE_NAME`, or
+   :opcode:`UNPACK_SEQUENCE`).
+
+
 .. opcode:: WITH_CLEANUP ()
 
    Cleans up the stack when a :keyword:`with` statement block exits.  On top of
diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst
index 3c716b2..afb7ebc 100644
--- a/Doc/reference/compound_stmts.rst
+++ b/Doc/reference/compound_stmts.rst
@@ -339,6 +339,8 @@
 
 #. The context expression is evaluated to obtain a context manager.
 
+#. The context manager's :meth:`__exit__` is loaded for later use.
+
 #. The context manager's :meth:`__enter__` method is invoked.
 
 #. If a target was included in the :keyword:`with` statement, the return value
@@ -349,7 +351,7 @@
       The :keyword:`with` statement guarantees that if the :meth:`__enter__` method
       returns without an error, then :meth:`__exit__` will always be called. Thus, if
       an error occurs during the assignment to the target list, it will be treated the
-      same as an error occurring within the suite would be. See step 5 below.
+      same as an error occurring within the suite would be. See step 6 below.
 
 #. The suite is executed.
 
diff --git a/Include/opcode.h b/Include/opcode.h
index 36a35dc..9739782 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -141,8 +141,10 @@
 #define CALL_FUNCTION_KW           141	/* #args + (#kwargs<<8) */
 #define CALL_FUNCTION_VAR_KW       142	/* #args + (#kwargs<<8) */
 
+#define SETUP_WITH 143
+
 /* Support for opargs more than 16 bits long */
-#define EXTENDED_ARG  143
+#define EXTENDED_ARG  145
 
 
 enum cmp_op {PyCmp_LT=Py_LT, PyCmp_LE=Py_LE, PyCmp_EQ=Py_EQ, PyCmp_NE=Py_NE, PyCmp_GT=Py_GT, PyCmp_GE=Py_GE,
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 24d5dcc..7c3962d 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -181,7 +181,10 @@
 def_op('CALL_FUNCTION_VAR', 140)     # #args + (#kwargs << 8)
 def_op('CALL_FUNCTION_KW', 141)      # #args + (#kwargs << 8)
 def_op('CALL_FUNCTION_VAR_KW', 142)  # #args + (#kwargs << 8)
-def_op('EXTENDED_ARG', 143)
-EXTENDED_ARG = 143
+
+jrel_op('SETUP_WITH', 143)
+
+def_op('EXTENDED_ARG', 145)
+EXTENDED_ARG = 145
 
 del def_op, name_op, jrel_op, jabs_op
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py
index c7989b1..627f8aa 100644
--- a/Lib/test/test_descr.py
+++ b/Lib/test/test_descr.py
@@ -1689,6 +1689,7 @@
             return isinstance(int, obj)
         def do_issubclass(obj):
             return issubclass(int, obj)
+        def swallow(*args): pass
 
         # It would be nice to have every special method tested here, but I'm
         # only listing the ones I can remember outside of typeobject.c, since it
@@ -1702,11 +1703,8 @@
             ("__instancecheck__", do_isinstance, return_true, set(), {}),
             ("__subclasscheck__", do_issubclass, return_true,
              set(("__bases__",)), {}),
-            # These two fail because the compiler generates LOAD_ATTR to look
-            # them up.  We'd have to add a new opcode to fix this, and it's
-            # probably not worth it.
-            # ("__enter__", run_context, iden),
-            # ("__exit__", run_context, iden),
+            ("__enter__", run_context, iden, set(), {"__exit__" : swallow}),
+            ("__exit__", run_context, swallow, set(), {"__enter__" : iden}),
             ]
 
         class Checker(object):
diff --git a/Misc/NEWS b/Misc/NEWS
index 5526d9c..a6b5fce 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@
 Core and Builtins
 -----------------
 
+- Issue #6101: A new opcode, SETUP_WITH, has been added to speed up the with
+  statement and correctly lookup the __enter__ and __exit__ special methods.
+
 - Issue #5829: complex("1e500") no longer raises OverflowError.  This
   makes it consistent with float("1e500") and interpretation of real
   and imaginary literals.
diff --git a/Python/ceval.c b/Python/ceval.c
index 4f0877b..92021e6 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -128,6 +128,7 @@
 static PyObject * string_concatenate(PyObject *, PyObject *,
 				    PyFrameObject *, unsigned char *);
 static PyObject * kwd_as_string(PyObject *);
+static PyObject * special_lookup(PyObject *, char *, PyObject **);
 
 #define NAME_ERROR_MSG \
 	"name '%.200s' is not defined"
@@ -2467,6 +2468,33 @@
 					   STACK_LEVEL());
 			continue;
 
+		case SETUP_WITH:
+                {
+			static PyObject *exit, *enter;
+			w = TOP();
+			x = special_lookup(w, "__exit__", &exit);
+			if (!x)
+				break;
+			SET_TOP(x);
+		        u = special_lookup(w, "__enter__", &enter);
+			Py_DECREF(w);
+			if (!u) {
+				x = NULL;
+				break;
+			}
+			x = PyObject_CallFunctionObjArgs(u, NULL);
+			Py_DECREF(u);
+			if (!x)
+				break;
+			/* Setup the finally block before pushing the result
+			   of __enter__ on the stack. */
+			PyFrame_BlockSetup(f, SETUP_FINALLY, INSTR_OFFSET() + oparg,
+					   STACK_LEVEL());
+
+			PUSH(x);
+			continue;
+		}
+
 		case WITH_CLEANUP:
 		{
 			/* At the top of the stack are 1-3 values indicating
@@ -3171,6 +3199,24 @@
 }
 
 
+static PyObject *
+special_lookup(PyObject *o, char *meth, PyObject **cache)
+{
+	PyObject *res;
+	if (PyInstance_Check(o)) {
+		if (!*cache)
+			return PyObject_GetAttrString(o, meth);
+		else
+			return PyObject_GetAttr(o, *cache);
+	}
+	res = _PyObject_LookupSpecial(o, meth, cache);
+	if (res == NULL && !PyErr_Occurred()) {
+		PyErr_SetObject(PyExc_AttributeError, *cache);
+		return NULL;
+	}
+	return res;
+}
+
 
 static PyObject *
 kwd_as_string(PyObject *kwd) {
diff --git a/Python/compile.c b/Python/compile.c
index 8c85306..e263d76 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -778,6 +778,8 @@
 			return -1;
 		case BREAK_LOOP:
 			return 0;
+	        case SETUP_WITH:
+			return 1;
 		case WITH_CLEANUP:
 			return -1; /* XXX Sometimes more */
 		case LOAD_LOCALS:
@@ -2821,80 +2823,31 @@
 static int
 compiler_with(struct compiler *c, stmt_ty s)
 {
-    static identifier enter_attr, exit_attr;
     basicblock *block, *finally;
-    identifier tmpvalue = NULL;
 
     assert(s->kind == With_kind);
 
-    if (!enter_attr) {
-	enter_attr = PyString_InternFromString("__enter__");
-	if (!enter_attr)
-	    return 0;
-    }
-    if (!exit_attr) {
-	exit_attr = PyString_InternFromString("__exit__");
-	if (!exit_attr)
-	    return 0;
-    }
-
     block = compiler_new_block(c);
     finally = compiler_new_block(c);
     if (!block || !finally)
 	return 0;
 
-    if (s->v.With.optional_vars) {
-	/* Create a temporary variable to hold context.__enter__().
-	   We need to do this rather than preserving it on the stack
-	   because SETUP_FINALLY remembers the stack level.
-	   We need to do the assignment *inside* the try/finally
-	   so that context.__exit__() is called when the assignment
-	   fails.  But we need to call context.__enter__() *before*
-	   the try/finally so that if it fails we won't call
-	   context.__exit__().
-	*/
-	tmpvalue = compiler_new_tmpname(c);
-	if (tmpvalue == NULL)
-	    return 0;
-	PyArena_AddPyObject(c->c_arena, tmpvalue);
-    }
-
     /* Evaluate EXPR */
     VISIT(c, expr, s->v.With.context_expr);
+    ADDOP_JREL(c, SETUP_WITH, finally);
 
-    /* Squirrel away context.__exit__ by stuffing it under context */
-    ADDOP(c, DUP_TOP);
-    ADDOP_O(c, LOAD_ATTR, exit_attr, names);
-    ADDOP(c, ROT_TWO);
-
-    /* Call context.__enter__() */
-    ADDOP_O(c, LOAD_ATTR, enter_attr, names);
-    ADDOP_I(c, CALL_FUNCTION, 0);
-
-    if (s->v.With.optional_vars) {
-	/* Store it in tmpvalue */
-	if (!compiler_nameop(c, tmpvalue, Store))
-	    return 0;
-    }
-    else {
-	/* Discard result from context.__enter__() */
-	ADDOP(c, POP_TOP);
-    }
-
-    /* Start the try block */
-    ADDOP_JREL(c, SETUP_FINALLY, finally);
-
+    /* SETUP_WITH pushes a finally block. */
     compiler_use_next_block(c, block);
     if (!compiler_push_fblock(c, FINALLY_TRY, block)) {
 	return 0;
     }
 
     if (s->v.With.optional_vars) {
-	/* Bind saved result of context.__enter__() to VAR */
-	if (!compiler_nameop(c, tmpvalue, Load) ||
-	    !compiler_nameop(c, tmpvalue, Del))
-	  return 0;
-	VISIT(c, expr, s->v.With.optional_vars);
+        VISIT(c, expr, s->v.With.optional_vars);
+    }
+    else {
+        /* Discard result from context.__enter__() */
+        ADDOP(c, POP_TOP);
     }
 
     /* BLOCK code */
diff --git a/Python/import.c b/Python/import.c
index 5e42e5c..88aced0 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -74,9 +74,10 @@
        Python 2.7a0: 62171 (optimize list comprehensions/change LIST_APPEND)
        Python 2.7a0: 62181 (optimize conditional branches:
 			    introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE)
+       Python 2.7a0  62191 (introduce SETUP_WITH)
 .
 */
-#define MAGIC (62181 | ((long)'\r'<<16) | ((long)'\n'<<24))
+#define MAGIC (62191 | ((long)'\r'<<16) | ((long)'\n'<<24))
 
 /* Magic word as global; note that _PyImport_Init() can change the
    value of this global to accommodate for alterations of how the