[pypy-commit] pypy default: merged with pypy

l.diekmann noreply at buildbot.pypy.org
Fri Sep 23 13:15:47 CEST 2011


Author: Lukas Diekmann <lukas.diekmann at uni-duesseldorf.de>
Branch: 
Changeset: r47561:55e13d860ac0
Date: 2011-09-23 11:06 +0200
http://bitbucket.org/pypy/pypy/changeset/55e13d860ac0/

Log:	merged with pypy

diff too long, truncating to 10000 out of 10082 lines

diff --git a/lib-python/2.7/ssl.py b/lib-python/2.7/ssl.py
--- a/lib-python/2.7/ssl.py
+++ b/lib-python/2.7/ssl.py
@@ -62,7 +62,6 @@
 from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION
 from _ssl import SSLError
 from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
-from _ssl import PROTOCOL_SSLv2, PROTOCOL_SSLv3, PROTOCOL_SSLv23, PROTOCOL_TLSv1
 from _ssl import RAND_status, RAND_egd, RAND_add
 from _ssl import \
      SSL_ERROR_ZERO_RETURN, \
@@ -74,6 +73,18 @@
      SSL_ERROR_WANT_CONNECT, \
      SSL_ERROR_EOF, \
      SSL_ERROR_INVALID_ERROR_CODE
+from _ssl import PROTOCOL_SSLv3, PROTOCOL_SSLv23, PROTOCOL_TLSv1
+_PROTOCOL_NAMES = {
+    PROTOCOL_TLSv1: "TLSv1",
+    PROTOCOL_SSLv23: "SSLv23",
+    PROTOCOL_SSLv3: "SSLv3",
+}
+try:
+    from _ssl import PROTOCOL_SSLv2
+except ImportError:
+    pass
+else:
+    _PROTOCOL_NAMES[PROTOCOL_SSLv2] = "SSLv2"
 
 from socket import socket, _fileobject, _delegate_methods, error as socket_error
 from socket import getnameinfo as _getnameinfo
@@ -408,16 +419,7 @@
     return DER_cert_to_PEM_cert(dercert)
 
 def get_protocol_name(protocol_code):
-    if protocol_code == PROTOCOL_TLSv1:
-        return "TLSv1"
-    elif protocol_code == PROTOCOL_SSLv23:
-        return "SSLv23"
-    elif protocol_code == PROTOCOL_SSLv2:
-        return "SSLv2"
-    elif protocol_code == PROTOCOL_SSLv3:
-        return "SSLv3"
-    else:
-        return "<unknown>"
+    return _PROTOCOL_NAMES.get(protocol_code, '<unknown>')
 
 
 # a replacement for the old socket.ssl function
diff --git a/lib-python/2.7/test/test_ssl.py b/lib-python/2.7/test/test_ssl.py
--- a/lib-python/2.7/test/test_ssl.py
+++ b/lib-python/2.7/test/test_ssl.py
@@ -58,32 +58,35 @@
 
 # Issue #9415: Ubuntu hijacks their OpenSSL and forcefully disables SSLv2
 def skip_if_broken_ubuntu_ssl(func):
-    # We need to access the lower-level wrapper in order to create an
-    # implicit SSL context without trying to connect or listen.
-    try:
-        import _ssl
-    except ImportError:
-        # The returned function won't get executed, just ignore the error
-        pass
-    @functools.wraps(func)
-    def f(*args, **kwargs):
+    if hasattr(ssl, 'PROTOCOL_SSLv2'):
+        # We need to access the lower-level wrapper in order to create an
+        # implicit SSL context without trying to connect or listen.
         try:
-            s = socket.socket(socket.AF_INET)
-            _ssl.sslwrap(s._sock, 0, None, None,
-                         ssl.CERT_NONE, ssl.PROTOCOL_SSLv2, None, None)
-        except ssl.SSLError as e:
-            if (ssl.OPENSSL_VERSION_INFO == (0, 9, 8, 15, 15) and
-                platform.linux_distribution() == ('debian', 'squeeze/sid', '')
-                and 'Invalid SSL protocol variant specified' in str(e)):
-                raise unittest.SkipTest("Patched Ubuntu OpenSSL breaks behaviour")
-        return func(*args, **kwargs)
-    return f
+            import _ssl
+        except ImportError:
+            # The returned function won't get executed, just ignore the error
+            pass
+        @functools.wraps(func)
+        def f(*args, **kwargs):
+            try:
+                s = socket.socket(socket.AF_INET)
+                _ssl.sslwrap(s._sock, 0, None, None,
+                             ssl.CERT_NONE, ssl.PROTOCOL_SSLv2, None, None)
+            except ssl.SSLError as e:
+                if (ssl.OPENSSL_VERSION_INFO == (0, 9, 8, 15, 15) and
+                    platform.linux_distribution() == ('debian', 'squeeze/sid', '')
+                    and 'Invalid SSL protocol variant specified' in str(e)):
+                    raise unittest.SkipTest("Patched Ubuntu OpenSSL breaks behaviour")
+            return func(*args, **kwargs)
+        return f
+    else:
+        return func
 
 
 class BasicSocketTests(unittest.TestCase):
 
     def test_constants(self):
-        ssl.PROTOCOL_SSLv2
+        #ssl.PROTOCOL_SSLv2
         ssl.PROTOCOL_SSLv23
         ssl.PROTOCOL_SSLv3
         ssl.PROTOCOL_TLSv1
@@ -964,7 +967,8 @@
             try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True)
             try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL)
             try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED)
-            try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False)
+            if hasattr(ssl, 'PROTOCOL_SSLv2'):
+                try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False)
             try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, False)
             try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1, False)
 
@@ -976,7 +980,8 @@
             try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True)
             try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL)
             try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)
-            try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv2, False)
+            if hasattr(ssl, 'PROTOCOL_SSLv2'):
+                try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv2, False)
             try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv3, False)
             try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv23, False)
 
diff --git a/lib-python/modified-2.7/ssl.py b/lib-python/modified-2.7/ssl.py
--- a/lib-python/modified-2.7/ssl.py
+++ b/lib-python/modified-2.7/ssl.py
@@ -62,7 +62,6 @@
 from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION
 from _ssl import SSLError
 from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
-from _ssl import PROTOCOL_SSLv2, PROTOCOL_SSLv3, PROTOCOL_SSLv23, PROTOCOL_TLSv1
 from _ssl import RAND_status, RAND_egd, RAND_add
 from _ssl import \
      SSL_ERROR_ZERO_RETURN, \
@@ -74,6 +73,18 @@
      SSL_ERROR_WANT_CONNECT, \
      SSL_ERROR_EOF, \
      SSL_ERROR_INVALID_ERROR_CODE
+from _ssl import PROTOCOL_SSLv3, PROTOCOL_SSLv23, PROTOCOL_TLSv1
+_PROTOCOL_NAMES = {
+    PROTOCOL_TLSv1: "TLSv1",
+    PROTOCOL_SSLv23: "SSLv23",
+    PROTOCOL_SSLv3: "SSLv3",
+}
+try:
+    from _ssl import PROTOCOL_SSLv2
+except ImportError:
+    pass
+else:
+    _PROTOCOL_NAMES[PROTOCOL_SSLv2] = "SSLv2"
 
 from socket import socket, _fileobject, error as socket_error
 from socket import getnameinfo as _getnameinfo
@@ -400,16 +411,7 @@
     return DER_cert_to_PEM_cert(dercert)
 
 def get_protocol_name(protocol_code):
-    if protocol_code == PROTOCOL_TLSv1:
-        return "TLSv1"
-    elif protocol_code == PROTOCOL_SSLv23:
-        return "SSLv23"
-    elif protocol_code == PROTOCOL_SSLv2:
-        return "SSLv2"
-    elif protocol_code == PROTOCOL_SSLv3:
-        return "SSLv3"
-    else:
-        return "<unknown>"
+    return _PROTOCOL_NAMES.get(protocol_code, '<unknown>')
 
 
 # a replacement for the old socket.ssl function
diff --git a/lib-python/modified-2.7/test/test_ssl.py b/lib-python/modified-2.7/test/test_ssl.py
--- a/lib-python/modified-2.7/test/test_ssl.py
+++ b/lib-python/modified-2.7/test/test_ssl.py
@@ -58,32 +58,35 @@
 
 # Issue #9415: Ubuntu hijacks their OpenSSL and forcefully disables SSLv2
 def skip_if_broken_ubuntu_ssl(func):
-    # We need to access the lower-level wrapper in order to create an
-    # implicit SSL context without trying to connect or listen.
-    try:
-        import _ssl
-    except ImportError:
-        # The returned function won't get executed, just ignore the error
-        pass
-    @functools.wraps(func)
-    def f(*args, **kwargs):
+    if hasattr(ssl, 'PROTOCOL_SSLv2'):
+        # We need to access the lower-level wrapper in order to create an
+        # implicit SSL context without trying to connect or listen.
         try:
-            s = socket.socket(socket.AF_INET)
-            _ssl.sslwrap(s._sock, 0, None, None,
-                         ssl.CERT_NONE, ssl.PROTOCOL_SSLv2, None, None)
-        except ssl.SSLError as e:
-            if (ssl.OPENSSL_VERSION_INFO == (0, 9, 8, 15, 15) and
-                platform.linux_distribution() == ('debian', 'squeeze/sid', '')
-                and 'Invalid SSL protocol variant specified' in str(e)):
-                raise unittest.SkipTest("Patched Ubuntu OpenSSL breaks behaviour")
-        return func(*args, **kwargs)
-    return f
+            import _ssl
+        except ImportError:
+            # The returned function won't get executed, just ignore the error
+            pass
+        @functools.wraps(func)
+        def f(*args, **kwargs):
+            try:
+                s = socket.socket(socket.AF_INET)
+                _ssl.sslwrap(s._sock, 0, None, None,
+                             ssl.CERT_NONE, ssl.PROTOCOL_SSLv2, None, None)
+            except ssl.SSLError as e:
+                if (ssl.OPENSSL_VERSION_INFO == (0, 9, 8, 15, 15) and
+                    platform.linux_distribution() == ('debian', 'squeeze/sid', '')
+                    and 'Invalid SSL protocol variant specified' in str(e)):
+                    raise unittest.SkipTest("Patched Ubuntu OpenSSL breaks behaviour")
+            return func(*args, **kwargs)
+        return f
+    else:
+        return func
 
 
 class BasicSocketTests(unittest.TestCase):
 
     def test_constants(self):
-        ssl.PROTOCOL_SSLv2
+        #ssl.PROTOCOL_SSLv2
         ssl.PROTOCOL_SSLv23
         ssl.PROTOCOL_SSLv3
         ssl.PROTOCOL_TLSv1
@@ -966,7 +969,8 @@
             try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True)
             try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL)
             try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED)
-            try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False)
+            if hasattr(ssl, 'PROTOCOL_SSLv2'):
+                try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False)
             try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, False)
             try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1, False)
 
@@ -978,7 +982,8 @@
             try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True)
             try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL)
             try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)
-            try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv2, False)
+            if hasattr(ssl, 'PROTOCOL_SSLv2'):
+                try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv2, False)
             try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv3, False)
             try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv23, False)
 
diff --git a/lib_pypy/_functools.py b/lib_pypy/_functools.py
--- a/lib_pypy/_functools.py
+++ b/lib_pypy/_functools.py
@@ -14,10 +14,9 @@
             raise TypeError("the first argument must be callable")
         self.func = func
         self.args = args
-        self.keywords = keywords
+        self.keywords = keywords or None
 
     def __call__(self, *fargs, **fkeywords):
-        newkeywords = self.keywords.copy()
-        newkeywords.update(fkeywords)
-        return self.func(*(self.args + fargs), **newkeywords)
-
+        if self.keywords is not None:
+            fkeywords.update(self.keywords)
+        return self.func(*(self.args + fargs), **fkeywords)
diff --git a/lib_pypy/greenlet.py b/lib_pypy/greenlet.py
--- a/lib_pypy/greenlet.py
+++ b/lib_pypy/greenlet.py
@@ -48,23 +48,23 @@
     def switch(self, *args):
         "Switch execution to this greenlet, optionally passing the values "
         "given as argument(s).  Returns the value passed when switching back."
-        return self.__switch(_continulet.switch, args)
+        return self.__switch('switch', args)
 
     def throw(self, typ=GreenletExit, val=None, tb=None):
         "raise exception in greenlet, return value passed when switching back"
-        return self.__switch(_continulet.throw, typ, val, tb)
+        return self.__switch('throw', typ, val, tb)
 
-    def __switch(target, unbound_method, *args):
+    def __switch(target, methodname, *args):
         current = getcurrent()
         #
         while not target:
             if not target.__started:
-                if unbound_method != _continulet.throw:
+                if methodname == 'switch':
                     greenlet_func = _greenlet_start
                 else:
                     greenlet_func = _greenlet_throw
                 _continulet.__init__(target, greenlet_func, *args)
-                unbound_method = _continulet.switch
+                methodname = 'switch'
                 args = ()
                 target.__started = True
                 break
@@ -75,22 +75,8 @@
             target = target.parent
         #
         try:
-            if current.__main:
-                if target.__main:
-                    # switch from main to main
-                    if unbound_method == _continulet.throw:
-                        raise args[0], args[1], args[2]
-                    (args,) = args
-                else:
-                    # enter from main to target
-                    args = unbound_method(target, *args)
-            else:
-                if target.__main:
-                    # leave to go to target=main
-                    args = unbound_method(current, *args)
-                else:
-                    # switch from non-main to non-main
-                    args = unbound_method(current, *args, to=target)
+            unbound_method = getattr(_continulet, methodname)
+            args = unbound_method(current, *args, to=target)
         except GreenletExit, e:
             args = (e,)
         finally:
@@ -138,8 +124,7 @@
     try:
         res = greenlet.run(*args)
     finally:
-        if greenlet.parent is not _tls.main:
-            _continuation.permute(greenlet, greenlet.parent)
+        _continuation.permute(greenlet, greenlet.parent)
     return (res,)
 
 def _greenlet_throw(greenlet, exc, value, tb):
@@ -147,5 +132,4 @@
     try:
         raise exc, value, tb
     finally:
-        if greenlet.parent is not _tls.main:
-            _continuation.permute(greenlet, greenlet.parent)
+        _continuation.permute(greenlet, greenlet.parent)
diff --git a/lib_pypy/stackless.py b/lib_pypy/stackless.py
--- a/lib_pypy/stackless.py
+++ b/lib_pypy/stackless.py
@@ -7,19 +7,12 @@
 
 import traceback
 import _continuation
-from functools import partial
 
 class TaskletExit(Exception):
     pass
 
 CoroutineExit = TaskletExit
 
-class GWrap(_continuation.continulet):
-    """This is just a wrapper around continulet to allow
-       to stick additional attributes to a continulet.
-       To be more concrete, we need a backreference to
-       the coroutine object"""
-
 
 class coroutine(object):
     "we can't have continulet as a base, because continulets can't be rebound"
@@ -42,12 +35,10 @@
            arguments *argl, **argd
         """
         if self._frame is None or not self._frame.is_pending():
-
-            def _func(c, *args, **kwargs):
-                return func(*args, **kwargs)
-            
-            run = partial(_func, *argl, **argd)
-            self._frame = frame = GWrap(run)
+            def run(c):
+                _tls.current_coroutine = self
+                return func(*argl, **argd)
+            self._frame = frame = _continuation.continulet(run)
         else:
             raise ValueError("cannot bind a bound coroutine")
 
@@ -58,16 +49,18 @@
            None is returned
         """
         current = _getcurrent()
-        current._jump_to(self)
-
-    def _jump_to(self, coroutine):
-        _tls.current_coroutine = coroutine
-        self._frame.switch(to=coroutine._frame)
+        try:
+            current._frame.switch(to=self._frame)
+        finally:
+            _tls.current_coroutine = current
 
     def kill(self):
         """coro.kill() : kill coroutine coro"""
-        _tls.current_coroutine = self
-        self._frame.throw(CoroutineExit)
+        current = _getcurrent()
+        try:
+            current._frame.throw(CoroutineExit, to=self._frame)
+        finally:
+            _tls.current_coroutine = current
 
     def _is_alive(self):
         if self._frame is None:
@@ -78,10 +71,7 @@
 
     def getcurrent():
         """coroutine.getcurrent() -> the currently running coroutine"""
-        try:
-            return _getcurrent()
-        except AttributeError:
-            return _maincoro
+        return _getcurrent()
     getcurrent = staticmethod(getcurrent)
 
     def __reduce__(self):
@@ -109,13 +99,10 @@
     # create the main coroutine for this thread
     _tls.current_coroutine = None
     main_coroutine = coroutine()
-    main_coroutine.bind(lambda x:x)
+    typ = _continuation.continulet
+    main_coroutine._frame = typ.__new__(typ)
     _tls.main_coroutine = main_coroutine
     _tls.current_coroutine = main_coroutine
-    return main_coroutine
-
-
-_maincoro = _coroutine_create_main()
 
 
 from collections import deque
@@ -161,10 +148,10 @@
     _last_task = next
     assert not next.blocked
     if next is not current:
-        try:
+        #try:
             next.switch()
-        except CoroutineExit:
-            raise TaskletExit
+        #except CoroutineExit:  --- they are the same anyway
+        #    raise TaskletExit
     return current
 
 def set_schedule_callback(callback):
@@ -459,6 +446,7 @@
         def _func():
             try:
                 try:
+                    coroutine.switch(back)
                     func(*argl, **argd)
                 except TaskletExit:
                     pass
@@ -468,6 +456,8 @@
 
         self.func = None
         coroutine.bind(self, _func)
+        back = _getcurrent()
+        coroutine.switch(self)
         self.alive = True
         _scheduler_append(self)
         return self
diff --git a/pypy/annotation/annrpython.py b/pypy/annotation/annrpython.py
--- a/pypy/annotation/annrpython.py
+++ b/pypy/annotation/annrpython.py
@@ -149,7 +149,7 @@
         desc = olddesc.bind_self(classdef)
         args = self.bookkeeper.build_args("simple_call", args_s[:])
         desc.consider_call_site(self.bookkeeper, desc.getcallfamily(), [desc],
-            args, annmodel.s_ImpossibleValue)
+            args, annmodel.s_ImpossibleValue, None)
         result = []
         def schedule(graph, inputcells):
             result.append((graph, inputcells))
diff --git a/pypy/annotation/bookkeeper.py b/pypy/annotation/bookkeeper.py
--- a/pypy/annotation/bookkeeper.py
+++ b/pypy/annotation/bookkeeper.py
@@ -209,8 +209,8 @@
                 self.consider_call_site(call_op)
 
             for pbc, args_s in self.emulated_pbc_calls.itervalues():
-                self.consider_call_site_for_pbc(pbc, 'simple_call', 
-                                                args_s, s_ImpossibleValue)
+                self.consider_call_site_for_pbc(pbc, 'simple_call',
+                                                args_s, s_ImpossibleValue, None)
             self.emulated_pbc_calls = {}
         finally:
             self.leave()
@@ -257,18 +257,18 @@
             args_s = [lltype_to_annotation(adtmeth.ll_ptrtype)] + args_s
         if isinstance(s_callable, SomePBC):
             s_result = binding(call_op.result, s_ImpossibleValue)
-            self.consider_call_site_for_pbc(s_callable,
-                                            call_op.opname,
-                                            args_s, s_result)
+            self.consider_call_site_for_pbc(s_callable, call_op.opname, args_s,
+                                            s_result, call_op)
 
-    def consider_call_site_for_pbc(self, s_callable, opname, args_s, s_result):
+    def consider_call_site_for_pbc(self, s_callable, opname, args_s, s_result,
+                                   call_op):
         descs = list(s_callable.descriptions)
         if not descs:
             return
         family = descs[0].getcallfamily()
         args = self.build_args(opname, args_s)
         s_callable.getKind().consider_call_site(self, family, descs, args,
-                                                s_result)
+                                                s_result, call_op)
 
     def getuniqueclassdef(self, cls):
         """Get the ClassDef associated with the given user cls.
@@ -656,6 +656,7 @@
                 whence = None
             else:
                 whence = emulated # callback case
+            op = None
             s_previous_result = s_ImpossibleValue
 
         def schedule(graph, inputcells):
@@ -663,7 +664,7 @@
 
         results = []
         for desc in descs:
-            results.append(desc.pycall(schedule, args, s_previous_result))
+            results.append(desc.pycall(schedule, args, s_previous_result, op))
         s_result = unionof(*results)
         return s_result
 
diff --git a/pypy/annotation/description.py b/pypy/annotation/description.py
--- a/pypy/annotation/description.py
+++ b/pypy/annotation/description.py
@@ -255,7 +255,11 @@
             raise TypeError, "signature mismatch: %s" % e.getmsg(self.name)
         return inputcells
 
-    def specialize(self, inputcells):
+    def specialize(self, inputcells, op=None):
+        if (op is None and
+            getattr(self.bookkeeper, "position_key", None) is not None):
+            _, block, i = self.bookkeeper.position_key
+            op = block.operations[i]
         if self.specializer is None:
             # get the specializer based on the tag of the 'pyobj'
             # (if any), according to the current policy
@@ -269,11 +273,14 @@
                 enforceargs = Sig(*enforceargs)
                 self.pyobj._annenforceargs_ = enforceargs
             enforceargs(self, inputcells) # can modify inputcells in-place
-        return self.specializer(self, inputcells)
+        if getattr(self.pyobj, '_annspecialcase_', '').endswith("call_location"):
+            return self.specializer(self, inputcells, op)
+        else:
+            return self.specializer(self, inputcells)
 
-    def pycall(self, schedule, args, s_previous_result):
+    def pycall(self, schedule, args, s_previous_result, op=None):
         inputcells = self.parse_arguments(args)
-        result = self.specialize(inputcells)
+        result = self.specialize(inputcells, op)
         if isinstance(result, FunctionGraph):
             graph = result         # common case
             # if that graph has a different signature, we need to re-parse
@@ -296,17 +303,17 @@
                                              None,       # selfclassdef
                                              name)
 
-    def consider_call_site(bookkeeper, family, descs, args, s_result):
+    def consider_call_site(bookkeeper, family, descs, args, s_result, op):
         shape = rawshape(args)
-        row = FunctionDesc.row_to_consider(descs, args)
+        row = FunctionDesc.row_to_consider(descs, args, op)
         family.calltable_add_row(shape, row)
     consider_call_site = staticmethod(consider_call_site)
 
-    def variant_for_call_site(bookkeeper, family, descs, args):
+    def variant_for_call_site(bookkeeper, family, descs, args, op):
         shape = rawshape(args)
         bookkeeper.enter(None)
         try:
-            row = FunctionDesc.row_to_consider(descs, args)
+            row = FunctionDesc.row_to_consider(descs, args, op)
         finally:
             bookkeeper.leave()
         index = family.calltable_lookup_row(shape, row)
@@ -316,7 +323,7 @@
     def rowkey(self):
         return self
 
-    def row_to_consider(descs, args):
+    def row_to_consider(descs, args, op):
         # see comments in CallFamily
         from pypy.annotation.model import s_ImpossibleValue
         row = {}
@@ -324,7 +331,7 @@
             def enlist(graph, ignore):
                 row[desc.rowkey()] = graph
                 return s_ImpossibleValue   # meaningless
-            desc.pycall(enlist, args, s_ImpossibleValue)
+            desc.pycall(enlist, args, s_ImpossibleValue, op)
         return row
     row_to_consider = staticmethod(row_to_consider)
 
@@ -521,7 +528,7 @@
                             "specialization" % (self.name,))
         return self.getclassdef(None)
 
-    def pycall(self, schedule, args, s_previous_result):
+    def pycall(self, schedule, args, s_previous_result, op=None):
         from pypy.annotation.model import SomeInstance, SomeImpossibleValue
         if self.specialize:
             if self.specialize == 'specialize:ctr_location':
@@ -664,7 +671,7 @@
             cdesc = cdesc.basedesc
         return s_result     # common case
 
-    def consider_call_site(bookkeeper, family, descs, args, s_result):
+    def consider_call_site(bookkeeper, family, descs, args, s_result, op):
         from pypy.annotation.model import SomeInstance, SomePBC, s_None
         if len(descs) == 1:
             # call to a single class, look at the result annotation
@@ -709,7 +716,7 @@
             initdescs[0].mergecallfamilies(*initdescs[1:])
             initfamily = initdescs[0].getcallfamily()
             MethodDesc.consider_call_site(bookkeeper, initfamily, initdescs,
-                                          args, s_None)
+                                          args, s_None, op)
     consider_call_site = staticmethod(consider_call_site)
 
     def getallbases(self):
@@ -782,13 +789,13 @@
     def getuniquegraph(self):
         return self.funcdesc.getuniquegraph()
 
-    def pycall(self, schedule, args, s_previous_result):
+    def pycall(self, schedule, args, s_previous_result, op=None):
         from pypy.annotation.model import SomeInstance
         if self.selfclassdef is None:
             raise Exception("calling %r" % (self,))
         s_instance = SomeInstance(self.selfclassdef, flags = self.flags)
         args = args.prepend(s_instance)
-        return self.funcdesc.pycall(schedule, args, s_previous_result)
+        return self.funcdesc.pycall(schedule, args, s_previous_result, op)
 
     def bind_under(self, classdef, name):
         self.bookkeeper.warning("rebinding an already bound %r" % (self,))
@@ -801,10 +808,10 @@
                                              self.name,
                                              flags)
 
-    def consider_call_site(bookkeeper, family, descs, args, s_result):
+    def consider_call_site(bookkeeper, family, descs, args, s_result, op):
         shape = rawshape(args, nextra=1)     # account for the extra 'self'
         funcdescs = [methoddesc.funcdesc for methoddesc in descs]
-        row = FunctionDesc.row_to_consider(descs, args)
+        row = FunctionDesc.row_to_consider(descs, args, op)
         family.calltable_add_row(shape, row)
     consider_call_site = staticmethod(consider_call_site)
 
@@ -956,16 +963,16 @@
         return '<MethodOfFrozenDesc %r of %r>' % (self.funcdesc,
                                                   self.frozendesc)
 
-    def pycall(self, schedule, args, s_previous_result):
+    def pycall(self, schedule, args, s_previous_result, op=None):
         from pypy.annotation.model import SomePBC
         s_self = SomePBC([self.frozendesc])
         args = args.prepend(s_self)
-        return self.funcdesc.pycall(schedule, args, s_previous_result)
+        return self.funcdesc.pycall(schedule, args, s_previous_result, op)
 
-    def consider_call_site(bookkeeper, family, descs, args, s_result):
+    def consider_call_site(bookkeeper, family, descs, args, s_result, op):
         shape = rawshape(args, nextra=1)    # account for the extra 'self'
         funcdescs = [mofdesc.funcdesc for mofdesc in descs]
-        row = FunctionDesc.row_to_consider(descs, args)
+        row = FunctionDesc.row_to_consider(descs, args, op)
         family.calltable_add_row(shape, row)
     consider_call_site = staticmethod(consider_call_site)
 
diff --git a/pypy/annotation/policy.py b/pypy/annotation/policy.py
--- a/pypy/annotation/policy.py
+++ b/pypy/annotation/policy.py
@@ -1,7 +1,7 @@
 # base annotation policy for specialization
 from pypy.annotation.specialize import default_specialize as default
 from pypy.annotation.specialize import specialize_argvalue, specialize_argtype, specialize_arglistitemtype
-from pypy.annotation.specialize import memo
+from pypy.annotation.specialize import memo, specialize_call_location
 # for some reason, model must be imported first,
 # or we create a cycle.
 from pypy.annotation import model as annmodel
@@ -75,6 +75,7 @@
     specialize__arg = staticmethod(specialize_argvalue) # specialize:arg(N)
     specialize__argtype = staticmethod(specialize_argtype) # specialize:argtype(N)
     specialize__arglistitemtype = staticmethod(specialize_arglistitemtype)
+    specialize__call_location = staticmethod(specialize_call_location)
 
     def specialize__ll(pol, *args):
         from pypy.rpython.annlowlevel import LowLevelAnnotatorPolicy
diff --git a/pypy/annotation/specialize.py b/pypy/annotation/specialize.py
--- a/pypy/annotation/specialize.py
+++ b/pypy/annotation/specialize.py
@@ -370,3 +370,7 @@
     else:
         key = s.listdef.listitem.s_value.knowntype
     return maybe_star_args(funcdesc, key, args_s)
+
+def specialize_call_location(funcdesc, args_s, op):
+    assert op is not None
+    return maybe_star_args(funcdesc, op, args_s)
diff --git a/pypy/annotation/test/test_annrpython.py b/pypy/annotation/test/test_annrpython.py
--- a/pypy/annotation/test/test_annrpython.py
+++ b/pypy/annotation/test/test_annrpython.py
@@ -1099,8 +1099,8 @@
         allocdesc = a.bookkeeper.getdesc(alloc)
         s_C1 = a.bookkeeper.immutablevalue(C1)
         s_C2 = a.bookkeeper.immutablevalue(C2)
-        graph1 = allocdesc.specialize([s_C1])
-        graph2 = allocdesc.specialize([s_C2])
+        graph1 = allocdesc.specialize([s_C1], None)
+        graph2 = allocdesc.specialize([s_C2], None)
         assert a.binding(graph1.getreturnvar()).classdef == C1df
         assert a.binding(graph2.getreturnvar()).classdef == C2df
         assert graph1 in a.translator.graphs
@@ -1135,8 +1135,8 @@
         allocdesc = a.bookkeeper.getdesc(alloc)
         s_C1 = a.bookkeeper.immutablevalue(C1)
         s_C2 = a.bookkeeper.immutablevalue(C2)
-        graph1 = allocdesc.specialize([s_C1, s_C2])
-        graph2 = allocdesc.specialize([s_C2, s_C2])
+        graph1 = allocdesc.specialize([s_C1, s_C2], None)
+        graph2 = allocdesc.specialize([s_C2, s_C2], None)
         assert a.binding(graph1.getreturnvar()).classdef == C1df
         assert a.binding(graph2.getreturnvar()).classdef == C2df
         assert graph1 in a.translator.graphs
@@ -1194,6 +1194,19 @@
         assert len(executedesc._cache[(0, 'star', 2)].startblock.inputargs) == 4
         assert len(executedesc._cache[(1, 'star', 3)].startblock.inputargs) == 5
 
+    def test_specialize_call_location(self):
+        def g(a):
+            return a
+        g._annspecialcase_ = "specialize:call_location"
+        def f(x):
+            return g(x)
+        f._annspecialcase_ = "specialize:argtype(0)"
+        def h(y):
+            w = f(y)
+            return int(f(str(y))) + w
+        a = self.RPythonAnnotator()
+        assert a.build_types(h, [int]) == annmodel.SomeInteger()
+
     def test_assert_list_doesnt_lose_info(self):
         class T(object):
             pass
diff --git a/pypy/doc/stackless.rst b/pypy/doc/stackless.rst
--- a/pypy/doc/stackless.rst
+++ b/pypy/doc/stackless.rst
@@ -66,7 +66,7 @@
 In practice, in PyPy, you cannot change the ``f_back`` of an abitrary
 frame, but only of frames stored in ``continulets``.
 
-Continulets are internally implemented using stacklets.  Stacklets are a
+Continulets are internally implemented using stacklets_.  Stacklets are a
 bit more primitive (they are really one-shot continuations), but that
 idea only works in C, not in Python.  The basic idea of continulets is
 to have at any point in time a complete valid stack; this is important
@@ -215,11 +215,6 @@
 
 * Support for other CPUs than x86 and x86-64
 
-* The app-level ``f_back`` field of frames crossing continulet boundaries
-  is None for now, unlike what I explain in the theoretical overview
-  above.  It mostly means that in a ``pdb.set_trace()`` you cannot go
-  ``up`` past countinulet boundaries.  This could be fixed.
-
 .. __: `recursion depth limit`_
 
 (*) Pickling, as well as changing threads, could be implemented by using
@@ -285,6 +280,24 @@
 to use other interfaces like genlets and greenlets.)
 
 
+Stacklets
++++++++++
+
+Continulets are internally implemented using stacklets, which is the
+generic RPython-level building block for "one-shot continuations".  For
+more information about them please see the documentation in the C source
+at `pypy/translator/c/src/stacklet/stacklet.h`_.
+
+The module ``pypy.rlib.rstacklet`` is a thin wrapper around the above
+functions.  The key point is that new() and switch() always return a
+fresh stacklet handle (or an empty one), and switch() additionally
+consumes one.  It makes no sense to have code in which the returned
+handle is ignored, or used more than once.  Note that ``stacklet.c`` is
+written assuming that the user knows that, and so no additional checking
+occurs; this can easily lead to obscure crashes if you don't use a
+wrapper like PyPy's '_continuation' module.
+
+
 Theory of composability
 +++++++++++++++++++++++
 
diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py
--- a/pypy/interpreter/argument.py
+++ b/pypy/interpreter/argument.py
@@ -125,6 +125,7 @@
 
     ###  Manipulation  ###
 
+    @jit.look_inside_iff(lambda self: not self._dont_jit)
     def unpack(self): # slowish
         "Return a ([w1,w2...], {'kw':w3...}) pair."
         kwds_w = {}
@@ -245,6 +246,8 @@
 
     ###  Parsing for function calls  ###
 
+    # XXX: this should be @jit.look_inside_iff, but we need key word arguments,
+    # and it doesn't support them for now.
     def _match_signature(self, w_firstarg, scope_w, signature, defaults_w=None,
                          blindargs=0):
         """Parse args and kwargs according to the signature of a code object,
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -8,13 +8,13 @@
 from pypy.interpreter.miscutils import ThreadLocals
 from pypy.tool.cache import Cache
 from pypy.tool.uid import HUGEVAL_BYTES
-from pypy.rlib.objectmodel import we_are_translated
+from pypy.rlib.objectmodel import we_are_translated, newlist
 from pypy.rlib.debug import make_sure_not_resized
 from pypy.rlib.timer import DummyTimer, Timer
 from pypy.rlib.rarithmetic import r_uint
 from pypy.rlib import jit
 from pypy.tool.sourcetools import func_with_new_name
-import os, sys, py
+import os, sys
 
 __all__ = ['ObjSpace', 'OperationError', 'Wrappable', 'W_Root']
 
@@ -757,7 +757,18 @@
         w_iterator = self.iter(w_iterable)
         # If we know the expected length we can preallocate.
         if expected_length == -1:
-            items = []
+            try:
+                lgt_estimate = self.len_w(w_iterable)
+            except OperationError, o:
+                if (not o.match(self, self.w_AttributeError) and
+                    not o.match(self, self.w_TypeError)):
+                    raise
+                items = []
+            else:
+                try:
+                    items = newlist(lgt_estimate)
+                except MemoryError:
+                    items = [] # it might have lied
         else:
             items = [None] * expected_length
         idx = 0
diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -1,5 +1,4 @@
 import sys
-from pypy.interpreter.miscutils import Stack
 from pypy.interpreter.error import OperationError
 from pypy.rlib.rarithmetic import LONG_BIT
 from pypy.rlib.unroll import unrolling_iterable
@@ -48,6 +47,7 @@
         return frame
 
     @staticmethod
+    @jit.unroll_safe  # should usually loop 0 times, very rarely more than once
     def getnextframe_nohidden(frame):
         frame = frame.f_backref()
         while frame and frame.hide():
@@ -81,58 +81,6 @@
 
     # ________________________________________________________________
 
-
-    class Subcontext(object):
-        # coroutine: subcontext support
-
-        def __init__(self):
-            self.topframe = None
-            self.w_tracefunc = None
-            self.profilefunc = None
-            self.w_profilefuncarg = None
-            self.is_tracing = 0
-
-        def enter(self, ec):
-            ec.topframeref = jit.non_virtual_ref(self.topframe)
-            ec.w_tracefunc = self.w_tracefunc
-            ec.profilefunc = self.profilefunc
-            ec.w_profilefuncarg = self.w_profilefuncarg
-            ec.is_tracing = self.is_tracing
-            ec.space.frame_trace_action.fire()
-
-        def leave(self, ec):
-            self.topframe = ec.gettopframe()
-            self.w_tracefunc = ec.w_tracefunc
-            self.profilefunc = ec.profilefunc
-            self.w_profilefuncarg = ec.w_profilefuncarg
-            self.is_tracing = ec.is_tracing
-
-        def clear_framestack(self):
-            self.topframe = None
-
-        # the following interface is for pickling and unpickling
-        def getstate(self, space):
-            if self.topframe is None:
-                return space.w_None
-            return self.topframe
-
-        def setstate(self, space, w_state):
-            from pypy.interpreter.pyframe import PyFrame
-            if space.is_w(w_state, space.w_None):
-                self.topframe = None
-            else:
-                self.topframe = space.interp_w(PyFrame, w_state)
-
-        def getframestack(self):
-            lst = []
-            f = self.topframe
-            while f is not None:
-                lst.append(f)
-                f = f.f_backref()
-            lst.reverse()
-            return lst
-        # coroutine: I think this is all, folks!
-
     def c_call_trace(self, frame, w_func, args=None):
         "Profile the call of a builtin function"
         self._c_call_return_trace(frame, w_func, args, 'c_call')
diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py
--- a/pypy/interpreter/function.py
+++ b/pypy/interpreter/function.py
@@ -242,8 +242,10 @@
             # we have been seen by other means so rtyping should not choke
             # on us
             identifier = self.code.identifier
-            assert Function._all.get(identifier, self) is self, ("duplicate "
-                                                                 "function ids")
+            previous = Function._all.get(identifier, self)
+            assert previous is self, (
+                "duplicate function ids with identifier=%r: %r and %r" % (
+                identifier, previous, self))
             self.add_to_table()
         return False
 
diff --git a/pypy/interpreter/miscutils.py b/pypy/interpreter/miscutils.py
--- a/pypy/interpreter/miscutils.py
+++ b/pypy/interpreter/miscutils.py
@@ -2,154 +2,6 @@
 Miscellaneous utilities.
 """
 
-import types
-
-from pypy.rlib.rarithmetic import r_uint
-
-class RootStack:
-    pass
-
-class Stack(RootStack):
-    """Utility class implementing a stack."""
-
-    _annspecialcase_ = "specialize:ctr_location" # polymorphic
-
-    def __init__(self):
-        self.items = []
-
-    def clone(self):
-        s = self.__class__()
-        for item in self.items:
-            try:
-                item = item.clone()
-            except AttributeError:
-                pass
-            s.push(item)
-        return s
-
-    def push(self, item):
-        self.items.append(item)
-
-    def pop(self):
-        return self.items.pop()
-
-    def drop(self, n):
-        if n > 0:
-            del self.items[-n:]
-
-    def top(self, position=0):
-        """'position' is 0 for the top of the stack, 1 for the item below,
-        and so on.  It must not be negative."""
-        if position < 0:
-            raise ValueError, 'negative stack position'
-        if position >= len(self.items):
-            raise IndexError, 'not enough entries in stack'
-        return self.items[~position]
-
-    def set_top(self, value, position=0):
-        """'position' is 0 for the top of the stack, 1 for the item below,
-        and so on.  It must not be negative."""
-        if position < 0:
-            raise ValueError, 'negative stack position'
-        if position >= len(self.items):
-            raise IndexError, 'not enough entries in stack'
-        self.items[~position] = value
-
-    def depth(self):
-        return len(self.items)
-
-    def empty(self):
-        return len(self.items) == 0
-
-
-class FixedStack(RootStack):
-    _annspecialcase_ = "specialize:ctr_location" # polymorphic
-
-    # unfortunately, we have to re-do everything
-    def __init__(self):
-        pass
-
-    def setup(self, stacksize):
-        self.ptr = r_uint(0) # we point after the last element
-        self.items = [None] * stacksize
-
-    def clone(self):
-        # this is only needed if we support flow space
-        s = self.__class__()
-        s.setup(len(self.items))
-        for item in self.items[:self.ptr]:
-            try:
-                item = item.clone()
-            except AttributeError:
-                pass
-            s.push(item)
-        return s
-
-    def push(self, item):
-        ptr = self.ptr
-        self.items[ptr] = item
-        self.ptr = ptr + 1
-
-    def pop(self):
-        ptr = self.ptr - 1
-        ret = self.items[ptr]   # you get OverflowError if the stack is empty
-        self.items[ptr] = None
-        self.ptr = ptr
-        return ret
-
-    def drop(self, n):
-        while n > 0:
-            n -= 1
-            self.ptr -= 1
-            self.items[self.ptr] = None
-
-    def top(self, position=0):
-        # for a fixed stack, we assume correct indices
-        return self.items[self.ptr + ~position]
-
-    def set_top(self, value, position=0):
-        # for a fixed stack, we assume correct indices
-        self.items[self.ptr + ~position] = value
-
-    def depth(self):
-        return self.ptr
-
-    def empty(self):
-        return not self.ptr
-
-
-class InitializedClass(type):
-    """NOT_RPYTHON.  A meta-class that allows a class to initialize itself (or
-    its subclasses) by calling __initclass__() as a class method."""
-    def __init__(self, name, bases, dict):
-        super(InitializedClass, self).__init__(name, bases, dict)
-        for basecls in self.__mro__:
-            raw = basecls.__dict__.get('__initclass__')
-            if isinstance(raw, types.FunctionType):
-                raw(self)   # call it as a class method
-
-
-class RwDictProxy(object):
-    """NOT_RPYTHON.  A dict-like class standing for 'cls.__dict__', to work
-    around the fact that the latter is a read-only proxy for new-style
-    classes."""
-    
-    def __init__(self, cls):
-        self.cls = cls
-
-    def __getitem__(self, attr):
-        return self.cls.__dict__[attr]
-
-    def __setitem__(self, attr, value):
-        setattr(self.cls, attr, value)
-
-    def __contains__(self, value):
-        return value in self.cls.__dict__
-
-    def items(self):
-        return self.cls.__dict__.items()
-
-
 class ThreadLocals:
     """Pseudo thread-local storage, for 'space.threadlocals'.
     This is not really thread-local at all; the intention is that the PyPy
diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py
--- a/pypy/interpreter/pycode.py
+++ b/pypy/interpreter/pycode.py
@@ -10,7 +10,7 @@
 from pypy.interpreter.argument import Signature
 from pypy.interpreter.error import OperationError
 from pypy.interpreter.gateway import NoneNotWrapped, unwrap_spec
-from pypy.interpreter.astcompiler.consts import (CO_OPTIMIZED,
+from pypy.interpreter.astcompiler.consts import (
     CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS, CO_NESTED,
     CO_GENERATOR, CO_CONTAINSGLOBALS)
 from pypy.rlib.rarithmetic import intmask
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -66,7 +66,7 @@
         make_sure_not_resized(self.locals_stack_w)
         check_nonneg(self.nlocals)
         #
-        if space.config.objspace.honor__builtins__:
+        if space.config.objspace.honor__builtins__ and w_globals is not None:
             self.builtin = space.builtin.pick_builtin(w_globals)
         # regular functions always have CO_OPTIMIZED and CO_NEWLOCALS.
         # class bodies only have CO_NEWLOCALS.
@@ -614,7 +614,8 @@
         return self.get_builtin().getdict(space)
 
     def fget_f_back(self, space):
-        return self.space.wrap(self.f_backref())
+        f_back = ExecutionContext.getnextframe_nohidden(self)
+        return self.space.wrap(f_back)
 
     def fget_f_lasti(self, space):
         return self.space.wrap(self.last_instr)
diff --git a/pypy/interpreter/test/test_objspace.py b/pypy/interpreter/test/test_objspace.py
--- a/pypy/interpreter/test/test_objspace.py
+++ b/pypy/interpreter/test/test_objspace.py
@@ -71,6 +71,23 @@
         assert err.value.match(space, space.w_ValueError)
         err = raises(OperationError, space.unpackiterable, w_l, 5)
         assert err.value.match(space, space.w_ValueError)
+        w_a = space.appexec((), """():
+        class A(object):
+            def __iter__(self):
+                return self
+            def next(self):
+                raise StopIteration
+            def __len__(self):
+                1/0
+        return A()
+        """)
+        try:
+            space.unpackiterable(w_a)
+        except OperationError, o:
+            if not o.match(space, space.w_ZeroDivisionError):
+                raise Exception("DID NOT RAISE")
+        else:
+            raise Exception("DID NOT RAISE")
 
     def test_fixedview(self):
         space = self.space
diff --git a/pypy/interpreter/test/test_pyframe.py b/pypy/interpreter/test/test_pyframe.py
--- a/pypy/interpreter/test/test_pyframe.py
+++ b/pypy/interpreter/test/test_pyframe.py
@@ -1,4 +1,5 @@
 from pypy.tool import udir
+from pypy.conftest import option
 
 
 class AppTestPyFrame:
@@ -6,6 +7,15 @@
     def setup_class(cls):
         cls.w_udir = cls.space.wrap(str(udir.udir))
         cls.w_tempfile1 = cls.space.wrap(str(udir.udir.join('tempfile1')))
+        if not option.runappdirect:
+            w_call_further = cls.space.appexec([], """():
+                def call_further(f):
+                    return f()
+                return call_further
+            """)
+            assert not w_call_further.code.hidden_applevel
+            w_call_further.code.hidden_applevel = True       # hack
+            cls.w_call_further = w_call_further
 
     # test for the presence of the attributes, not functionality
 
@@ -107,6 +117,22 @@
         frame = f()
         assert frame.f_back.f_code.co_name == 'f'
 
+    def test_f_back_hidden(self):
+        if not hasattr(self, 'call_further'):
+            skip("not for runappdirect testing")
+        import sys
+        def f():
+            return (sys._getframe(0),
+                    sys._getframe(1),
+                    sys._getframe(0).f_back)
+        def main():
+            return self.call_further(f)
+        f0, f1, f1bis = main()
+        assert f0.f_code.co_name == 'f'
+        assert f1.f_code.co_name == 'main'
+        assert f1bis is f1
+        assert f0.f_back is f1
+
     def test_f_exc_xxx(self):
         import sys
 
diff --git a/pypy/jit/backend/llsupport/llmodel.py b/pypy/jit/backend/llsupport/llmodel.py
--- a/pypy/jit/backend/llsupport/llmodel.py
+++ b/pypy/jit/backend/llsupport/llmodel.py
@@ -496,6 +496,16 @@
         u = lltype.cast_opaque_ptr(lltype.Ptr(rstr.UNICODE), string)
         u.chars[index] = unichr(newvalue)
 
+    def bh_copystrcontent(self, src, dst, srcstart, dststart, length):
+        src = lltype.cast_opaque_ptr(lltype.Ptr(rstr.STR), src)
+        dst = lltype.cast_opaque_ptr(lltype.Ptr(rstr.STR), dst)
+        rstr.copy_string_contents(src, dst, srcstart, dststart, length)
+
+    def bh_copyunicodecontent(self, src, dst, srcstart, dststart, length):
+        src = lltype.cast_opaque_ptr(lltype.Ptr(rstr.UNICODE), src)
+        dst = lltype.cast_opaque_ptr(lltype.Ptr(rstr.UNICODE), dst)
+        rstr.copy_unicode_contents(src, dst, srcstart, dststart, length)
+
     def bh_call_i(self, func, calldescr, args_i, args_r, args_f):
         assert isinstance(calldescr, BaseIntCallDescr)
         if not we_are_translated():
diff --git a/pypy/jit/backend/model.py b/pypy/jit/backend/model.py
--- a/pypy/jit/backend/model.py
+++ b/pypy/jit/backend/model.py
@@ -78,7 +78,7 @@
         Optionally, return a ``ops_offset`` dictionary.  See the docstring of
         ``compiled_loop`` for more informations about it.
         """
-        raise NotImplementedError    
+        raise NotImplementedError
 
     def dump_loop_token(self, looptoken):
         """Print a disassembled version of looptoken to stdout"""
@@ -298,6 +298,10 @@
         raise NotImplementedError
     def bh_unicodesetitem(self, string, index, newvalue):
         raise NotImplementedError
+    def bh_copystrcontent(self, src, dst, srcstart, dststart, length):
+        raise NotImplementedError
+    def bh_copyunicodecontent(self, src, dst, srcstart, dststart, length):
+        raise NotImplementedError
 
     def force(self, force_token):
         raise NotImplementedError
diff --git a/pypy/jit/codewriter/jtransform.py b/pypy/jit/codewriter/jtransform.py
--- a/pypy/jit/codewriter/jtransform.py
+++ b/pypy/jit/codewriter/jtransform.py
@@ -1158,6 +1158,12 @@
             return SpaceOperation('%s_assert_green' % kind, args, None)
         elif oopspec_name == 'jit.current_trace_length':
             return SpaceOperation('current_trace_length', [], op.result)
+        elif oopspec_name == 'jit.isconstant':
+            kind = getkind(args[0].concretetype)
+            return SpaceOperation('%s_isconstant' % kind, args, op.result)
+        elif oopspec_name == 'jit.isvirtual':
+            kind = getkind(args[0].concretetype)
+            return SpaceOperation('%s_isvirtual' % kind, args, op.result)
         else:
             raise AssertionError("missing support for %r" % oopspec_name)
 
@@ -1415,6 +1421,14 @@
         else:
             assert 0, "args[0].concretetype must be STR or UNICODE"
         #
+        if oopspec_name == 'stroruni.copy_contents':
+            if SoU.TO == rstr.STR:
+                new_op = 'copystrcontent'
+            elif SoU.TO == rstr.UNICODE:
+                new_op = 'copyunicodecontent'
+            else:
+                assert 0
+            return SpaceOperation(new_op, args, op.result)
         if oopspec_name == "stroruni.equal":
             for otherindex, othername, argtypes, resulttype in [
                 (EffectInfo.OS_STREQ_SLICE_CHECKNULL,
diff --git a/pypy/jit/metainterp/blackhole.py b/pypy/jit/metainterp/blackhole.py
--- a/pypy/jit/metainterp/blackhole.py
+++ b/pypy/jit/metainterp/blackhole.py
@@ -835,6 +835,18 @@
     def bhimpl_current_trace_length():
         return -1
 
+    @arguments("i", returns="i")
+    def bhimpl_int_isconstant(x):
+        return False
+
+    @arguments("r", returns="i")
+    def bhimpl_ref_isconstant(x):
+        return False
+
+    @arguments("r", returns="i")
+    def bhimpl_ref_isvirtual(x):
+        return False
+
     # ----------
     # the main hints and recursive calls
 
@@ -1224,6 +1236,9 @@
     @arguments("cpu", "r", "i", "i")
     def bhimpl_strsetitem(cpu, string, index, newchr):
         cpu.bh_strsetitem(string, index, newchr)
+    @arguments("cpu", "r", "r", "i", "i", "i")
+    def bhimpl_copystrcontent(cpu, src, dst, srcstart, dststart, length):
+        cpu.bh_copystrcontent(src, dst, srcstart, dststart, length)
 
     @arguments("cpu", "i", returns="r")
     def bhimpl_newunicode(cpu, length):
@@ -1237,6 +1252,9 @@
     @arguments("cpu", "r", "i", "i")
     def bhimpl_unicodesetitem(cpu, unicode, index, newchr):
         cpu.bh_unicodesetitem(unicode, index, newchr)
+    @arguments("cpu", "r", "r", "i", "i", "i")
+    def bhimpl_copyunicodecontent(cpu, src, dst, srcstart, dststart, length):
+        cpu.bh_copyunicodecontent(src, dst, srcstart, dststart, length)
 
     @arguments(returns=(longlong.is_64_bit and "i" or "f"))
     def bhimpl_ll_read_timestamp():
@@ -1441,7 +1459,7 @@
 def resume_in_blackhole(metainterp_sd, jitdriver_sd, resumedescr,
                         all_virtuals=None):
     from pypy.jit.metainterp.resume import blackhole_from_resumedata
-    debug_start('jit-blackhole')
+    #debug_start('jit-blackhole')
     metainterp_sd.profiler.start_blackhole()
     blackholeinterp = blackhole_from_resumedata(
         metainterp_sd.blackholeinterpbuilder,
@@ -1460,12 +1478,12 @@
         _run_forever(blackholeinterp, current_exc)
     finally:
         metainterp_sd.profiler.end_blackhole()
-        debug_stop('jit-blackhole')
+        #debug_stop('jit-blackhole')
 
 def convert_and_run_from_pyjitpl(metainterp, raising_exception=False):
     # Get a chain of blackhole interpreters and fill them by copying
     # 'metainterp.framestack'.
-    debug_start('jit-blackhole')
+    #debug_start('jit-blackhole')
     metainterp_sd = metainterp.staticdata
     metainterp_sd.profiler.start_blackhole()
     nextbh = None
@@ -1488,4 +1506,4 @@
         _run_forever(firstbh, current_exc)
     finally:
         metainterp_sd.profiler.end_blackhole()
-        debug_stop('jit-blackhole')
+        #debug_stop('jit-blackhole')
diff --git a/pypy/jit/metainterp/heapcache.py b/pypy/jit/metainterp/heapcache.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/metainterp/heapcache.py
@@ -0,0 +1,210 @@
+from pypy.jit.metainterp.history import ConstInt
+from pypy.jit.metainterp.resoperation import rop
+
+
+class HeapCache(object):
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        # contains boxes where the class is already known
+        self.known_class_boxes = {}
+        # store the boxes that contain newly allocated objects, this maps the
+        # boxes to a bool, the bool indicates whether or not the object has
+        # escaped the trace or not (True means the box never escaped, False
+        # means it did escape), its presences in the mapping shows that it was
+        # allocated inside the trace
+        self.new_boxes = {}
+        # Tracks which boxes should be marked as escaped when the key box
+        # escapes.
+        self.dependencies = {}
+        # contains frame boxes that are not virtualizables
+        self.nonstandard_virtualizables = {}
+        # heap cache
+        # maps descrs to {from_box, to_box} dicts
+        self.heap_cache = {}
+        # heap array cache
+        # maps descrs to {index: {from_box: to_box}} dicts
+        self.heap_array_cache = {}
+        # cache the length of arrays
+        self.length_cache = {}
+
+    def invalidate_caches(self, opnum, descr, argboxes):
+        self.mark_escaped(opnum, argboxes)
+        self.clear_caches(opnum, descr, argboxes)
+
+    def mark_escaped(self, opnum, argboxes):
+        idx = 0
+        if opnum == rop.SETFIELD_GC:
+            assert len(argboxes) == 2
+            box, valuebox = argboxes
+            if self.is_unescaped(box) and self.is_unescaped(valuebox):
+                self.dependencies.setdefault(box, []).append(valuebox)
+            else:
+                self._escape(valuebox)
+        # GETFIELD_GC doesn't escape it's argument
+        elif opnum != rop.GETFIELD_GC:
+            for box in argboxes:
+                # setarrayitem_gc don't escape its first argument
+                if not (idx == 0 and opnum in [rop.SETARRAYITEM_GC]):
+                    self._escape(box)
+                idx += 1
+
+    def _escape(self, box):
+        if box in self.new_boxes:
+            self.new_boxes[box] = False
+        if box in self.dependencies:
+            for dep in self.dependencies[box]:
+                self._escape(dep)
+            del self.dependencies[box]
+
+    def clear_caches(self, opnum, descr, argboxes):
+        if opnum == rop.SETFIELD_GC:
+            return
+        if opnum == rop.SETARRAYITEM_GC:
+            return
+        if opnum == rop.SETFIELD_RAW:
+            return
+        if opnum == rop.SETARRAYITEM_RAW:
+            return
+        if rop._OVF_FIRST <= opnum <= rop._OVF_LAST:
+            return
+        if rop._NOSIDEEFFECT_FIRST <= opnum <= rop._NOSIDEEFFECT_LAST:
+            return
+        if opnum == rop.CALL or opnum == rop.CALL_LOOPINVARIANT:
+            effectinfo = descr.get_extra_info()
+            ef = effectinfo.extraeffect
+            if ef == effectinfo.EF_LOOPINVARIANT or \
+               ef == effectinfo.EF_ELIDABLE_CANNOT_RAISE or \
+               ef == effectinfo.EF_ELIDABLE_CAN_RAISE:
+                return
+            # A special case for ll_arraycopy, because it is so common, and its
+            # effects are so well defined.
+            elif effectinfo.oopspecindex == effectinfo.OS_ARRAYCOPY:
+                # The destination box
+                if argboxes[2] in self.new_boxes:
+                    # XXX: no descr here so we invalidate any of them, not just
+                    # of the correct type
+                    # XXX: in theory the indices of the copy could be looked at
+                    # as well
+                    for descr, cache in self.heap_array_cache.iteritems():
+                        for idx, cache in cache.iteritems():
+                            for frombox in cache.keys():
+                                if frombox not in self.new_boxes:
+                                    del cache[frombox]
+                    return
+
+        self.heap_cache.clear()
+        self.heap_array_cache.clear()
+
+    def is_class_known(self, box):
+        return box in self.known_class_boxes
+
+    def class_now_known(self, box):
+        self.known_class_boxes[box] = None
+
+    def is_nonstandard_virtualizable(self, box):
+        return box in self.nonstandard_virtualizables
+
+    def nonstandard_virtualizables_now_known(self, box):
+        self.nonstandard_virtualizables[box] = None
+
+    def is_unescaped(self, box):
+        return self.new_boxes.get(box, False)
+
+    def new(self, box):
+        self.new_boxes[box] = True
+
+    def new_array(self, box, lengthbox):
+        self.new(box)
+        self.arraylen_now_known(box, lengthbox)
+
+    def getfield(self, box, descr):
+        d = self.heap_cache.get(descr, None)
+        if d:
+            tobox = d.get(box, None)
+            if tobox:
+                return tobox
+        return None
+
+    def getfield_now_known(self, box, descr, fieldbox):
+        self.heap_cache.setdefault(descr, {})[box] = fieldbox
+
+    def setfield(self, box, descr, fieldbox):
+        d = self.heap_cache.get(descr, None)
+        new_d = self._do_write_with_aliasing(d, box, fieldbox)
+        self.heap_cache[descr] = new_d
+
+    def _do_write_with_aliasing(self, d, box, fieldbox):
+        # slightly subtle logic here
+        # a write to an arbitrary box, all other boxes can alias this one
+        if not d or box not in self.new_boxes:
+            # therefore we throw away the cache
+            return {box: fieldbox}
+        # the object we are writing to is freshly allocated
+        # only remove some boxes from the cache
+        new_d = {}
+        for frombox, tobox in d.iteritems():
+            # the other box is *also* freshly allocated
+            # therefore frombox and box *must* contain different objects
+            # thus we can keep it in the cache
+            if frombox in self.new_boxes:
+                new_d[frombox] = tobox
+        new_d[box] = fieldbox
+        return new_d
+
+    def getarrayitem(self, box, descr, indexbox):
+        if not isinstance(indexbox, ConstInt):
+            return
+        index = indexbox.getint()
+        cache = self.heap_array_cache.get(descr, None)
+        if cache:
+            indexcache = cache.get(index, None)
+            if indexcache is not None:
+                return indexcache.get(box, None)
+
+    def getarrayitem_now_known(self, box, descr, indexbox, valuebox):
+        if not isinstance(indexbox, ConstInt):
+            return
+        index = indexbox.getint()
+        cache = self.heap_array_cache.setdefault(descr, {})
+        indexcache = cache.get(index, None)
+        if indexcache is not None:
+            indexcache[box] = valuebox
+        else:
+            cache[index] = {box: valuebox}
+
+    def setarrayitem(self, box, descr, indexbox, valuebox):
+        if not isinstance(indexbox, ConstInt):
+            cache = self.heap_array_cache.get(descr, None)
+            if cache is not None:
+                cache.clear()
+            return
+        index = indexbox.getint()
+        cache = self.heap_array_cache.setdefault(descr, {})
+        indexcache = cache.get(index, None)
+        cache[index] = self._do_write_with_aliasing(indexcache, box, valuebox)
+
+    def arraylen(self, box):
+        return self.length_cache.get(box, None)
+
+    def arraylen_now_known(self, box, lengthbox):
+        self.length_cache[box] = lengthbox
+
+    def _replace_box(self, d, oldbox, newbox):
+        new_d = {}
+        for frombox, tobox in d.iteritems():
+            if frombox is oldbox:
+                frombox = newbox
+            if tobox is oldbox:
+                tobox = newbox
+            new_d[frombox] = tobox
+        return new_d
+
+    def replace_box(self, oldbox, newbox):
+        for descr, d in self.heap_cache.iteritems():
+            self.heap_cache[descr] = self._replace_box(d, oldbox, newbox)
+        for descr, d in self.heap_array_cache.iteritems():
+            for index, cache in d.iteritems():
+                d[index] = self._replace_box(cache, oldbox, newbox)
+        self.length_cache = self._replace_box(self.length_cache, oldbox, newbox)
diff --git a/pypy/jit/metainterp/optimizeopt/optimizer.py b/pypy/jit/metainterp/optimizeopt/optimizer.py
--- a/pypy/jit/metainterp/optimizeopt/optimizer.py
+++ b/pypy/jit/metainterp/optimizeopt/optimizer.py
@@ -71,7 +71,7 @@
             guards.append(op)
         elif self.level == LEVEL_KNOWNCLASS:
             op = ResOperation(rop.GUARD_NONNULL, [box], None)
-            guards.append(op)            
+            guards.append(op)
             op = ResOperation(rop.GUARD_CLASS, [box, self.known_class], None)
             guards.append(op)
         else:
@@ -112,7 +112,7 @@
                     self.lenbound.bound.intersect(other.lenbound.bound)
                 else:
                     self.lenbound = other.lenbound.clone()
-                    
+
 
     def force_box(self):
         return self.box
@@ -146,7 +146,7 @@
         assert isinstance(constbox, Const)
         self.box = constbox
         self.level = LEVEL_CONSTANT
-        
+
         if isinstance(constbox, ConstInt):
             val = constbox.getint()
             self.intbound = IntBound(val, val)
@@ -378,7 +378,7 @@
         new.set_optimizations(optimizations)
         new.quasi_immutable_deps = self.quasi_immutable_deps
         return new
-        
+
     def produce_potential_short_preamble_ops(self, sb):
         raise NotImplementedError('This is implemented in unroll.UnrollableOptimizer')
 
@@ -505,9 +505,9 @@
         if op.returns_bool_result():
             self.bool_boxes[self.getvalue(op.result)] = None
         self._emit_operation(op)
-        
+
     @specialize.argtype(0)
-    def _emit_operation(self, op):        
+    def _emit_operation(self, op):
         for i in range(op.numargs()):
             arg = op.getarg(i)
             try:
@@ -568,7 +568,7 @@
                 arg = value.get_key_box()
             args[i] = arg
         args[n] = ConstInt(op.getopnum())
-        args[n+1] = op.getdescr()
+        args[n + 1] = op.getdescr()
         return args
 
     @specialize.argtype(0)
@@ -616,7 +616,7 @@
 
     def remember_emitting_pure(self, op):
         pass
-    
+
     def constant_fold(self, op):
         argboxes = [self.get_constant_box(op.getarg(i))
                     for i in range(op.numargs())]
@@ -658,9 +658,9 @@
             arrayvalue = self.getvalue(op.getarg(0))
             arrayvalue.make_len_gt(MODE_UNICODE, op.getdescr(), indexvalue.box.getint())
         self.optimize_default(op)
-        
 
-    
+
+
 
 dispatch_opt = make_dispatcher_method(Optimizer, 'optimize_',
         default=Optimizer.optimize_default)
diff --git a/pypy/jit/metainterp/optimizeopt/rewrite.py b/pypy/jit/metainterp/optimizeopt/rewrite.py
--- a/pypy/jit/metainterp/optimizeopt/rewrite.py
+++ b/pypy/jit/metainterp/optimizeopt/rewrite.py
@@ -19,7 +19,7 @@
 
     def new(self):
         return OptRewrite()
-        
+
     def produce_potential_short_preamble_ops(self, sb):
         for op in self.loop_invariant_producer.values():
             sb.add_potential(op)
@@ -231,6 +231,17 @@
             else:
                 self.make_constant(op.result, result)
                 return
+
+        args = self.optimizer.make_args_key(op)
+        oldop = self.optimizer.pure_operations.get(args, None)
+        if oldop is not None and oldop.getdescr() is op.getdescr():
+            assert oldop.getopnum() == op.getopnum()
+            self.make_equal_to(op.result, self.getvalue(oldop.result))
+            return
+        else:
+            self.optimizer.pure_operations[args] = op
+            self.optimizer.remember_emitting_pure(op)
+
         # replace CALL_PURE with just CALL
         args = op.getarglist()
         self.emit_operation(ResOperation(rop.CALL, args, op.result,
@@ -351,7 +362,7 @@
         # expects a compile-time constant
         assert isinstance(arg, Const)
         key = make_hashable_int(arg.getint())
-        
+
         resvalue = self.loop_invariant_results.get(key, None)
         if resvalue is not None:
             self.make_equal_to(op.result, resvalue)
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
@@ -4711,6 +4711,35 @@
         """
         self.optimize_loop(ops, expected)
 
+    def test_empty_copystrunicontent(self):
+        ops = """
+        [p0, p1, i0, i2, i3]
+        i4 = int_eq(i3, 0)
+        guard_true(i4) []
+        copystrcontent(p0, p1, i0, i2, i3)
+        jump(p0, p1, i0, i2, i3)
+        """
+        expected = """
+        [p0, p1, i0, i2, i3]
+        i4 = int_eq(i3, 0)
+        guard_true(i4) []
+        jump(p0, p1, i0, i2, 0)
+        """
+        self.optimize_strunicode_loop(ops, expected)
+
+    def test_empty_copystrunicontent_virtual(self):
+        ops = """
+        [p0]
+        p1 = newstr(23)
+        copystrcontent(p0, p1, 0, 0, 0)
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        jump(p0)
+        """
+        self.optimize_strunicode_loop(ops, expected)
+
     def test_forced_virtuals_aliasing(self):
         ops = """
         [i0, i1]
@@ -4739,6 +4768,7 @@
         self.optimize_loop(ops, expected)
 
 
+
 class TestLLtype(BaseTestOptimizeBasic, LLtypeMixin):
     pass
 
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -102,9 +102,9 @@
             print "Short Preamble:"
             short = loop.preamble.token.short_preamble[0]
             print short.inputargs
-            print '\n'.join([str(o) for o in short.operations])        
+            print '\n'.join([str(o) for o in short.operations])
             print
-        
+
         assert expected != "crash!", "should have raised an exception"
         self.assert_equal(loop, expected)
         if expected_preamble:
@@ -113,7 +113,7 @@
         if expected_short:
             self.assert_equal(short, expected_short,
                               text_right='expected short preamble')
-            
+
         return loop
 
 class OptimizeOptTest(BaseTestWithUnroll):
@@ -866,10 +866,10 @@
         setfield_gc(p3sub, i1, descr=valuedescr)
         setfield_gc(p1, p3sub, descr=nextdescr)
         # XXX: We get two extra operations here because the setfield
-        #      above is the result of forcing p1 and thus not 
+        #      above is the result of forcing p1 and thus not
         #      registered with the heap optimizer. I've makred tests
         #      below with VIRTUALHEAP if they suffer from this issue
-        p3sub2 = getfield_gc(p1, descr=nextdescr) 
+        p3sub2 = getfield_gc(p1, descr=nextdescr)
         guard_nonnull_class(p3sub2, ConstClass(node_vtable2)) []
         jump(i1, p1, p3sub2)
         """
@@ -1411,7 +1411,7 @@
         guard_isnull(p18) [p0, p8]
         p31 = new(descr=ssize)
         p35 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p35, p31, descr=valuedescr)        
+        setfield_gc(p35, p31, descr=valuedescr)
         jump(p0, p35)
         """
         expected = """
@@ -1426,7 +1426,7 @@
         guard_isnull(p18) [p0, p8]
         p31 = new(descr=ssize)
         p35 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p35, p31, descr=valuedescr)        
+        setfield_gc(p35, p31, descr=valuedescr)
         jump(p0, p35, p19, p18)
         """
         expected = """
@@ -1435,7 +1435,7 @@
         jump(p0, NULL)
         """
         self.optimize_loop(ops, expected)
-        
+
     def test_varray_1(self):
         ops = """
         [i1]
@@ -2181,7 +2181,7 @@
         jump(p1)
         """
         self.optimize_loop(ops, expected)
-        
+
     def test_duplicate_getarrayitem_2(self):
         ops = """
         [p1, i0]
@@ -2199,7 +2199,7 @@
         jump(p1, i7, i6)
         """
         self.optimize_loop(ops, expected)
-        
+
     def test_duplicate_getarrayitem_after_setarrayitem_1(self):
         ops = """
         [p1, p2]
@@ -2812,14 +2812,14 @@
         guard_no_overflow() []
         i3b = int_is_true(i3)
         guard_true(i3b) []
-        setfield_gc(p1, i1, descr=valuedescr)        
+        setfield_gc(p1, i1, descr=valuedescr)
         escape(i3)
         escape(i3)
         jump(i1, p1, i3)
         """
         expected = """
         [i1, p1, i3]
-        setfield_gc(p1, i1, descr=valuedescr)        
+        setfield_gc(p1, i1, descr=valuedescr)
         escape(i3)
         escape(i3)
         jump(i1, p1, i3)
@@ -2830,7 +2830,7 @@
         ops = """
         [p8, p11, i24]
         p26 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p26, i24, descr=adescr)        
+        setfield_gc(p26, i24, descr=adescr)
         i34 = getfield_gc_pure(p11, descr=valuedescr)
         i35 = getfield_gc_pure(p26, descr=adescr)
         i36 = int_add_ovf(i34, i35)
@@ -2839,10 +2839,10 @@
         """
         expected = """
         [p8, p11, i26]
-        jump(p8, p11, i26)        
-        """
-        self.optimize_loop(ops, expected)
-        
+        jump(p8, p11, i26)
+        """
+        self.optimize_loop(ops, expected)
+
     def test_ovf_guard_in_short_preamble2(self):
         ops = """
         [p8, p11, p12]
@@ -3191,13 +3191,18 @@
         jump(p1, i4, i3)
         '''
         expected = '''
+        [p1, i4, i3, i5]
+        setfield_gc(p1, i5, descr=valuedescr)
+        jump(p1, i3, i5, i5)
+        '''
+        preamble = '''
         [p1, i1, i4]
         setfield_gc(p1, i1, descr=valuedescr)
         i3 = call(p1, descr=plaincalldescr)
         setfield_gc(p1, i3, descr=valuedescr)
-        jump(p1, i4, i3)
+        jump(p1, i4, i3, i3)
         '''
-        self.optimize_loop(ops, expected, expected)
+        self.optimize_loop(ops, expected, preamble)
 
     def test_call_pure_invalidates_heap_knowledge(self):
         # CALL_PURE should still force the setfield_gc() to occur before it
@@ -3209,21 +3214,20 @@
         jump(p1, i4, i3)
         '''
         expected = '''
+        [p1, i4, i3, i5]
+        setfield_gc(p1, i4, descr=valuedescr)
+        jump(p1, i3, i5, i5)
+        '''
+        preamble = '''
         [p1, i1, i4]
         setfield_gc(p1, i1, descr=valuedescr)
         i3 = call(p1, descr=plaincalldescr)
         setfield_gc(p1, i1, descr=valuedescr)
-        jump(p1, i4, i3)
+        jump(p1, i4, i3, i3)
         '''
-        self.optimize_loop(ops, expected, expected)
+        self.optimize_loop(ops, expected, preamble)
 
     def test_call_pure_constant_folding(self):
-        # CALL_PURE is not marked as is_always_pure(), because it is wrong
-        # to call the function arbitrary many times at arbitrary points in
-        # time.  Check that it is either constant-folded (and replaced by
-        # the result of the call, recorded as the first arg), or turned into
-        # a regular CALL.
-        # XXX can this test be improved with unrolling?
         arg_consts = [ConstInt(i) for i in (123456, 4, 5, 6)]
         call_pure_results = {tuple(arg_consts): ConstInt(42)}
         ops = '''
@@ -3239,14 +3243,13 @@
         escape(i1)
         escape(i2)
         i4 = call(123456, 4, i0, 6, descr=plaincalldescr)
-        jump(i0, i4)
+        jump(i0, i4, i4)
         '''
         expected = '''
-        [i0, i2]
+        [i0, i4, i5]
         escape(42)
-        escape(i2)
-        i4 = call(123456, 4, i0, 6, descr=plaincalldescr)
-        jump(i0, i4)
+        escape(i4)
+        jump(i0, i5, i5)
         '''
         self.optimize_loop(ops, expected, preamble, call_pure_results)
 
@@ -3270,18 +3273,43 @@
         escape(i2)
         i4 = call(123456, 4, i0, 6, descr=plaincalldescr)
         guard_no_exception() []
-        jump(i0, i4)
+        jump(i0, i4, i4)
         '''
         expected = '''
-        [i0, i2]
+        [i0, i2, i3]
         escape(42)
         escape(i2)
-        i4 = call(123456, 4, i0, 6, descr=plaincalldescr)
-        guard_no_exception() []
-        jump(i0, i4)
+        jump(i0, i3, i3)
         '''
         self.optimize_loop(ops, expected, preamble, call_pure_results)
 
+    def test_call_pure_returning_virtual(self):
+        # XXX: This kind of loop invaraint call_pure will be forced
+        #      both in the preamble and in the peeled loop
+        ops = '''
+        [p1, i1, i2]
+        p2 = call_pure(0, p1, i1, i2, descr=strslicedescr)
+        escape(p2)
+        jump(p1, i1, i2)
+        '''
+        preamble = '''
+        [p1, i1, i2]
+        i6 = int_sub(i2, i1)
+        p2 = newstr(i6)
+        copystrcontent(p1, p2, i1, 0, i6)
+        escape(p2)
+        jump(p1, i1, i2, i6)
+        '''
+        expected = '''
+        [p1, i1, i2, i6]
+        p2 = newstr(i6)
+        copystrcontent(p1, p2, i1, 0, i6)
+        escape(p2)
+        jump(p1, i1, i2, i6)
+        '''
+        self.optimize_loop(ops, expected, preamble)
+        
+
     # ----------
 
     def test_vref_nonvirtual_nonescape(self):
@@ -5150,14 +5178,14 @@
         [i0, i1, i10, i11, i2, i3, i4]
         escape(i2)
         escape(i3)
-        escape(i4)        
+        escape(i4)
         i24 = int_mul_ovf(i10, i11)
         guard_no_overflow() []
         i23 = int_sub_ovf(i10, i11)
         guard_no_overflow() []
         i22 = int_add_ovf(i10, i11)
         guard_no_overflow() []
-        jump(i0, i1, i10, i11, i2, i3, i4) 
+        jump(i0, i1, i10, i11, i2, i3, i4)
         """
         self.optimize_loop(ops, expected)
 
@@ -5366,6 +5394,8 @@
         """
         self.optimize_strunicode_loop(ops, expected, expected)
 
+    # XXX Should some of the call's below now be call_pure?
+
     def test_str_concat_1(self):
         ops = """
         [p1, p2]
@@ -5699,14 +5729,14 @@
         ops = """
         [p0, i0]
         i1 = unicodegetitem(p0, i0)
-        i10 = unicodegetitem(p0, i0)        
+        i10 = unicodegetitem(p0, i0)
         i2 = int_lt(i1, 0)
         guard_false(i2) []
         jump(p0, i0)
         """
         expected = """
         [p0, i0]
-        i1 = unicodegetitem(p0, i0)        
+        i1 = unicodegetitem(p0, i0)
         jump(p0, i0)
         """
         self.optimize_loop(ops, expected)
@@ -5865,7 +5895,7 @@
         """
         preamble = """
         [p1, i1, i2, p3]
-        guard_nonnull(p3) []        
+        guard_nonnull(p3) []
         i4 = int_sub(i2, i1)
         i0 = call(0, p1, i1, i4, p3, descr=streq_slice_nonnull_descr)
         escape(i0)
@@ -6474,7 +6504,7 @@
         setfield_gc(p3, i1, descr=adescr)
         setfield_gc(p3, i2, descr=bdescr)
         i5 = int_gt(ii, 42)
-        guard_true(i5) []        
+        guard_true(i5) []
         jump(p0, p1, p3, ii2, ii, i1, i2)
         """
         self.optimize_loop(ops, expected)
@@ -6500,7 +6530,7 @@
         p1 = getfield_gc(p0, descr=nextdescr)
         guard_nonnull_class(p1, ConstClass(node_vtable)) []
         p2 = getfield_gc(p1, descr=nextdescr)
-        guard_nonnull_class(p2, ConstClass(node_vtable)) []        
+        guard_nonnull_class(p2, ConstClass(node_vtable)) []
         jump(p0)
         """
         expected = """
@@ -6514,11 +6544,11 @@
         guard_class(p1, ConstClass(node_vtable)) []
         p2 = getfield_gc(p1, descr=nextdescr)
         guard_nonnull(p2) []
-        guard_class(p2, ConstClass(node_vtable)) []        
+        guard_class(p2, ConstClass(node_vtable)) []
         jump(p0)
         """
         self.optimize_loop(ops, expected, expected_short=short)
-        
+
     def test_forced_virtual_pure_getfield(self):
         ops = """
         [p0]
@@ -6582,7 +6612,7 @@
         jump(p1, i2)
         """
         self.optimize_loop(ops, expected)
-        
+
     def test_loopinvariant_strlen(self):
         ops = """
         [p9]
@@ -6715,7 +6745,7 @@
         [p0, p1]
         p2 = new_with_vtable(ConstClass(node_vtable))
         p3 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p2, p3, descr=nextdescr) 
+        setfield_gc(p2, p3, descr=nextdescr)
         jump(p2, p3)
         """
         expected = """
@@ -6734,7 +6764,7 @@
         jump(p2, i2)
         """
         expected = """
-        [p1]        
+        [p1]
         p2 = getarrayitem_gc(p1, 7, descr=<GcPtrArrayDescr>)
         i1 = arraylen_gc(p1)
         jump(p2)
@@ -6775,8 +6805,8 @@
         jump(p0, p2, p1)
         """
         self.optimize_loop(ops, expected, expected_short=short)
-        
-        
+
+
     def test_loopinvariant_constant_strgetitem(self):
         ops = """
         [p0]
@@ -6830,11 +6860,11 @@
         expected = """
         [p0, i22, p1]
         call(i22, descr=nonwritedescr)
-        i3 = unicodelen(p1) # Should be killed by backend        
+        i3 = unicodelen(p1) # Should be killed by backend
         jump(p0, i22, p1)
         """
         self.optimize_loop(ops, expected, expected_short=short)
-        
+
     def test_propagate_virtual_arryalen(self):
         ops = """
         [p0]
@@ -6903,7 +6933,7 @@
         [p0, p1, p10, p11]
         i1 = arraylen_gc(p10, descr=arraydescr)
         getarrayitem_gc(p11, 1, descr=arraydescr)
-        call(i1, descr=nonwritedescr)        
+        call(i1, descr=nonwritedescr)
         jump(p1, p0, p11, p10)
         """
         self.optimize_loop(ops, expected)
@@ -6912,20 +6942,20 @@
         ops = """
         [p5]
         i10 = getfield_gc(p5, descr=valuedescr)
-        call(i10, descr=nonwritedescr) 
+        call(i10, descr=nonwritedescr)
         setfield_gc(p5, 1, descr=valuedescr)
         jump(p5)
         """
         preamble = """
         [p5]
         i10 = getfield_gc(p5, descr=valuedescr)
-        call(i10, descr=nonwritedescr) 
+        call(i10, descr=nonwritedescr)
         setfield_gc(p5, 1, descr=valuedescr)
         jump(p5)
         """
         expected = """
         [p5]
-        call(1, descr=nonwritedescr) 
+        call(1, descr=nonwritedescr)
         jump(p5)
         """
         self.optimize_loop(ops, expected, preamble)
@@ -6963,7 +6993,7 @@
         [p9]
         call_assembler(0, descr=asmdescr)
         i18 = getfield_gc(p9, descr=valuedescr)
-        guard_value(i18, 0) []        
+        guard_value(i18, 0) []
         jump(p9)
         """
         self.optimize_loop(ops, expected)
@@ -6992,17 +7022,17 @@
         i10 = getfield_gc(p5, descr=valuedescr)
         i11 = getfield_gc(p6, descr=nextdescr)
         call(i10, i11, descr=nonwritedescr)
-        setfield_gc(p6, i10, descr=nextdescr)        
+        setfield_gc(p6, i10, descr=nextdescr)
         jump(p5, p6)
         """
         expected = """
         [p5, p6, i10, i11]
         call(i10, i11, descr=nonwritedescr)
-        setfield_gc(p6, i10, descr=nextdescr)        
+        setfield_gc(p6, i10, descr=nextdescr)
         jump(p5, p6, i10, i10)
         """
         self.optimize_loop(ops, expected)
-        
+
     def test_cached_pure_func_of_equal_fields(self):
         ops = """
         [p5, p6]
@@ -7011,18 +7041,18 @@
         i12 = int_add(i10, 7)
         i13 = int_add(i11, 7)
         call(i12, i13, descr=nonwritedescr)
-        setfield_gc(p6, i10, descr=nextdescr)        
+        setfield_gc(p6, i10, descr=nextdescr)
         jump(p5, p6)
         """
         expected = """
         [p5, p6, i14, i12, i10]
         i13 = int_add(i14, 7)
         call(i12, i13, descr=nonwritedescr)
-        setfield_gc(p6, i10, descr=nextdescr)        
+        setfield_gc(p6, i10, descr=nextdescr)
         jump(p5, p6, i10, i12, i10)
         """
         self.optimize_loop(ops, expected)
-        
+
     def test_forced_counter(self):
         # XXX: VIRTUALHEAP (see above)
         py.test.skip("would be fixed by make heap optimizer aware of virtual setfields")
@@ -7165,7 +7195,7 @@
         expected = """
         [p1, p2, i2, i1]
         call(i2, descr=nonwritedescr)
-        setfield_gc(p2, i1, descr=nextdescr)        
+        setfield_gc(p2, i1, descr=nextdescr)
         jump(p1, p2, i2, i1)
         """
         self.optimize_loop(ops, expected)
@@ -7185,11 +7215,11 @@
         expected = """
         [p1, p2, i2, i1]
         call(i2, descr=nonwritedescr)
-        setfield_gc(p2, i1, descr=valuedescr)        
+        setfield_gc(p2, i1, descr=valuedescr)
         jump(p1, p2, i2, i1)
         """
         self.optimize_loop(ops, expected)
-        
+
 class TestLLtype(OptimizeOptTest, LLtypeMixin):
     pass
-        
+
diff --git a/pypy/jit/metainterp/optimizeopt/unroll.py b/pypy/jit/metainterp/optimizeopt/unroll.py
--- a/pypy/jit/metainterp/optimizeopt/unroll.py
+++ b/pypy/jit/metainterp/optimizeopt/unroll.py
@@ -226,8 +226,9 @@
                 if op and op.result:
                     preamble_value = preamble_optimizer.getvalue(op.result)
                     value = self.optimizer.getvalue(op.result)
-                    imp = ValueImporter(self, preamble_value, op)
-                    self.optimizer.importable_values[value] = imp
+                    if not value.is_virtual():
+                        imp = ValueImporter(self, preamble_value, op)
+                        self.optimizer.importable_values[value] = imp
                     newresult = self.optimizer.getvalue(op.result).get_key_box()
                     if newresult is not op.result:
                         self.short_boxes.alias(newresult, op.result)
diff --git a/pypy/jit/metainterp/optimizeopt/vstring.py b/pypy/jit/metainterp/optimizeopt/vstring.py
--- a/pypy/jit/metainterp/optimizeopt/vstring.py
+++ b/pypy/jit/metainterp/optimizeopt/vstring.py
@@ -296,7 +296,7 @@
 
 
 def copy_str_content(optimizer, srcbox, targetbox,
-                     srcoffsetbox, offsetbox, lengthbox, mode):
+                     srcoffsetbox, offsetbox, lengthbox, mode, need_next_offset=True):
     if isinstance(srcbox, ConstPtr) and isinstance(srcoffsetbox, Const):
         M = 5
     else:
@@ -313,7 +313,10 @@
                                               None))
             offsetbox = _int_add(optimizer, offsetbox, CONST_1)
     else:
-        nextoffsetbox = _int_add(optimizer, offsetbox, lengthbox)
+        if need_next_offset:
+            nextoffsetbox = _int_add(optimizer, offsetbox, lengthbox)
+        else:
+            nextoffsetbox = None
         op = ResOperation(mode.COPYSTRCONTENT, [srcbox, targetbox,
                                                 srcoffsetbox, offsetbox,
                                                 lengthbox], None)
@@ -450,6 +453,30 @@
         lengthbox = value.getstrlen(self.optimizer, mode)
         self.make_equal_to(op.result, self.getvalue(lengthbox))
 
+    def optimize_COPYSTRCONTENT(self, op):
+        self._optimize_COPYSTRCONTENT(op, mode_string)
+    def optimize_COPYUNICODECONTENT(self, op):
+        self._optimize_COPYSTRCONTENT(op, mode_unicode)
+
+    def _optimize_COPYSTRCONTENT(self, op, mode):
+        # args: src dst srcstart dststart length
+        src = self.getvalue(op.getarg(0))
+        dst = self.getvalue(op.getarg(1))
+        srcstart = self.getvalue(op.getarg(2))
+        dststart = self.getvalue(op.getarg(3))
+        length = self.getvalue(op.getarg(4))
+
+        if length.is_constant() and length.box.getint() == 0:
+            return
+        copy_str_content(self.optimizer,
+            src.force_box(),
+            dst.force_box(),
+            srcstart.force_box(),
+            dststart.force_box(),
+            length.force_box(),
+            mode, need_next_offset=False
+        )
+
     def optimize_CALL(self, op):
         # dispatch based on 'oopspecindex' to a method that handles
         # specifically the given oopspec call.  For non-oopspec calls,
diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -17,6 +17,7 @@
 from pypy.jit.metainterp.jitprof import ABORT_TOO_LONG, ABORT_BRIDGE, \
                                         ABORT_FORCE_QUASIIMMUT, ABORT_BAD_LOOP
 from pypy.jit.metainterp.jitexc import JitException, get_llexception
+from pypy.jit.metainterp.heapcache import HeapCache
 from pypy.rlib.objectmodel import specialize
 from pypy.jit.codewriter.jitcode import JitCode, SwitchDictDescr
 from pypy.jit.codewriter import heaptracker
@@ -209,7 +210,8 @@
                 self.metainterp.clear_exception()
                 resbox = self.execute(rop.%s, b1, b2)
                 self.make_result_of_lastop(resbox)  # same as execute_varargs()
-                self.metainterp.handle_possible_overflow_error()
+                if not isinstance(resbox, Const):
+                    self.metainterp.handle_possible_overflow_error()
                 return resbox
         ''' % (_opimpl, _opimpl.upper())).compile()
 
@@ -321,7 +323,7 @@
     def _establish_nullity(self, box, orgpc):
         value = box.nonnull()
         if value:
-            if box not in self.metainterp.known_class_boxes:
+            if not self.metainterp.heapcache.is_class_known(box):
                 self.generate_guard(rop.GUARD_NONNULL, box, resumepc=orgpc)
         else:
             if not isinstance(box, Const):
@@ -366,14 +368,17 @@
 
     @arguments("descr")
     def opimpl_new(self, sizedescr):
-        return self.execute_with_descr(rop.NEW, sizedescr)
+        resbox = self.execute_with_descr(rop.NEW, sizedescr)
+        self.metainterp.heapcache.new(resbox)
+        return resbox
 
     @arguments("descr")
     def opimpl_new_with_vtable(self, sizedescr):
         cpu = self.metainterp.cpu
         cls = heaptracker.descr2vtable(cpu, sizedescr)
         resbox = self.execute(rop.NEW_WITH_VTABLE, ConstInt(cls))
-        self.metainterp.known_class_boxes[resbox] = None
+        self.metainterp.heapcache.new(resbox)
+        self.metainterp.heapcache.class_now_known(resbox)
         return resbox
 
 ##    @FixME  #arguments("box")
@@ -392,26 +397,30 @@
 ##        self.execute(rop.SUBCLASSOF, box1, box2)
 
     @arguments("descr", "box")
-    def opimpl_new_array(self, itemsizedescr, countbox):
-        return self.execute_with_descr(rop.NEW_ARRAY, itemsizedescr, countbox)
+    def opimpl_new_array(self, itemsizedescr, lengthbox):
+        resbox = self.execute_with_descr(rop.NEW_ARRAY, itemsizedescr, lengthbox)
+        self.metainterp.heapcache.new_array(resbox, lengthbox)
+        return resbox
+
+    @specialize.arg(1)
+    def _do_getarrayitem_gc_any(self, op, arraybox, arraydescr, indexbox):
+        tobox = self.metainterp.heapcache.getarrayitem(
+                arraybox, arraydescr, indexbox)
+        if tobox:
+            # sanity check: see whether the current array value
+            # corresponds to what the cache thinks the value is
+            resbox = executor.execute(self.metainterp.cpu, self.metainterp, op,
+                                      arraydescr, arraybox, indexbox)
+            assert resbox.constbox().same_constant(tobox.constbox())
+            return tobox
+        resbox = self.execute_with_descr(op, arraydescr, arraybox, indexbox)
+        self.metainterp.heapcache.getarrayitem_now_known(
+                arraybox, arraydescr, indexbox, resbox)
+        return resbox
 
     @arguments("box", "descr", "box")
     def _opimpl_getarrayitem_gc_any(self, arraybox, arraydescr, indexbox):
-        cache = self.metainterp.heap_array_cache.get(arraydescr, None)
-        if cache and isinstance(indexbox, ConstInt):
-            index = indexbox.getint()
-            frombox, tobox = cache.get(index, (None, None))
-            if frombox is arraybox:
-                return tobox
-        resbox = self.execute_with_descr(rop.GETARRAYITEM_GC,
-                                         arraydescr, arraybox, indexbox)
-        if isinstance(indexbox, ConstInt):
-            if not cache:
-                cache = self.metainterp.heap_array_cache[arraydescr] = {}
-            index = indexbox.getint()
-            cache[index] = arraybox, resbox
-        return resbox
-
+        return self._do_getarrayitem_gc_any(rop.GETARRAYITEM_GC, arraybox, arraydescr, indexbox)
 
     opimpl_getarrayitem_gc_i = _opimpl_getarrayitem_gc_any
     opimpl_getarrayitem_gc_r = _opimpl_getarrayitem_gc_any
@@ -427,8 +436,7 @@
 
     @arguments("box", "descr", "box")
     def _opimpl_getarrayitem_gc_pure_any(self, arraybox, arraydescr, indexbox):
-        return self.execute_with_descr(rop.GETARRAYITEM_GC_PURE,
-                                       arraydescr, arraybox, indexbox)
+        return self._do_getarrayitem_gc_any(rop.GETARRAYITEM_GC_PURE, arraybox, arraydescr, indexbox)
 
     opimpl_getarrayitem_gc_pure_i = _opimpl_getarrayitem_gc_pure_any
     opimpl_getarrayitem_gc_pure_r = _opimpl_getarrayitem_gc_pure_any
@@ -439,13 +447,8 @@
                                     indexbox, itembox):
         self.execute_with_descr(rop.SETARRAYITEM_GC, arraydescr, arraybox,
                                 indexbox, itembox)
-        if isinstance(indexbox, ConstInt):
-            cache = self.metainterp.heap_array_cache.setdefault(arraydescr, {})
-            cache[indexbox.getint()] = arraybox, itembox
-        else:
-            cache = self.metainterp.heap_array_cache.get(arraydescr, None)
-            if cache:
-                cache.clear()
+        self.metainterp.heapcache.setarrayitem(
+                arraybox, arraydescr, indexbox, itembox)
 
     opimpl_setarrayitem_gc_i = _opimpl_setarrayitem_gc_any
     opimpl_setarrayitem_gc_r = _opimpl_setarrayitem_gc_any
@@ -462,7 +465,12 @@
 
     @arguments("box", "descr")
     def opimpl_arraylen_gc(self, arraybox, arraydescr):
-        return self.execute_with_descr(rop.ARRAYLEN_GC, arraydescr, arraybox)
+        lengthbox = self.metainterp.heapcache.arraylen(arraybox)
+        if lengthbox is None:
+            lengthbox = self.execute_with_descr(
+                    rop.ARRAYLEN_GC, arraydescr, arraybox)
+            self.metainterp.heapcache.arraylen_now_known(arraybox, lengthbox)
+        return lengthbox
 
     @arguments("orgpc", "box", "descr", "box")
     def opimpl_check_neg_index(self, orgpc, arraybox, arraydescr, indexbox):
@@ -471,19 +479,17 @@
         negbox = self.implement_guard_value(orgpc, negbox)
         if negbox.getint():
             # the index is < 0; add the array length to it
-            lenbox = self.metainterp.execute_and_record(
-                rop.ARRAYLEN_GC, arraydescr, arraybox)
+            lengthbox = self.opimpl_arraylen_gc(arraybox, arraydescr)
             indexbox = self.metainterp.execute_and_record(
-                rop.INT_ADD, None, indexbox, lenbox)
+                rop.INT_ADD, None, indexbox, lengthbox)
         return indexbox
 
     @arguments("descr", "descr", "descr", "descr", "box")
     def opimpl_newlist(self, structdescr, lengthdescr, itemsdescr, arraydescr,
                        sizebox):
-        sbox = self.metainterp.execute_and_record(rop.NEW, structdescr)
+        sbox = self.opimpl_new(structdescr)
         self._opimpl_setfield_gc_any(sbox, lengthdescr, sizebox)
-        abox = self.metainterp.execute_and_record(rop.NEW_ARRAY, arraydescr,
-                                                  sizebox)
+        abox = self.opimpl_new_array(arraydescr, sizebox)
         self._opimpl_setfield_gc_any(sbox, itemsdescr, abox)
         return sbox
 
@@ -540,11 +546,15 @@
 
     @specialize.arg(1)
     def _opimpl_getfield_gc_any_pureornot(self, opnum, box, fielddescr):
-        frombox, tobox = self.metainterp.heap_cache.get(fielddescr, (None, None))
-        if frombox is box:
+        tobox = self.metainterp.heapcache.getfield(box, fielddescr)
+        if tobox is not None:
+            # sanity check: see whether the current struct value
+            # corresponds to what the cache thinks the value is
+            resbox = executor.execute(self.metainterp.cpu, self.metainterp,
+                                      rop.GETFIELD_GC, fielddescr, box)
             return tobox
         resbox = self.execute_with_descr(opnum, fielddescr, box)
-        self.metainterp.heap_cache[fielddescr] = (box, resbox)
+        self.metainterp.heapcache.getfield_now_known(box, fielddescr, resbox)
         return resbox
 
     @arguments("orgpc", "box", "descr")
@@ -565,11 +575,11 @@
 
     @arguments("box", "descr", "box")
     def _opimpl_setfield_gc_any(self, box, fielddescr, valuebox):
-        frombox, tobox = self.metainterp.heap_cache.get(fielddescr, (None, None))
-        if frombox is box and tobox is valuebox:
+        tobox = self.metainterp.heapcache.getfield(box, fielddescr)
+        if tobox is valuebox:
             return
         self.execute_with_descr(rop.SETFIELD_GC, fielddescr, box, valuebox)
-        self.metainterp.heap_cache[fielddescr] = (box, valuebox)
+        self.metainterp.heapcache.setfield(box, fielddescr, valuebox)
     opimpl_setfield_gc_i = _opimpl_setfield_gc_any
     opimpl_setfield_gc_r = _opimpl_setfield_gc_any
     opimpl_setfield_gc_f = _opimpl_setfield_gc_any
@@ -633,7 +643,7 @@
         standard_box = self.metainterp.virtualizable_boxes[-1]
         if standard_box is box:
             return False
-        if box in self.metainterp.nonstandard_virtualizables:
+        if self.metainterp.heapcache.is_nonstandard_virtualizable(box):
             return True
         eqbox = self.metainterp.execute_and_record(rop.PTR_EQ, None,
                                                    box, standard_box)
@@ -642,7 +652,7 @@
         if isstandard:
             self.metainterp.replace_box(box, standard_box)
         else:
-            self.metainterp.nonstandard_virtualizables[box] = None
+            self.metainterp.heapcache.nonstandard_virtualizables_now_known(box)
         return not isstandard
 
     def _get_virtualizable_field_index(self, fielddescr):
@@ -727,7 +737,7 @@
     def opimpl_arraylen_vable(self, pc, box, fdescr, adescr):
         if self._nonstandard_virtualizable(pc, box):
             arraybox = self._opimpl_getfield_gc_any(box, fdescr)
-            return self.execute_with_descr(rop.ARRAYLEN_GC, adescr, arraybox)
+            return self.opimpl_arraylen_gc(arraybox, adescr)
         vinfo = self.metainterp.jitdriver_sd.virtualizable_info
         virtualizable_box = self.metainterp.virtualizable_boxes[-1]
         virtualizable = vinfo.unwrap_virtualizable_box(virtualizable_box)
@@ -858,6 +868,14 @@
     def opimpl_newunicode(self, lengthbox):
         return self.execute(rop.NEWUNICODE, lengthbox)
 
+    @arguments("box", "box", "box", "box", "box")
+    def opimpl_copystrcontent(self, srcbox, dstbox, srcstartbox, dststartbox, lengthbox):
+        return self.execute(rop.COPYSTRCONTENT, srcbox, dstbox, srcstartbox, dststartbox, lengthbox)
+
+    @arguments("box", "box", "box", "box", "box")
+    def opimpl_copyunicodecontent(self, srcbox, dstbox, srcstartbox, dststartbox, lengthbox):
+        return self.execute(rop.COPYUNICODECONTENT, srcbox, dstbox, srcstartbox, dststartbox, lengthbox)
+
 ##    @FixME  #arguments("descr", "varargs")
 ##    def opimpl_residual_oosend_canraise(self, methdescr, varargs):
 ##        return self.execute_varargs(rop.OOSEND, varargs, descr=methdescr,
@@ -884,9 +902,9 @@
     @arguments("orgpc", "box")
     def opimpl_guard_class(self, orgpc, box):
         clsbox = self.cls_of_box(box)
-        if box not in self.metainterp.known_class_boxes:
+        if not self.metainterp.heapcache.is_class_known(box):
             self.generate_guard(rop.GUARD_CLASS, box, [clsbox], resumepc=orgpc)
-            self.metainterp.known_class_boxes[box] = None
+            self.metainterp.heapcache.class_now_known(box)
         return clsbox
 
     @arguments("int", "orgpc")
@@ -1052,6 +1070,18 @@
         return ConstInt(trace_length)
 
     @arguments("box")
+    def _opimpl_isconstant(self, box):
+        return ConstInt(isinstance(box, Const))
+
+    opimpl_int_isconstant = opimpl_ref_isconstant = _opimpl_isconstant
+
+    @arguments("box")
+    def _opimpl_isvirtual(self, box):
+        return ConstInt(self.metainterp.heapcache.is_unescaped(box))
+
+    opimpl_ref_isvirtual = _opimpl_isvirtual
+
+    @arguments("box")
     def opimpl_virtual_ref(self, box):
         # Details on the content of metainterp.virtualref_boxes:
         #
@@ -1492,16 +1522,7 @@
         self.last_exc_value_box = None
         self.retracing_loop_from = None
         self.call_pure_results = args_dict_box()
-        # contains boxes where the class is already known
-        self.known_class_boxes = {}
-        # contains frame boxes that are not virtualizables
-        self.nonstandard_virtualizables = {}
-        # heap cache
-        # maps descrs to (from_box, to_box) tuples
-        self.heap_cache = {}
-        # heap array cache
-        # maps descrs to {index: (from_box, to_box)} dicts
-        self.heap_array_cache = {}
+        self.heapcache = HeapCache()
 
     def perform_call(self, jitcode, boxes, greenkey=None):
         # causes the metainterp to enter the given subfunction
@@ -1674,32 +1695,18 @@
 
     def _record_helper_nonpure_varargs(self, opnum, resbox, descr, argboxes):
         assert resbox is None or isinstance(resbox, Box)
+        if (rop._OVF_FIRST <= opnum <= rop._OVF_LAST and
+            self.last_exc_value_box is None and
+            self._all_constants_varargs(argboxes)):
+            return resbox.constbox()
         # record the operation
         profiler = self.staticdata.profiler
         profiler.count_ops(opnum, RECORDED_OPS)
-        self._invalidate_caches(opnum, descr)
+        self.heapcache.invalidate_caches(opnum, descr, argboxes)
         op = self.history.record(opnum, argboxes, resbox, descr)
         self.attach_debug_info(op)
         return resbox
 
-    def _invalidate_caches(self, opnum, descr):
-        if opnum == rop.SETFIELD_GC:
-            return
-        if opnum == rop.SETARRAYITEM_GC:
-            return
-        if rop._NOSIDEEFFECT_FIRST <= opnum <= rop._NOSIDEEFFECT_LAST:
-            return
-        if opnum == rop.CALL:
-            effectinfo = descr.get_extra_info()
-            ef = effectinfo.extraeffect
-            if ef == effectinfo.EF_LOOPINVARIANT or \
-               ef == effectinfo.EF_ELIDABLE_CANNOT_RAISE or \
-               ef == effectinfo.EF_ELIDABLE_CAN_RAISE:
-                return
-        if self.heap_cache:
-            self.heap_cache.clear()
-        if self.heap_array_cache:
-            self.heap_array_cache.clear()
 
     def attach_debug_info(self, op):
         if (not we_are_translated() and op is not None
@@ -1862,10 +1869,7 @@
                 duplicates[box] = None
 
     def reached_loop_header(self, greenboxes, redboxes, resumedescr):
-        self.known_class_boxes = {}
-        self.nonstandard_virtualizables = {} # XXX maybe not needed?
-        self.heap_cache = {}
-        self.heap_array_cache = {}
+        self.heapcache.reset()
 
         duplicates = {}
         self.remove_consts_and_duplicates(redboxes, len(redboxes),
@@ -2373,17 +2377,7 @@
             for i in range(len(boxes)):
                 if boxes[i] is oldbox:
                     boxes[i] = newbox
-        for descr, (frombox, tobox) in self.heap_cache.iteritems():
-            change = False
-            if frombox is oldbox:
-                change = True
-                frombox = newbox
-            if tobox is oldbox:
-                change = True
-                tobox = newbox
-            if change:
-                self.heap_cache[descr] = frombox, tobox
-        # XXX what about self.heap_array_cache?
+        self.heapcache.replace_box(oldbox, newbox)
 
     def find_biggest_function(self):
         start_stack = []
diff --git a/pypy/jit/metainterp/test/test_ajit.py b/pypy/jit/metainterp/test/test_ajit.py
--- a/pypy/jit/metainterp/test/test_ajit.py
+++ b/pypy/jit/metainterp/test/test_ajit.py
@@ -1,23 +1,25 @@
+import sys
+
 import py
-import sys
-from pypy.rlib.jit import JitDriver, we_are_jitted, hint, dont_look_inside
-from pypy.rlib.jit import loop_invariant, elidable, promote
-from pypy.rlib.jit import jit_debug, assert_green, AssertGreenFailed
-from pypy.rlib.jit import unroll_safe, current_trace_length
+
+from pypy import conftest
+from pypy.jit.codewriter.policy import JitPolicy, StopAtXPolicy
 from pypy.jit.metainterp import pyjitpl, history
+from pypy.jit.metainterp.optimizeopt import ALL_OPTS_DICT
+from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin, noConst
+from pypy.jit.metainterp.typesystem import LLTypeHelper, OOTypeHelper
+from pypy.jit.metainterp.warmspot import get_stats
 from pypy.jit.metainterp.warmstate import set_future_value
-from pypy.jit.metainterp.warmspot import get_stats
-from pypy.jit.codewriter.policy import JitPolicy, StopAtXPolicy
-from pypy import conftest
+from pypy.rlib.jit import (JitDriver, we_are_jitted, hint, dont_look_inside,
+    loop_invariant, elidable, promote, jit_debug, assert_green,
+    AssertGreenFailed, unroll_safe, current_trace_length, look_inside_iff,
+    isconstant, isvirtual)
 from pypy.rlib.rarithmetic import ovfcheck
-from pypy.jit.metainterp.typesystem import LLTypeHelper, OOTypeHelper
 from pypy.rpython.lltypesystem import lltype, llmemory, rffi
 from pypy.rpython.ootypesystem import ootype
-from pypy.jit.metainterp.optimizeopt import ALL_OPTS_DICT
-from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin, noConst
+
 
 class BasicTests:
-
     def test_basic(self):
         def f(x, y):
             return x + y
@@ -99,14 +101,14 @@
                 myjitdriver.jit_merge_point(x=x, y=y, res=res)
                 res += x * x
                 x += 1
-                res += x * x                
+                res += x * x
                 y -= 1
             return res
         res = self.meta_interp(f, [6, 7])
         assert res == 1323
         self.check_loop_count(1)
         self.check_loops(int_mul=1)
-        
+
     def test_loop_variant_mul_ovf(self):
         myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x'])
         def f(x, y):
@@ -1372,7 +1374,7 @@
             return x
         res = self.meta_interp(f, [299], listops=True)
         assert res == f(299)
-        self.check_loops(guard_class=0, guard_value=3)        
+        self.check_loops(guard_class=0, guard_value=3)
         self.check_loops(guard_class=0, guard_value=6, everywhere=True)
 
     def test_merge_guardnonnull_guardclass(self):
@@ -2118,7 +2120,7 @@
             return sa
         res = self.meta_interp(f, [32, 7])
         assert res == f(32, 7)
-        
+
     def test_caching_setarrayitem_fixed(self):
         myjitdriver = JitDriver(greens = [], reds = ['sa', 'i', 'n', 'a', 'node'])
         def f(n, a):
@@ -2138,7 +2140,7 @@
             return sa
         res = self.meta_interp(f, [32, 7])
         assert res == f(32, 7)
-        
+
     def test_caching_setarrayitem_var(self):
         myjitdriver = JitDriver(greens = [], reds = ['sa', 'i', 'n', 'a', 'b', 'node'])
         def f(n, a, b):
@@ -2668,7 +2670,7 @@
             myjitdriver.set_param('threshold', 3)
             myjitdriver.set_param('trace_eagerness', 1)
             myjitdriver.set_param('retrace_limit', 5)
-            myjitdriver.set_param('function_threshold', -1)            
+            myjitdriver.set_param('function_threshold', -1)
             pc = sa = i = 0
             while pc < len(bytecode):
                 myjitdriver.jit_merge_point(pc=pc, n=n, sa=sa, i=i)
@@ -2693,12 +2695,12 @@
         def g(n1, n2):
             for i in range(10):
                 f(n1)
-            for i in range(10):                
+            for i in range(10):
                 f(n2)
 
         nn = [10, 3]
         assert self.meta_interp(g, nn) == g(*nn)
-        
+
         # The attempts of retracing first loop will end up retracing the
         # second and thus fail 5 times, saturating the retrace_count. Instead a
         # bridge back to the preamble of the first loop is produced. A guard in
@@ -2709,7 +2711,7 @@
         self.check_tree_loop_count(2 + 3)
 
         # FIXME: Add a gloabl retrace counter and test that we are not trying more than 5 times.
-        
+
         def g(n):
             for i in range(n):
                 for j in range(10):
@@ -2945,15 +2947,15 @@
             a = [0, 1, 2, 3, 4]
             while i < n:
                 myjitdriver.jit_merge_point(sa=sa, n=n, a=a, i=i)
-                if i < n/2:
+                if i < n / 2:
                     sa += a[4]
-                elif i == n/2:
+                elif i == n / 2:
                     a.pop()
                 i += 1
         res = self.meta_interp(f, [32])
         assert res == f(32)
         self.check_loops(arraylen_gc=2)
-        
+
 class TestOOtype(BasicTests, OOJitMixin):
 
     def test_oohash(self):
@@ -3173,7 +3175,7 @@
         res = self.meta_interp(f, [32])
         assert res == f(32)
         self.check_tree_loop_count(3)
-        
+
     def test_two_loopinvariant_arrays3(self):
         from pypy.rpython.lltypesystem import lltype, llmemory, rffi
         myjitdriver = JitDriver(greens = [], reds = ['sa', 'n', 'i', 'a'])
@@ -3197,7 +3199,7 @@
         res = self.meta_interp(f, [32])
         assert res == f(32)
         self.check_tree_loop_count(2)
-        
+
     def test_two_loopinvariant_arrays_boxed(self):
         class A(object):
             def __init__(self, a):
@@ -3222,7 +3224,7 @@
         res = self.meta_interp(f, [32])
         assert res == f(32)
         self.check_loops(arraylen_gc=2, everywhere=True)
-        
+
     def test_release_gil_flush_heap_cache(self):
         if sys.platform == "win32":
             py.test.skip("needs 'time'")
@@ -3276,7 +3278,136 @@
             return n
 
         self.meta_interp(f, [10], repeat=3)
-        
+
+    def test_jit_merge_point_with_pbc(self):
+        driver = JitDriver(greens = [], reds = ['x'])
+
+        class A(object):
+            def __init__(self, x):
+                self.x = x
+            def _freeze_(self):
+                return True
+        pbc = A(1)
+
+        def main(x):
+            return f(x, pbc)
+
+        def f(x, pbc):
+            while x > 0:
+                driver.jit_merge_point(x = x)
+                x -= pbc.x
+            return x
+
+        self.meta_interp(main, [10])
+
+    def test_look_inside_iff_const(self):
+        @look_inside_iff(lambda arg: isconstant(arg))
+        def f(arg):
+            s = 0
+            while arg > 0:
+                s += arg
+                arg -= 1
+            return s
+
+        driver = JitDriver(greens = ['code'], reds = ['n', 'arg', 's'])
+
+        def main(code, n, arg):
+            s = 0
+            while n > 0:
+                driver.jit_merge_point(code=code, n=n, arg=arg, s=s)
+                if code == 0:
+                    s += f(arg)
+                else:
+                    s += f(1)
+                n -= 1
+            return s
+
+        res = self.meta_interp(main, [0, 10, 2], enable_opts='')
+        assert res == main(0, 10, 2)
+        self.check_loops(call=1)
+        res = self.meta_interp(main, [1, 10, 2], enable_opts='')
+        assert res == main(1, 10, 2)
+        self.check_loops(call=0)
+
+    def test_look_inside_iff_virtual(self):
+        # There's no good reason for this to be look_inside_iff, but it's a test!
+        @look_inside_iff(lambda arg, n: isvirtual(arg))
+        def f(arg, n):
+            if n == 100:
+                for i in xrange(n):
+                    n += i
+            return arg.x
+        class A(object):
+            def __init__(self, x):
+                self.x = x
+        driver = JitDriver(greens=['n'], reds=['i', 'a'])
+        def main(n):
+            i = 0
+            a = A(3)
+            while i < 20:
+                driver.jit_merge_point(i=i, n=n, a=a)
+                if n == 0:
+                    i += f(a, n)
+                else:
+                    i += f(A(2), n)
+        res = self.meta_interp(main, [0], enable_opts='')
+        assert res == main(0)
+        self.check_loops(call=1, getfield_gc=0)
+        res = self.meta_interp(main, [1], enable_opts='')
+        assert res == main(1)
+        self.check_loops(call=0, getfield_gc=0)
+
+    def test_reuse_elidable_result(self):
+        driver = JitDriver(reds=['n', 's'], greens = [])
+        def main(n):
+            s = 0
+            while n > 0:
+                driver.jit_merge_point(s=s, n=n)
+                s += len(str(n)) + len(str(n))
+                n -= 1
+            return s
+        res = self.meta_interp(main, [10])
+        assert res == main(10)
+        self.check_loops({
+            'call': 1, 'guard_no_exception': 1, 'guard_true': 1, 'int_add': 2,
+            'int_gt': 1, 'int_sub': 1, 'strlen': 1, 'jump': 1,
+        })
+
+    def test_look_inside_iff_const_getarrayitem_gc_pure(self):
+        driver = JitDriver(greens=['unroll'], reds=['s', 'n'])
+
+        class A(object):
+            _immutable_fields_ = ["x[*]"]
+            def __init__(self, x):
+                self.x = [x]
+
+        @look_inside_iff(lambda x: isconstant(x))
+        def f(x):
+            i = 0
+            for c in x:
+                i += 1
+            return i
+
+        def main(unroll, n):
+            s = 0
+            while n > 0:
+                driver.jit_merge_point(s=s, n=n, unroll=unroll)
+                if unroll:
+                    x = A("xx")
+                else:
+                    x = A("x" * n)
+                s += f(x.x[0])
+                n -= 1
+            return s
+
+        res = self.meta_interp(main, [0, 10])
+        assert res == main(0, 10)
+        # 2 calls, one for f() and one for char_mul
+        self.check_loops(call=2)
+        res = self.meta_interp(main, [1, 10])
+        assert res == main(1, 10)
+        self.check_loops(call=0)
+
 
 class TestLLtype(BaseLLtypeTests, LLJitMixin):
     pass
diff --git a/pypy/jit/metainterp/test/test_dict.py b/pypy/jit/metainterp/test/test_dict.py
--- a/pypy/jit/metainterp/test/test_dict.py
+++ b/pypy/jit/metainterp/test/test_dict.py
@@ -153,11 +153,7 @@
 
         res = self.meta_interp(f, [100], listops=True)
         assert res == f(50)
-        # XXX: ideally there would be 7 calls here, but repeated CALL_PURE with
-        # the same arguments are not folded, because we have conflicting
-        # definitions of pure, once strhash can be appropriately folded
-        # this should be decreased to seven.
-        self.check_loops({"call": 8, "guard_false": 1, "guard_no_exception": 6,
+        self.check_loops({"call": 7, "guard_false": 1, "guard_no_exception": 6,
                           "guard_true": 1, "int_and": 1, "int_gt": 1,
                           "int_is_true": 1, "int_sub": 1, "jump": 1,
                           "new_with_vtable": 1, "setfield_gc": 1})
diff --git a/pypy/jit/metainterp/test/test_heapcache.py b/pypy/jit/metainterp/test/test_heapcache.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/metainterp/test/test_heapcache.py
@@ -0,0 +1,365 @@
+from pypy.jit.metainterp.heapcache import HeapCache
+from pypy.jit.metainterp.resoperation import rop
+from pypy.jit.metainterp.history import ConstInt
+
+box1 = object()
+box2 = object()
+box3 = object()
+box4 = object()
+lengthbox1 = object()
+lengthbox2 = object()
+descr1 = object()
+descr2 = object()
+descr3 = object()
+
+index1 = ConstInt(0)
+index2 = ConstInt(1)
+
+
+class FakeEffektinfo(object):
+    EF_ELIDABLE_CANNOT_RAISE           = 0 #elidable function (and cannot raise)
+    EF_LOOPINVARIANT                   = 1 #special: call it only once per loop
+    EF_CANNOT_RAISE                    = 2 #a function which cannot raise
+    EF_ELIDABLE_CAN_RAISE              = 3 #elidable function (but can raise)
+    EF_CAN_RAISE                       = 4 #normal function (can raise)
+    EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE = 5 #can raise and force virtualizables
+    EF_RANDOM_EFFECTS                  = 6 #can do whatever
+
+    OS_ARRAYCOPY = 0
+
+    def __init__(self, extraeffect, oopspecindex):
+        self.extraeffect = extraeffect
+        self.oopspecindex = oopspecindex
+
+class FakeCallDescr(object):
+    def __init__(self, extraeffect, oopspecindex=None):
+        self.extraeffect = extraeffect
+        self.oopspecindex = oopspecindex
+
+    def get_extra_info(self):
+        return FakeEffektinfo(self.extraeffect, self.oopspecindex)
+
+class TestHeapCache(object):
+    def test_known_class_box(self):
+        h = HeapCache()
+        assert not h.is_class_known(1)
+        assert not h.is_class_known(2)
+        h.class_now_known(1)
+        assert h.is_class_known(1)
+        assert not h.is_class_known(2)
+
+        h.reset()
+        assert not h.is_class_known(1)
+        assert not h.is_class_known(2)
+
+    def test_nonstandard_virtualizable(self):
+        h = HeapCache()
+        assert not h.is_nonstandard_virtualizable(1)
+        assert not h.is_nonstandard_virtualizable(2)
+        h.nonstandard_virtualizables_now_known(1)
+        assert h.is_nonstandard_virtualizable(1)
+        assert not h.is_nonstandard_virtualizable(2)
+
+        h.reset()
+        assert not h.is_nonstandard_virtualizable(1)
+        assert not h.is_nonstandard_virtualizable(2)
+
+
+    def test_heapcache_fields(self):
+        h = HeapCache()
+        assert h.getfield(box1, descr1) is None
+        assert h.getfield(box1, descr2) is None
+        h.setfield(box1, descr1, box2)
+        assert h.getfield(box1, descr1) is box2
+        assert h.getfield(box1, descr2) is None
+        h.setfield(box1, descr2, box3)
+        assert h.getfield(box1, descr1) is box2
+        assert h.getfield(box1, descr2) is box3
+        h.setfield(box1, descr1, box3)
+        assert h.getfield(box1, descr1) is box3
+        assert h.getfield(box1, descr2) is box3
+        h.setfield(box3, descr1, box1)
+        assert h.getfield(box3, descr1) is box1
+        assert h.getfield(box1, descr1) is None
+        assert h.getfield(box1, descr2) is box3
+
+        h.reset()
+        assert h.getfield(box1, descr1) is None
+        assert h.getfield(box1, descr2) is None
+        assert h.getfield(box3, descr1) is None
+
+    def test_heapcache_read_fields_multiple(self):
+        h = HeapCache()
+        h.getfield_now_known(box1, descr1, box2)
+        h.getfield_now_known(box3, descr1, box4)
+        assert h.getfield(box1, descr1) is box2
+        assert h.getfield(box1, descr2) is None
+        assert h.getfield(box3, descr1) is box4
+        assert h.getfield(box3, descr2) is None
+
+        h.reset()
+        assert h.getfield(box1, descr1) is None
+        assert h.getfield(box1, descr2) is None
+        assert h.getfield(box3, descr1) is None
+        assert h.getfield(box3, descr2) is None
+
+    def test_heapcache_write_fields_multiple(self):
+        h = HeapCache()
+        h.setfield(box1, descr1, box2)
+        assert h.getfield(box1, descr1) is box2
+        h.setfield(box3, descr1, box4)
+        assert h.getfield(box3, descr1) is box4
+        assert h.getfield(box1, descr1) is None # box1 and box3 can alias
+
+        h = HeapCache()
+        h.new(box1)
+        h.setfield(box1, descr1, box2)
+        assert h.getfield(box1, descr1) is box2
+        h.setfield(box3, descr1, box4)
+        assert h.getfield(box3, descr1) is box4
+        assert h.getfield(box1, descr1) is None # box1 and box3 can alias
+
+        h = HeapCache()
+        h.new(box1)
+        h.new(box3)
+        h.setfield(box1, descr1, box2)
+        assert h.getfield(box1, descr1) is box2
+        h.setfield(box3, descr1, box4)
+        assert h.getfield(box3, descr1) is box4
+        assert h.getfield(box1, descr1) is box2 # box1 and box3 cannot alias
+        h.setfield(box1, descr1, box3)
+        assert h.getfield(box1, descr1) is box3
+
+
+    def test_heapcache_arrays(self):
+        h = HeapCache()
+        assert h.getarrayitem(box1, descr1, index1) is None
+        assert h.getarrayitem(box1, descr2, index1) is None
+        assert h.getarrayitem(box1, descr1, index2) is None
+        assert h.getarrayitem(box1, descr2, index2) is None
+
+        h.setarrayitem(box1, descr1, index1, box2)
+        assert h.getarrayitem(box1, descr1, index1) is box2
+        assert h.getarrayitem(box1, descr2, index1) is None
+        assert h.getarrayitem(box1, descr1, index2) is None
+        assert h.getarrayitem(box1, descr2, index2) is None
+        h.setarrayitem(box1, descr1, index2, box4)
+        assert h.getarrayitem(box1, descr1, index1) is box2
+        assert h.getarrayitem(box1, descr2, index1) is None
+        assert h.getarrayitem(box1, descr1, index2) is box4
+        assert h.getarrayitem(box1, descr2, index2) is None
+
+        h.setarrayitem(box1, descr2, index1, box3)
+        assert h.getarrayitem(box1, descr1, index1) is box2
+        assert h.getarrayitem(box1, descr2, index1) is box3
+        assert h.getarrayitem(box1, descr1, index2) is box4
+        assert h.getarrayitem(box1, descr2, index2) is None
+
+        h.setarrayitem(box1, descr1, index1, box3)
+        assert h.getarrayitem(box1, descr1, index1) is box3
+        assert h.getarrayitem(box1, descr2, index1) is box3
+        assert h.getarrayitem(box1, descr1, index2) is box4
+        assert h.getarrayitem(box1, descr2, index2) is None
+
+        h.setarrayitem(box3, descr1, index1, box1)
+        assert h.getarrayitem(box3, descr1, index1) is box1
+        assert h.getarrayitem(box1, descr1, index1) is None
+        assert h.getarrayitem(box1, descr2, index1) is box3
+        assert h.getarrayitem(box1, descr1, index2) is box4
+        assert h.getarrayitem(box1, descr2, index2) is None
+
+        h.reset()
+        assert h.getarrayitem(box1, descr1, index1) is None
+        assert h.getarrayitem(box1, descr2, index1) is None
+        assert h.getarrayitem(box3, descr1, index1) is None
+
+    def test_heapcache_array_nonconst_index(self):
+        h = HeapCache()
+        h.setarrayitem(box1, descr1, index1, box2)
+        h.setarrayitem(box1, descr1, index2, box4)
+        assert h.getarrayitem(box1, descr1, index1) is box2
+        assert h.getarrayitem(box1, descr1, index2) is box4
+        h.setarrayitem(box1, descr1, box2, box3)
+        assert h.getarrayitem(box1, descr1, index1) is None
+        assert h.getarrayitem(box1, descr1, index2) is None
+
+    def test_heapcache_read_fields_multiple_array(self):
+        h = HeapCache()
+        h.getarrayitem_now_known(box1, descr1, index1, box2)
+        h.getarrayitem_now_known(box3, descr1, index1, box4)
+        assert h.getarrayitem(box1, descr1, index1) is box2
+        assert h.getarrayitem(box1, descr2, index1) is None
+        assert h.getarrayitem(box3, descr1, index1) is box4
+        assert h.getarrayitem(box3, descr2, index1) is None
+
+        h.reset()
+        assert h.getarrayitem(box1, descr1, index1) is None
+        assert h.getarrayitem(box1, descr2, index1) is None
+        assert h.getarrayitem(box3, descr1, index1) is None
+        assert h.getarrayitem(box3, descr2, index1) is None
+
+    def test_heapcache_write_fields_multiple_array(self):
+        h = HeapCache()
+        h.setarrayitem(box1, descr1, index1, box2)
+        assert h.getarrayitem(box1, descr1, index1) is box2
+        h.setarrayitem(box3, descr1, index1, box4)
+        assert h.getarrayitem(box3, descr1, index1) is box4
+        assert h.getarrayitem(box1, descr1, index1) is None # box1 and box3 can alias
+
+        h = HeapCache()
+        h.new(box1)
+        h.setarrayitem(box1, descr1, index1, box2)
+        assert h.getarrayitem(box1, descr1, index1) is box2
+        h.setarrayitem(box3, descr1, index1, box4)
+        assert h.getarrayitem(box3, descr1, index1) is box4
+        assert h.getarrayitem(box1, descr1, index1) is None # box1 and box3 can alias
+
+        h = HeapCache()
+        h.new(box1)
+        h.new(box3)
+        h.setarrayitem(box1, descr1, index1, box2)
+        assert h.getarrayitem(box1, descr1, index1) is box2
+        h.setarrayitem(box3, descr1, index1, box4)
+        assert h.getarrayitem(box3, descr1, index1) is box4
+        assert h.getarrayitem(box1, descr1, index1) is box2 # box1 and box3 cannot alias
+        h.setarrayitem(box1, descr1, index1, box3)
+        assert h.getarrayitem(box3, descr1, index1) is box4
+        assert h.getarrayitem(box1, descr1, index1) is box3 # box1 and box3 cannot alias
+
+    def test_length_cache(self):
+        h = HeapCache()
+        h.new_array(box1, lengthbox1)
+        assert h.arraylen(box1) is lengthbox1
+
+        assert h.arraylen(box2) is None
+        h.arraylen_now_known(box2, lengthbox2)
+        assert h.arraylen(box2) is lengthbox2
+
+
+    def test_invalidate_cache(self):
+        h = HeapCache()
+        h.setfield(box1, descr1, box2)
+        h.setarrayitem(box1, descr1, index1, box2)
+        h.setarrayitem(box1, descr1, index2, box4)
+        h.invalidate_caches(rop.INT_ADD, None, [])
+        h.invalidate_caches(rop.INT_ADD_OVF, None, [])
+        h.invalidate_caches(rop.SETFIELD_RAW, None, [])
+        h.invalidate_caches(rop.SETARRAYITEM_RAW, None, [])
+        assert h.getfield(box1, descr1) is box2
+        assert h.getarrayitem(box1, descr1, index1) is box2
+        assert h.getarrayitem(box1, descr1, index2) is box4
+
+        h.invalidate_caches(
+            rop.CALL, FakeCallDescr(FakeEffektinfo.EF_ELIDABLE_CANNOT_RAISE), [])
+        assert h.getfield(box1, descr1) is box2
+        assert h.getarrayitem(box1, descr1, index1) is box2
+        assert h.getarrayitem(box1, descr1, index2) is box4
+
+        h.invalidate_caches(
+            rop.CALL_LOOPINVARIANT, FakeCallDescr(FakeEffektinfo.EF_LOOPINVARIANT), [])
+
+        h.invalidate_caches(
+            rop.CALL, FakeCallDescr(FakeEffektinfo.EF_RANDOM_EFFECTS), [])
+        assert h.getfield(box1, descr1) is None
+        assert h.getarrayitem(box1, descr1, index1) is None
+        assert h.getarrayitem(box1, descr1, index2) is None
+
+
+    def test_replace_box(self):
+        h = HeapCache()
+        h.setfield(box1, descr1, box2)
+        h.setfield(box1, descr2, box3)
+        h.setfield(box2, descr3, box3)
+        h.replace_box(box1, box4)
+        assert h.getfield(box1, descr1) is None
+        assert h.getfield(box1, descr2) is None
+        assert h.getfield(box4, descr1) is box2
+        assert h.getfield(box4, descr2) is box3
+        assert h.getfield(box2, descr3) is box3
+
+    def test_replace_box_array(self):
+        h = HeapCache()
+        h.setarrayitem(box1, descr1, index1, box2)
+        h.setarrayitem(box1, descr2, index1, box3)
+        h.arraylen_now_known(box1, lengthbox1)
+        h.setarrayitem(box2, descr1, index2, box1)
+        h.setarrayitem(box3, descr2, index2, box1)
+        h.setarrayitem(box2, descr3, index2, box3)
+        h.replace_box(box1, box4)
+        assert h.getarrayitem(box1, descr1, index1) is None
+        assert h.getarrayitem(box1, descr2, index1) is None
+        assert h.arraylen(box1) is None
+        assert h.arraylen(box4) is lengthbox1
+        assert h.getarrayitem(box4, descr1, index1) is box2
+        assert h.getarrayitem(box4, descr2, index1) is box3
+        assert h.getarrayitem(box2, descr1, index2) is box4
+        assert h.getarrayitem(box3, descr2, index2) is box4
+        assert h.getarrayitem(box2, descr3, index2) is box3
+
+        h.replace_box(lengthbox1, lengthbox2)
+        assert h.arraylen(box4) is lengthbox2
+
+    def test_ll_arraycopy(self):
+        h = HeapCache()
+        h.new_array(box1, lengthbox1)
+        h.setarrayitem(box1, descr1, index1, box2)
+        h.new_array(box2, lengthbox1)
+        # Just need the destination box for this call
+        h.invalidate_caches(
+            rop.CALL,
+            FakeCallDescr(FakeEffektinfo.EF_CANNOT_RAISE, FakeEffektinfo.OS_ARRAYCOPY),
+            [None, None, box2, None, None]
+        )
+        assert h.getarrayitem(box1, descr1, index1) is box2
+        h.invalidate_caches(
+            rop.CALL,
+            FakeCallDescr(FakeEffektinfo.EF_CANNOT_RAISE, FakeEffektinfo.OS_ARRAYCOPY),
+            [None, None, box3, None, None]
+        )
+        assert h.getarrayitem(box1, descr1, index1) is None
+
+        h.setarrayitem(box4, descr1, index1, box2)
+        assert h.getarrayitem(box4, descr1, index1) is box2
+        h.invalidate_caches(
+            rop.CALL,
+            FakeCallDescr(FakeEffektinfo.EF_CANNOT_RAISE, FakeEffektinfo.OS_ARRAYCOPY),
+            [None, None, box2, None, None]
+        )
+        assert h.getarrayitem(box4, descr1, index1) is None
+
+    def test_unescaped(self):
+        h = HeapCache()
+        assert not h.is_unescaped(box1)
+        h.new(box2)
+        assert h.is_unescaped(box2)
+        h.invalidate_caches(rop.SETFIELD_GC, None, [box2, box1])
+        assert h.is_unescaped(box2)
+        h.invalidate_caches(rop.SETFIELD_GC, None, [box1, box2])
+        assert not h.is_unescaped(box2)
+
+    def test_unescaped_testing(self):
+        h = HeapCache()
+        h.new(box1)
+        h.new(box2)
+        assert h.is_unescaped(box1)
+        assert h.is_unescaped(box2)
+        # Putting a virtual inside of another virtual doesn't escape it.
+        h.invalidate_caches(rop.SETFIELD_GC, None, [box1, box2])
+        assert h.is_unescaped(box2)
+        # Reading a field from a virtual doesn't escape it.
+        h.invalidate_caches(rop.GETFIELD_GC, None, [box1])
+        assert h.is_unescaped(box1)
+        # Escaping a virtual transitively escapes anything inside of it.
+        assert not h.is_unescaped(box3)
+        h.invalidate_caches(rop.SETFIELD_GC, None, [box3, box1])
+        assert not h.is_unescaped(box1)
+        assert not h.is_unescaped(box2)
+
+    def test_unescaped_array(self):
+        h = HeapCache()
+        h.new_array(box1, lengthbox1)
+        assert h.is_unescaped(box1)
+        h.invalidate_caches(rop.SETARRAYITEM_GC, None, [box1, index1, box2])
+        assert h.is_unescaped(box1)
+        h.invalidate_caches(rop.SETARRAYITEM_GC, None, [box2, index1, box1])
+        assert not h.is_unescaped(box1)
\ No newline at end of file
diff --git a/pypy/jit/metainterp/test/test_list.py b/pypy/jit/metainterp/test/test_list.py
--- a/pypy/jit/metainterp/test/test_list.py
+++ b/pypy/jit/metainterp/test/test_list.py
@@ -34,7 +34,7 @@
                 l = [x + 1]
                 n -= 1
             return l[0]
-        
+
         res = self.meta_interp(f, [10], listops=True)
         assert res == f(10)
         self.check_all_virtualized()
@@ -60,7 +60,7 @@
 
     def test_ll_fixed_setitem_fast(self):
         jitdriver = JitDriver(greens = [], reds = ['n', 'l'])
-        
+
         def f(n):
             l = [1, 2, 3]
 
@@ -116,7 +116,7 @@
         assert res == f(10)
         py.test.skip("'[non-null] * n' gives a residual call so far")
         self.check_loops(setarrayitem_gc=0, getarrayitem_gc=0, call=0)
-    
+
     def test_arraycopy_simpleoptimize(self):
         def f():
             l = [1, 2, 3, 4]
@@ -208,6 +208,26 @@
         assert res == f(15)
         self.check_loops(guard_exception=0)
 
+    def test_virtual_resize(self):
+        jitdriver = JitDriver(greens = [], reds = ['n', 's'])
+        def f(n):
+            s = 0
+            while n > 0:
+                jitdriver.jit_merge_point(n=n, s=s)
+                lst = []
+                lst += [1]
+                n -= len(lst)
+                s += lst[0]
+                lst.pop()
+                lst.append(1)
+                s /= lst.pop()
+            return s
+        res = self.meta_interp(f, [15], listops=True)
+        assert res == f(15)
+        self.check_loops({"int_add": 1, "int_sub": 1, "int_gt": 1,
+                          "guard_true": 1, "jump": 1})
+
+
 class TestOOtype(ListTests, OOJitMixin):
     pass
 
@@ -236,8 +256,6 @@
             return a * b
         res = self.meta_interp(f, [37])
         assert res == f(37)
-        # There is the one actual field on a, plus 2 getfield's from the list
-        # itself, 1 to get the length (which is then incremented and passed to
-        # the resize func), and then a read of the items field to actually
-        # perform the setarrayitem on
-        self.check_loops(getfield_gc=5, everywhere=True)
+        # There is the one actual field on a, plus several fields on the list
+        # itself
+        self.check_loops(getfield_gc=10, everywhere=True)
diff --git a/pypy/jit/metainterp/test/test_slist.py b/pypy/jit/metainterp/test/test_slist.py
--- a/pypy/jit/metainterp/test/test_slist.py
+++ b/pypy/jit/metainterp/test/test_slist.py
@@ -5,7 +5,6 @@
 class ListTests(object):
 
     def test_basic_list(self):
-        py.test.skip("not yet")
         myjitdriver = JitDriver(greens = [], reds = ['n', 'lst'])
         def f(n):
             lst = []
@@ -34,7 +33,7 @@
             return m
         res = self.interp_operations(f, [11], listops=True)
         assert res == 49
-        self.check_operations_history(call=5)
+        self.check_operations_history(call=3)
 
     def test_list_of_voids(self):
         myjitdriver = JitDriver(greens = [], reds = ['n', 'lst'])
@@ -93,7 +92,7 @@
             return x
         res = self.meta_interp(f, [-2], listops=True)
         assert res == 41
-        self.check_loops(call=1, guard_value=0)
+        self.check_loops(call=0, guard_value=0)
 
 # we don't support resizable lists on ootype
 #class TestOOtype(ListTests, OOJitMixin):
diff --git a/pypy/jit/metainterp/test/test_string.py b/pypy/jit/metainterp/test/test_string.py
--- a/pypy/jit/metainterp/test/test_string.py
+++ b/pypy/jit/metainterp/test/test_string.py
@@ -27,7 +27,7 @@
             return i
         res = self.meta_interp(f, [10, True, _str('h')], listops=True)
         assert res == 5
-        self.check_loops(**{self.CALL: 1, self.CALL_PURE: 0})
+        self.check_loops(**{self.CALL: 1, self.CALL_PURE: 0, 'everywhere': True})
 
     def test_eq_folded(self):
         _str = self._str
@@ -327,7 +327,7 @@
     def test_str_slice_len_surviving(self):
         _str = self._str
         longstring = _str("Unrolling Trouble")
-        mydriver = JitDriver(reds = ['i', 'a', 'sa'], greens = []) 
+        mydriver = JitDriver(reds = ['i', 'a', 'sa'], greens = [])
         def f(a):
             i = sa = a
             while i < len(longstring):
@@ -343,7 +343,7 @@
         fillers = _str("abcdefghijklmnopqrstuvwxyz")
         data = _str("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
 
-        mydriver = JitDriver(reds = ['line', 'noise', 'res'], greens = []) 
+        mydriver = JitDriver(reds = ['line', 'noise', 'res'], greens = [])
         def f():
             line = data
             noise = fillers
@@ -370,7 +370,7 @@
             def __init__(self, value):
                 self.value = value
         mydriver = JitDriver(reds = ['ratio', 'line', 'noise', 'res'],
-                             greens = []) 
+                             greens = [])
         def f():
             line = Str(data)
             noise = Str(fillers)
@@ -408,7 +408,7 @@
             return len(sa)
         assert self.meta_interp(f, [16]) == f(16)
 
-    def test_loop_invariant_string_slize(self):
+    def test_loop_invariant_string_slice(self):
         _str = self._str
         mydriver = JitDriver(reds = ['i', 'n', 'sa', 's', 's1'], greens = [])
         def f(n, c):
@@ -425,7 +425,7 @@
             return sa
         assert self.meta_interp(f, [16, 'a']) == f(16, 'a')
 
-    def test_loop_invariant_string_slize_boxed(self):
+    def test_loop_invariant_string_slice_boxed(self):
         class Str(object):
             def __init__(self, value):
                 self.value = value
@@ -445,7 +445,7 @@
             return sa
         assert self.meta_interp(f, [16, 'a']) == f(16, 'a')
 
-    def test_loop_invariant_string_slize_in_array(self):
+    def test_loop_invariant_string_slice_in_array(self):
         _str = self._str
         mydriver = JitDriver(reds = ['i', 'n', 'sa', 's', 's1'], greens = [])
         def f(n, c):
@@ -513,7 +513,7 @@
                 m -= 1
             return 42
         self.meta_interp(f, [6, 7])
-        self.check_loops(call=3,    # str(), _str(), escape()
+        self.check_loops(call=1,    # escape()
                          newunicode=1, unicodegetitem=0,
                          unicodesetitem=1, copyunicodecontent=1)
 
@@ -536,3 +536,27 @@
         self.check_loops(call_pure=0, call=1,
                          newunicode=0, unicodegetitem=0,
                          unicodesetitem=0, copyunicodecontent=0)
+
+    def test_join_chars(self):
+        jitdriver = JitDriver(reds=['a', 'b', 'c', 'i'], greens=[])
+        def f(a, b, c):
+            i = 0
+            while i < 10:
+                jitdriver.jit_merge_point(a=a, b=b, c=c, i=i)
+                x = []
+                if a:
+                    x.append("a")
+                if b:
+                    x.append("b")
+                if c:
+                    x.append("c")
+                i += len("".join(x))
+            return i
+        res = self.meta_interp(f, [1, 1, 1])
+        assert res == f(True, True, True)
+        # The "".join should be unrolled, since the length of x is known since
+        # it is virtual, ensure there are no calls to ll_join_chars, or
+        # allocations.
+        self.check_loops({
+            "guard_true": 5, "int_is_true": 3, "int_lt": 2, "int_add": 2, "jump": 2,
+        }, everywhere=True)
diff --git a/pypy/jit/metainterp/test/test_tracingopts.py b/pypy/jit/metainterp/test/test_tracingopts.py
--- a/pypy/jit/metainterp/test/test_tracingopts.py
+++ b/pypy/jit/metainterp/test/test_tracingopts.py
@@ -1,7 +1,10 @@
+import sys
+
+from pypy.jit.metainterp.test.support import LLJitMixin
+from pypy.rlib import jit
+from pypy.rlib.rarithmetic import ovfcheck
+
 import py
-import sys
-from pypy.rlib import jit
-from pypy.jit.metainterp.test.support import LLJitMixin
 
 
 class TestLLtype(LLJitMixin):
@@ -257,6 +260,28 @@
         self.check_operations_history(setarrayitem_gc=2, setfield_gc=2,
                                       getarrayitem_gc=0, getfield_gc=2)
 
+    def test_promote_changes_array_cache(self):
+        a1 = [0, 0]
+        a2 = [0, 0]
+        def fn(n):
+            if n > 0:
+                a = a1
+            else:
+                a = a2
+            a[0] = n
+            jit.hint(n, promote=True)
+            x1 = a[0]
+            jit.hint(x1, promote=True)
+            a[n - n] = n + 1
+            return a[0] + x1
+        res = self.interp_operations(fn, [7])
+        assert res == 7 + 7 + 1
+        self.check_operations_history(getarrayitem_gc=0, guard_value=1)
+        res = self.interp_operations(fn, [-7])
+        assert res == -7 - 7 + 1
+        self.check_operations_history(getarrayitem_gc=0, guard_value=1)
+
+
     def test_list_caching(self):
         a1 = [0, 0]
         a2 = [0, 0]
@@ -357,7 +382,7 @@
         assert res == f(10, 1, 1)
         self.check_history(getarrayitem_gc=0, getfield_gc=0)
 
-    def test_heap_caching_pure(self):
+    def test_heap_caching_array_pure(self):
         class A(object):
             pass
         p1 = A()
@@ -405,3 +430,164 @@
         assert res == -7 + 7
         self.check_operations_history(getfield_gc=0)
         return
+
+    def test_heap_caching_multiple_objects(self):
+        class Gbl(object):
+            pass
+        g = Gbl()
+        class A(object):
+            pass
+        a1 = A()
+        g.a1 = a1
+        a1.x = 7
+        a2 = A()
+        g.a2 = a2
+        a2.x = 7
+        def gn(a1, a2):
+            return a1.x + a2.x
+        def fn(n):
+            if n < 0:
+                a1 = A()
+                g.a1 = a1
+                a1.x = n
+                a2 = A()
+                g.a2 = a2
+                a2.x = n - 1
+            else:
+                a1 = g.a1
+                a2 = g.a2
+            return a1.x + a2.x + gn(a1, a2)
+        res = self.interp_operations(fn, [-7])
+        assert res == 2 * -7 + 2 * -8
+        self.check_operations_history(setfield_gc=4, getfield_gc=0)
+        res = self.interp_operations(fn, [7])
+        assert res == 4 * 7
+        self.check_operations_history(getfield_gc=4)
+
+    def test_heap_caching_multiple_tuples(self):
+        class Gbl(object):
+            pass
+        g = Gbl()
+        def gn(a1, a2):
+            return a1[0] + a2[0]
+        def fn(n):
+            a1 = (n, )
+            g.a = a1
+            a2 = (n - 1, )
+            g.a = a2
+            jit.promote(n)
+            return a1[0] + a2[0] + gn(a1, a2)
+        res = self.interp_operations(fn, [7])
+        assert res == 2 * 7 + 2 * 6
+        self.check_operations_history(getfield_gc_pure=0)
+        res = self.interp_operations(fn, [-7])
+        assert res == 2 * -7 + 2 * -8
+        self.check_operations_history(getfield_gc_pure=0)
+
+    def test_heap_caching_multiple_arrays(self):
+        class Gbl(object):
+            pass
+        g = Gbl()
+        def fn(n):
+            a1 = [n, n, n]
+            g.a = a1
+            a1[0] = n
+            a2 = [n, n, n]
+            g.a = a2
+            a2[0] = n - 1
+            return a1[0] + a2[0] + a1[0] + a2[0]
+        res = self.interp_operations(fn, [7])
+        assert res == 2 * 7 + 2 * 6
+        self.check_operations_history(getarrayitem_gc=0)
+        res = self.interp_operations(fn, [-7])
+        assert res == 2 * -7 + 2 * -8
+        self.check_operations_history(getarrayitem_gc=0)
+
+    def test_heap_caching_multiple_arrays_getarrayitem(self):
+        class Gbl(object):
+            pass
+        g = Gbl()
+        g.a1 = [7, 8, 9]
+        g.a2 = [8, 9, 10, 11]
+
+        def fn(i):
+            if i < 0:
+                g.a1 = [7, 8, 9]
+                g.a2 = [7, 8, 9, 10]
+            jit.promote(i)
+            a1 = g.a1
+            a1[i + 1] = 15 # make lists mutable
+            a2 = g.a2
+            a2[i + 1] = 19
+            return a1[i] + a2[i] + a1[i] + a2[i]
+        res = self.interp_operations(fn, [0])
+        assert res == 2 * 7 + 2 * 8
+        self.check_operations_history(getarrayitem_gc=2)
+
+
+    def test_heap_caching_multiple_lists(self):
+        class Gbl(object):
+            pass
+        g = Gbl()
+        g.l = []
+        def fn(n):
+            if n < -100:
+                g.l.append(1)
+            a1 = [n, n, n]
+            g.l = a1
+            a1[0] = n
+            a2 = [n, n, n]
+            g.l = a2
+            a2[0] = n - 1
+            return a1[0] + a2[0] + a1[0] + a2[0]
+        res = self.interp_operations(fn, [7])
+        assert res == 2 * 7 + 2 * 6
+        self.check_operations_history(getarrayitem_gc=0, getfield_gc=0)
+        res = self.interp_operations(fn, [-7])
+        assert res == 2 * -7 + 2 * -8
+        self.check_operations_history(getarrayitem_gc=0, getfield_gc=0)
+
+    def test_length_caching(self):
+        class Gbl(object):
+            pass
+        g = Gbl()
+        g.a = [0] * 7
+        def fn(n):
+            a = g.a
+            res = len(a) + len(a)
+            a1 = [0] * n
+            g.a = a1
+            return len(a1) + res
+        res = self.interp_operations(fn, [7])
+        assert res == 7 * 3
+        self.check_operations_history(arraylen_gc=1)
+
+    def test_arraycopy(self):
+        class Gbl(object):
+            pass
+        g = Gbl()
+        g.a = [0] * 7
+        def fn(n):
+            assert n >= 0
+            a = g.a
+            x = [0] * n
+            x[2] = 21
+            return len(a[:n]) + x[2]
+        res = self.interp_operations(fn, [3])
+        assert res == 24
+        self.check_operations_history(getarrayitem_gc=0)
+
+    def test_fold_int_add_ovf(self):
+        def fn(n):
+            jit.promote(n)
+            try:
+                n = ovfcheck(n + 1)
+            except OverflowError:
+                return 12
+            else:
+                return n
+        res = self.interp_operations(fn, [3])
+        assert res == 4
+        self.check_operations_history(int_add_ovf=0)
+        res = self.interp_operations(fn, [sys.maxint])
+        assert res == 12
\ No newline at end of file
diff --git a/pypy/jit/metainterp/warmstate.py b/pypy/jit/metainterp/warmstate.py
--- a/pypy/jit/metainterp/warmstate.py
+++ b/pypy/jit/metainterp/warmstate.py
@@ -367,9 +367,9 @@
             # ---------- execute assembler ----------
             while True:     # until interrupted by an exception
                 metainterp_sd.profiler.start_running()
-                debug_start("jit-running")
+                #debug_start("jit-running")
                 fail_descr = warmrunnerdesc.execute_token(loop_token)
-                debug_stop("jit-running")
+                #debug_stop("jit-running")
                 metainterp_sd.profiler.end_running()
                 loop_token = None     # for test_memmgr
                 if vinfo is not None:
diff --git a/pypy/jit/tl/pypyjit.py b/pypy/jit/tl/pypyjit.py
--- a/pypy/jit/tl/pypyjit.py
+++ b/pypy/jit/tl/pypyjit.py
@@ -40,7 +40,7 @@
 config.objspace.usemodules.array = False
 config.objspace.usemodules._weakref = True
 config.objspace.usemodules._sre = False
-config.objspace.usemodules._lsprof = True
+config.objspace.usemodules._lsprof = False
 #
 config.objspace.usemodules._ffi = True
 config.objspace.usemodules.micronumpy = False
@@ -77,7 +77,7 @@
 
 def read_code():
     from pypy.module.marshal.interp_marshal import dumps
-    
+
     filename = 'pypyjit_demo.py'
     source = readfile(filename)
     ec = space.getexecutioncontext()
diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py
--- a/pypy/module/__pypy__/__init__.py
+++ b/pypy/module/__pypy__/__init__.py
@@ -8,6 +8,7 @@
     appleveldefs = {}
 
     interpleveldefs = {
+        "StringBuilder": "interp_builders.W_StringBuilder",
         "UnicodeBuilder": "interp_builders.W_UnicodeBuilder",
     }
 
diff --git a/pypy/module/__pypy__/interp_builders.py b/pypy/module/__pypy__/interp_builders.py
--- a/pypy/module/__pypy__/interp_builders.py
+++ b/pypy/module/__pypy__/interp_builders.py
@@ -2,49 +2,55 @@
 from pypy.interpreter.error import OperationError
 from pypy.interpreter.gateway import interp2app, unwrap_spec
 from pypy.interpreter.typedef import TypeDef
-from pypy.rlib.rstring import UnicodeBuilder
+from pypy.rlib.rstring import UnicodeBuilder, StringBuilder
+from pypy.tool.sourcetools import func_with_new_name
 
 
-class W_UnicodeBuilder(Wrappable):
-    def __init__(self, space, size):
-        if size < 0:
-            self.builder = UnicodeBuilder()
-        else:
-            self.builder = UnicodeBuilder(size)
-        self.done = False
+def create_builder(name, strtype, builder_cls):
+    class W_Builder(Wrappable):
+        def __init__(self, space, size):
+            if size < 0:
+                self.builder = builder_cls()
+            else:
+                self.builder = builder_cls(size)
 
-    def _check_done(self, space):
-        if self.done:
-            raise OperationError(space.w_ValueError, space.wrap("Can't operate on a done builder"))
+        def _check_done(self, space):
+            if self.builder is None:
+                raise OperationError(space.w_ValueError, space.wrap("Can't operate on a done builder"))
 
-    @unwrap_spec(size=int)
-    def descr__new__(space, w_subtype, size=-1):
-        return W_UnicodeBuilder(space, size)
+        @unwrap_spec(size=int)
+        def descr__new__(space, w_subtype, size=-1):
+            return W_Builder(space, size)
 
-    @unwrap_spec(s=unicode)
-    def descr_append(self, space, s):
-        self._check_done(space)
-        self.builder.append(s)
+        @unwrap_spec(s=strtype)
+        def descr_append(self, space, s):
+            self._check_done(space)
+            self.builder.append(s)
 
-    @unwrap_spec(s=unicode, start=int, end=int)
-    def descr_append_slice(self, space, s, start, end):
-        self._check_done(space)
-        if not 0 <= start <= end <= len(s):
-            raise OperationError(space.w_ValueError, space.wrap("bad start/stop"))
-        self.builder.append_slice(s, start, end)
+        @unwrap_spec(s=strtype, start=int, end=int)
+        def descr_append_slice(self, space, s, start, end):
+            self._check_done(space)
+            if not 0 <= start <= end <= len(s):
+                raise OperationError(space.w_ValueError, space.wrap("bad start/stop"))
+            self.builder.append_slice(s, start, end)
 
-    def descr_build(self, space):
-        self._check_done(space)
-        w_s = space.wrap(self.builder.build())
-        self.done = True
-        return w_s
+        def descr_build(self, space):
+            self._check_done(space)
+            w_s = space.wrap(self.builder.build())
+            self.builder = None
+            return w_s
 
+    W_Builder.__name__ = "W_%s" % name
+    W_Builder.typedef = TypeDef(name,
+        __new__ = interp2app(func_with_new_name(
+                                    W_Builder.descr__new__.im_func,
+                                    '%s_new' % (name,))),
+        append = interp2app(W_Builder.descr_append),
+        append_slice = interp2app(W_Builder.descr_append_slice),
+        build = interp2app(W_Builder.descr_build),
+    )
+    W_Builder.typedef.acceptable_as_base_class = False
+    return W_Builder
 
-W_UnicodeBuilder.typedef = TypeDef("UnicodeBuilder",
-    __new__ = interp2app(W_UnicodeBuilder.descr__new__.im_func),
-
-    append = interp2app(W_UnicodeBuilder.descr_append),
-    append_slice = interp2app(W_UnicodeBuilder.descr_append_slice),
-    build = interp2app(W_UnicodeBuilder.descr_build),
-)
-W_UnicodeBuilder.typedef.acceptable_as_base_class = False
+W_StringBuilder = create_builder("StringBuilder", str, StringBuilder)
+W_UnicodeBuilder = create_builder("UnicodeBuilder", unicode, UnicodeBuilder)
diff --git a/pypy/module/__pypy__/test/test_builders.py b/pypy/module/__pypy__/test/test_builders.py
--- a/pypy/module/__pypy__/test/test_builders.py
+++ b/pypy/module/__pypy__/test/test_builders.py
@@ -31,4 +31,14 @@
         raises(ValueError, b.append_slice, u"1", 2, 1)
         s = b.build()
         assert s == "cde"
-        raises(ValueError, b.append_slice, u"abc", 1, 2)
\ No newline at end of file
+        raises(ValueError, b.append_slice, u"abc", 1, 2)
+
+    def test_stringbuilder(self):
+        from __pypy__.builders import StringBuilder
+        b = StringBuilder()
+        b.append("abc")
+        b.append("123")
+        b.append("you and me")
+        s = b.build()
+        assert s == "abc123you and me"
+        raises(ValueError, b.build)
\ No newline at end of file
diff --git a/pypy/module/_continuation/interp_continuation.py b/pypy/module/_continuation/interp_continuation.py
--- a/pypy/module/_continuation/interp_continuation.py
+++ b/pypy/module/_continuation/interp_continuation.py
@@ -5,6 +5,7 @@
 from pypy.interpreter.baseobjspace import Wrappable
 from pypy.interpreter.typedef import TypeDef
 from pypy.interpreter.gateway import interp2app
+from pypy.interpreter.pycode import PyCode
 
 
 class W_Continulet(Wrappable):
@@ -20,16 +21,17 @@
     def check_sthread(self):
         ec = self.space.getexecutioncontext()
         if ec.stacklet_thread is not self.sthread:
-            start_state.clear()
+            global_state.clear()
             raise geterror(self.space, "inter-thread support is missing")
         return ec
 
     def descr_init(self, w_callable, __args__):
         if self.sthread is not None:
             raise geterror(self.space, "continulet already __init__ialized")
-        start_state.origin = self
-        start_state.w_callable = w_callable
-        start_state.args = __args__
+        global_state.origin = self
+        global_state.w_callable = w_callable
+        global_state.args = __args__
+        self.bottomframe = make_fresh_frame(self.space)
         self.sthread = build_sthread(self.space)
         try:
             self.h = self.sthread.new(new_stacklet_callback)
@@ -37,49 +39,53 @@
                 raise MemoryError
         except MemoryError:
             self.sthread = None
-            start_state.clear()
+            global_state.clear()
             raise getmemoryerror(self.space)
 
     def switch(self, w_to):
+        sthread = self.sthread
+        if sthread is not None and sthread.is_empty_handle(self.h):
+            global_state.clear()
+            raise geterror(self.space, "continulet already finished")
         to = self.space.interp_w(W_Continulet, w_to, can_be_None=True)
+        if to is not None and to.sthread is None:
+            to = None
+        if sthread is None:      # if self is non-initialized:
+            if to is not None:   #     if we are given a 'to'
+                self = to        #         then just use it and ignore 'self'
+                sthread = self.sthread
+                to = None
+            else:
+                return get_result()  # else: no-op
         if to is not None:
-            if to.sthread is None:
-                start_state.clear()
-                raise geterror(self.space, "continulet not initialized yet")
+            if to.sthread is not sthread:
+                global_state.clear()
+                raise geterror(self.space, "cross-thread double switch")
             if self is to:    # double-switch to myself: no-op
                 return get_result()
-        if self.sthread is None:
-            start_state.clear()
-            raise geterror(self.space, "continulet not initialized yet")
+            if sthread.is_empty_handle(to.h):
+                global_state.clear()
+                raise geterror(self.space, "continulet already finished")
         ec = self.check_sthread()
-        saved_topframeref = ec.topframeref
         #
-        start_state.origin = self
+        global_state.origin = self
         if to is None:
             # simple switch: going to self.h
-            start_state.destination = self
+            global_state.destination = self
         else:
             # double switch: the final destination is to.h
-            start_state.destination = to
-        #
-        h = start_state.destination.h
-        sthread = self.sthread
-        if sthread.is_empty_handle(h):
-            start_state.clear()
-            raise geterror(self.space, "continulet already finished")
+            global_state.destination = to
         #
         try:
-            do_switch(sthread, h)
+            do_switch(sthread, global_state.destination.h)
         except MemoryError:
-            start_state.clear()
+            global_state.clear()
             raise getmemoryerror(self.space)
         #
-        ec = sthread.ec
-        ec.topframeref = saved_topframeref
         return get_result()
 
     def descr_switch(self, w_value=None, w_to=None):
-        start_state.w_value = w_value
+        global_state.w_value = w_value
         return self.switch(w_to)
 
     def descr_throw(self, w_type, w_val=None, w_tb=None, w_to=None):
@@ -94,8 +100,8 @@
         #
         operr = OperationError(w_type, w_val, tb)
         operr.normalize_exception(space)
-        start_state.w_value = None
-        start_state.propagate_exception = operr
+        global_state.w_value = None
+        global_state.propagate_exception = operr
         return self.switch(w_to)
 
     def descr_is_pending(self):
@@ -123,13 +129,21 @@
 
 # ____________________________________________________________
 
+# Continulet objects maintain a dummy frame object in order to ensure
+# that the 'f_back' chain is consistent.  We hide this dummy frame
+# object by giving it a dummy code object with hidden_applevel=True.
 
 class State:
     def __init__(self, space):
+        from pypy.interpreter.astcompiler.consts import CO_OPTIMIZED
         self.space = space 
         w_module = space.getbuiltinmodule('_continuation')
         self.w_error = space.getattr(w_module, space.wrap('error'))
         self.w_memoryerror = OperationError(space.w_MemoryError, space.w_None)
+        self.dummy_pycode = PyCode(space, 0, 0, 0, CO_OPTIMIZED,
+                                   '', [], [], [], '',
+                                   '<bottom of continulet>', 0, '', [], [],
+                                   hidden_applevel=True)
 
 def geterror(space, message):
     cs = space.fromcache(State)
@@ -139,6 +153,10 @@
     cs = space.fromcache(State)
     return cs.w_memoryerror
 
+def make_fresh_frame(space):
+    cs = space.fromcache(State)
+    return space.FrameClass(space, cs.dummy_pycode, None, None)
+
 # ____________________________________________________________
 
 
@@ -154,7 +172,7 @@
 # ____________________________________________________________
 
 
-class StartState:   # xxx a single global to pass around the function to start
+class GlobalState:
     def clear(self):
         self.origin = None
         self.destination = None
@@ -162,15 +180,15 @@
         self.args = None
         self.w_value = None
         self.propagate_exception = None
-start_state = StartState()
-start_state.clear()
+global_state = GlobalState()
+global_state.clear()
 
 
 def new_stacklet_callback(h, arg):
-    self       = start_state.origin
-    w_callable = start_state.w_callable
-    args       = start_state.args
-    start_state.clear()
+    self       = global_state.origin
+    w_callable = global_state.w_callable
+    args       = global_state.args
+    global_state.clear()
     try:
         do_switch(self.sthread, h)
     except MemoryError:
@@ -178,41 +196,46 @@
     #
     space = self.space
     try:
-        ec = self.sthread.ec
-        ec.topframeref = jit.vref_None
-
-        if start_state.propagate_exception is not None:
-            raise start_state.propagate_exception   # just propagate it further
-        if start_state.w_value is not space.w_None:
+        assert self.sthread.ec.topframeref() is None
+        self.sthread.ec.topframeref = jit.non_virtual_ref(self.bottomframe)
+        if global_state.propagate_exception is not None:
+            raise global_state.propagate_exception  # just propagate it further
+        if global_state.w_value is not space.w_None:
             raise OperationError(space.w_TypeError, space.wrap(
                 "can't send non-None value to a just-started continulet"))
 
         args = args.prepend(self.space.wrap(self))
         w_result = space.call_args(w_callable, args)
     except Exception, e:
-        start_state.propagate_exception = e
+        global_state.propagate_exception = e
     else:
-        start_state.w_value = w_result
-    start_state.origin = self
-    start_state.destination = self
+        global_state.w_value = w_result
+    self.sthread.ec.topframeref = jit.vref_None
+    global_state.origin = self
+    global_state.destination = self
     return self.h
 
 
 def do_switch(sthread, h):
     h = sthread.switch(h)
-    origin = start_state.origin
-    self = start_state.destination
-    start_state.origin = None
-    start_state.destination = None
+    origin = global_state.origin
+    self = global_state.destination
+    global_state.origin = None
+    global_state.destination = None
     self.h, origin.h = origin.h, h
+    #
+    current = sthread.ec.topframeref
+    sthread.ec.topframeref = self.bottomframe.f_backref
+    self.bottomframe.f_backref = origin.bottomframe.f_backref
+    origin.bottomframe.f_backref = current
 
 def get_result():
-    if start_state.propagate_exception:
-        e = start_state.propagate_exception
-        start_state.propagate_exception = None
+    if global_state.propagate_exception:
+        e = global_state.propagate_exception
+        global_state.propagate_exception = None
         raise e
-    w_value = start_state.w_value
-    start_state.w_value = None
+    w_value = global_state.w_value
+    global_state.w_value = None
     return w_value
 
 def build_sthread(space):
@@ -232,7 +255,7 @@
         cont = space.interp_w(W_Continulet, w_cont)
         if cont.sthread is not sthread:
             if cont.sthread is None:
-                raise geterror(space, "got a non-initialized continulet")
+                continue   # ignore non-initialized continulets
             else:
                 raise geterror(space, "inter-thread support is missing")
         elif sthread.is_empty_handle(cont.h):
@@ -240,6 +263,9 @@
         contlist.append(cont)
     #
     if len(contlist) > 1:
-        other = contlist[-1].h
+        otherh = contlist[-1].h
+        otherb = contlist[-1].bottomframe.f_backref
         for cont in contlist:
-            other, cont.h = cont.h, other
+            otherh, cont.h = cont.h, otherh
+            b = cont.bottomframe
+            otherb, b.f_backref = b.f_backref, otherb
diff --git a/pypy/module/_continuation/test/support.py b/pypy/module/_continuation/test/support.py
--- a/pypy/module/_continuation/test/support.py
+++ b/pypy/module/_continuation/test/support.py
@@ -9,4 +9,4 @@
             import pypy.rlib.rstacklet
         except CompilationError, e:
             py.test.skip("cannot import rstacklet: %s" % e)
-        cls.space = gettestobjspace(usemodules=['_continuation'])
+        cls.space = gettestobjspace(usemodules=['_continuation'], continuation=True)
diff --git a/pypy/module/_continuation/test/test_stacklet.py b/pypy/module/_continuation/test/test_stacklet.py
--- a/pypy/module/_continuation/test/test_stacklet.py
+++ b/pypy/module/_continuation/test/test_stacklet.py
@@ -135,12 +135,6 @@
         e = raises(error, c.switch)
         assert str(e.value) == "continulet already finished"
 
-    def test_not_initialized_yet(self):
-        from _continuation import continulet, error
-        c = continulet.__new__(continulet)
-        e = raises(error, c.switch)
-        assert str(e.value) == "continulet not initialized yet"
-
     def test_go_depth2(self):
         from _continuation import continulet
         #
@@ -254,6 +248,15 @@
         res = c_upper.switch('D')
         assert res == 'E'
 
+    def test_switch_not_initialized(self):
+        from _continuation import continulet
+        c0 = continulet.__new__(continulet)
+        res = c0.switch()
+        assert res is None
+        res = c0.switch(123)
+        assert res == 123
+        raises(ValueError, c0.throw, ValueError)
+
     def test_exception_with_switch_depth2(self):
         from _continuation import continulet
         #
@@ -312,7 +315,7 @@
         res = f()
         assert res == 2002
 
-    def test_f_back_is_None_for_now(self):
+    def test_f_back(self):
         import sys
         from _continuation import continulet
         #
@@ -321,6 +324,7 @@
             c.switch(sys._getframe(0).f_back)
             c.switch(sys._getframe(1))
             c.switch(sys._getframe(1).f_back)
+            assert sys._getframe(2) is f3.f_back
             c.switch(sys._getframe(2))
         def f(c):
             g(c)
@@ -331,10 +335,21 @@
         f2 = c.switch()
         assert f2.f_code.co_name == 'f'
         f3 = c.switch()
-        assert f3.f_code.co_name == 'f'
-        f4 = c.switch()
-        assert f4 is None
-        raises(ValueError, c.switch)    # "call stack is not deep enough"
+        assert f3 is f2
+        assert f1.f_back is f3
+        def main():
+            f4 = c.switch()
+            assert f4.f_code.co_name == 'main', repr(f4.f_code.co_name)
+            assert f3.f_back is f1    # not running, so a loop
+        def main2():
+            f5 = c.switch()
+            assert f5.f_code.co_name == 'main2', repr(f5.f_code.co_name)
+            assert f3.f_back is f1    # not running, so a loop
+        main()
+        main2()
+        res = c.switch()
+        assert res is None
+        assert f3.f_back is None
 
     def test_traceback_is_complete(self):
         import sys
@@ -487,16 +502,31 @@
         assert res == 'z'
         raises(TypeError, c1.switch, to=c2)  # "can't send non-None value"
 
-    def test_switch2_not_initialized_yet(self):
-        from _continuation import continulet, error
+    def test_switch2_not_initialized(self):
+        from _continuation import continulet
+        c0 = continulet.__new__(continulet)
+        c0bis = continulet.__new__(continulet)
+        res = c0.switch(123, to=c0)
+        assert res == 123
+        res = c0.switch(123, to=c0bis)
+        assert res == 123
+        raises(ValueError, c0.throw, ValueError, to=c0)
+        raises(ValueError, c0.throw, ValueError, to=c0bis)
         #
         def f1(c1):
-            not_reachable
-        #
+            c1.switch('a')
+            raises(ValueError, c1.switch, 'b')
+            raises(KeyError, c1.switch, 'c')
+            return 'd'
         c1 = continulet(f1)
-        c2 = continulet.__new__(continulet)
-        e = raises(error, c1.switch, to=c2)
-        assert str(e.value) == "continulet not initialized yet"
+        res = c0.switch(to=c1)
+        assert res == 'a'
+        res = c1.switch(to=c0)
+        assert res == 'b'
+        res = c1.throw(ValueError, to=c0)
+        assert res == 'c'
+        res = c0.throw(KeyError, to=c1)
+        assert res == 'd'
 
     def test_switch2_already_finished(self):
         from _continuation import continulet, error
@@ -609,6 +639,7 @@
         assert res == "ok"
 
     def test_permute(self):
+        import sys
         from _continuation import continulet, permute
         #
         def f1(c1):
@@ -617,14 +648,34 @@
             return "done"
         #
         def f2(c2):
+            assert sys._getframe(1).f_code.co_name == 'main'
             permute(c1, c2)
+            assert sys._getframe(1).f_code.co_name == 'f1'
             return "ok"
         #
         c1 = continulet(f1)
         c2 = continulet(f2)
+        def main():
+            c1.switch()
+            res = c2.switch()
+            assert res == "done"
+        main()
+
+    def test_permute_noninitialized(self):
+        from _continuation import continulet, permute
+        permute(continulet.__new__(continulet))    # ignored
+        permute(continulet.__new__(continulet),    # ignored
+                continulet.__new__(continulet))
+
+    def test_bug_finish_with_already_finished_stacklet(self):
+        from _continuation import continulet, error
+        # make an already-finished continulet
+        c1 = continulet(lambda x: x)
         c1.switch()
-        res = c2.switch()
-        assert res == "done"
+        # make another continulet
+        c2 = continulet(lambda x: x)
+        # this switch is forbidden, because it causes a crash when c2 finishes
+        raises(error, c1.switch, to=c2)
 
     def test_various_depths(self):
         skip("may fail on top of CPython")
diff --git a/pypy/module/_ssl/interp_ssl.py b/pypy/module/_ssl/interp_ssl.py
--- a/pypy/module/_ssl/interp_ssl.py
+++ b/pypy/module/_ssl/interp_ssl.py
@@ -52,7 +52,8 @@
 constants["CERT_OPTIONAL"] = PY_SSL_CERT_OPTIONAL
 constants["CERT_REQUIRED"] = PY_SSL_CERT_REQUIRED
 
-constants["PROTOCOL_SSLv2"]  = PY_SSL_VERSION_SSL2
+if not OPENSSL_NO_SSL2:
+    constants["PROTOCOL_SSLv2"]  = PY_SSL_VERSION_SSL2
 constants["PROTOCOL_SSLv3"]  = PY_SSL_VERSION_SSL3
 constants["PROTOCOL_SSLv23"] = PY_SSL_VERSION_SSL23
 constants["PROTOCOL_TLSv1"]  = PY_SSL_VERSION_TLS1
@@ -673,7 +674,7 @@
         method = libssl_TLSv1_method()
     elif protocol == PY_SSL_VERSION_SSL3:
         method = libssl_SSLv3_method()
-    elif protocol == PY_SSL_VERSION_SSL2:
+    elif protocol == PY_SSL_VERSION_SSL2 and not OPENSSL_NO_SSL2:
         method = libssl_SSLv2_method()
     elif protocol == PY_SSL_VERSION_SSL23:
         method = libssl_SSLv23_method()
diff --git a/pypy/module/cpyext/funcobject.py b/pypy/module/cpyext/funcobject.py
--- a/pypy/module/cpyext/funcobject.py
+++ b/pypy/module/cpyext/funcobject.py
@@ -4,9 +4,21 @@
     cpython_api, bootstrap_function, cpython_struct, build_type_checkers)
 from pypy.module.cpyext.pyobject import (
     PyObject, make_ref, from_ref, Py_DecRef, make_typedescr, borrow_from)
+from pypy.rlib.unroll import unrolling_iterable
 from pypy.interpreter.error import OperationError
 from pypy.interpreter.function import Function, Method
 from pypy.interpreter.pycode import PyCode
+from pypy.interpreter import pycode
+
+CODE_FLAGS = dict(
+    CO_OPTIMIZED   = 0x0001,
+    CO_NEWLOCALS   = 0x0002,
+    CO_VARARGS     = 0x0004,
+    CO_VARKEYWORDS = 0x0008,
+    CO_NESTED      = 0x0010,
+    CO_GENERATOR   = 0x0020,
+)
+ALL_CODE_FLAGS = unrolling_iterable(CODE_FLAGS.items())
 
 PyFunctionObjectStruct = lltype.ForwardReference()
 PyFunctionObject = lltype.Ptr(PyFunctionObjectStruct)
@@ -16,7 +28,12 @@
 
 PyCodeObjectStruct = lltype.ForwardReference()
 PyCodeObject = lltype.Ptr(PyCodeObjectStruct)
-cpython_struct("PyCodeObject", PyObjectFields, PyCodeObjectStruct)
+PyCodeObjectFields = PyObjectFields + \
+    (("co_name", PyObject),
+     ("co_flags", rffi.INT),
+     ("co_argcount", rffi.INT),
+    )
+cpython_struct("PyCodeObject", PyCodeObjectFields, PyCodeObjectStruct)
 
 @bootstrap_function
 def init_functionobject(space):
@@ -24,6 +41,10 @@
                    basestruct=PyFunctionObject.TO,
                    attach=function_attach,
                    dealloc=function_dealloc)
+    make_typedescr(PyCode.typedef,
+                   basestruct=PyCodeObject.TO,
+                   attach=code_attach,
+                   dealloc=code_dealloc)
 
 PyFunction_Check, PyFunction_CheckExact = build_type_checkers("Function", Function)
 PyMethod_Check, PyMethod_CheckExact = build_type_checkers("Method", Method)
@@ -40,6 +61,31 @@
     from pypy.module.cpyext.object import PyObject_dealloc
     PyObject_dealloc(space, py_obj)
 
+def code_attach(space, py_obj, w_obj):
+    py_code = rffi.cast(PyCodeObject, py_obj)
+    assert isinstance(w_obj, PyCode)
+    py_code.c_co_name = make_ref(space, space.wrap(w_obj.co_name))
+    co_flags = 0
+    for name, value in ALL_CODE_FLAGS:
+        if w_obj.co_flags & getattr(pycode, name):
+            co_flags |= value
+    rffi.setintfield(py_code, 'c_co_flags', co_flags)
+    rffi.setintfield(py_code, 'c_co_argcount', w_obj.co_argcount)
+
+ at cpython_api([PyObject], lltype.Void, external=False)
+def code_dealloc(space, py_obj):
+    py_code = rffi.cast(PyCodeObject, py_obj)
+    Py_DecRef(space, py_code.c_co_name)
+    from pypy.module.cpyext.object import PyObject_dealloc
+    PyObject_dealloc(space, py_obj)
+
+ at cpython_api([PyObject], PyObject)
+def PyFunction_GetCode(space, w_func):
+    """Return the code object associated with the function object op."""
+    func = space.interp_w(Function, w_func)
+    w_code = space.wrap(func.code)
+    return borrow_from(w_func, w_code)
+
 @cpython_api([PyObject, PyObject, PyObject], PyObject)
 def PyMethod_New(space, w_func, w_self, w_cls):
     """Return a new method object, with func being any callable object; this is the
diff --git a/pypy/module/cpyext/include/code.h b/pypy/module/cpyext/include/code.h
--- a/pypy/module/cpyext/include/code.h
+++ b/pypy/module/cpyext/include/code.h
@@ -4,7 +4,21 @@
 extern "C" {
 #endif
 
-typedef PyObject PyCodeObject;
+typedef struct {
+    PyObject_HEAD
+    PyObject *co_name;
+    int co_argcount;
+    int co_flags;
+} PyCodeObject;
+
+/* Masks for co_flags above */
+/* These values are also in funcobject.py */
+#define CO_OPTIMIZED	0x0001
+#define CO_NEWLOCALS	0x0002
+#define CO_VARARGS	0x0004
+#define CO_VARKEYWORDS	0x0008
+#define CO_NESTED       0x0010
+#define CO_GENERATOR    0x0020
 
 #ifdef __cplusplus
 }
diff --git a/pypy/module/cpyext/include/funcobject.h b/pypy/module/cpyext/include/funcobject.h
--- a/pypy/module/cpyext/include/funcobject.h
+++ b/pypy/module/cpyext/include/funcobject.h
@@ -12,6 +12,8 @@
     PyObject *func_name;	/* The __name__ attribute, a string object */
 } PyFunctionObject;
 
+#define PyFunction_GET_CODE(obj) PyFunction_GetCode((PyObject*)(obj))
+
 #define PyMethod_GET_FUNCTION(obj) PyMethod_Function((PyObject*)(obj))
 #define PyMethod_GET_SELF(obj) PyMethod_Self((PyObject*)(obj))
 #define PyMethod_GET_CLASS(obj) PyMethod_Class((PyObject*)(obj))
diff --git a/pypy/module/cpyext/include/object.h b/pypy/module/cpyext/include/object.h
--- a/pypy/module/cpyext/include/object.h
+++ b/pypy/module/cpyext/include/object.h
@@ -501,6 +501,9 @@
 #define PyObject_TypeCheck(ob, tp) \
     ((ob)->ob_type == (tp) || PyType_IsSubtype((ob)->ob_type, (tp)))
 
+#define Py_TRASHCAN_SAFE_BEGIN(pyObj)
+#define Py_TRASHCAN_SAFE_END(pyObj)
+
 /* Copied from CPython ----------------------------- */
 int PyObject_AsReadBuffer(PyObject *, const void **, Py_ssize_t *);
 int PyObject_AsWriteBuffer(PyObject *, void **, Py_ssize_t *);
diff --git a/pypy/module/cpyext/include/pythonrun.h b/pypy/module/cpyext/include/pythonrun.h
--- a/pypy/module/cpyext/include/pythonrun.h
+++ b/pypy/module/cpyext/include/pythonrun.h
@@ -12,6 +12,7 @@
 #define Py_Py3kWarningFlag 0
 
 #define Py_FrozenFlag 0
+#define Py_VerboseFlag 0
 
 typedef struct {
     int cf_flags;  /* bitmask of CO_xxx flags relevant to future */
diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py
--- a/pypy/module/cpyext/stubs.py
+++ b/pypy/module/cpyext/stubs.py
@@ -920,12 +920,6 @@
     raise NotImplementedError
 
 @cpython_api([PyObject], PyObject)
-def PyFunction_GetCode(space, op):
-    """Return the code object associated with the function object op."""
-    borrow_from()
-    raise NotImplementedError
-
- at cpython_api([PyObject], PyObject)
 def PyFunction_GetGlobals(space, op):
     """Return the globals dictionary associated with the function object op."""
     borrow_from()
diff --git a/pypy/module/cpyext/test/foo.c b/pypy/module/cpyext/test/foo.c
--- a/pypy/module/cpyext/test/foo.c
+++ b/pypy/module/cpyext/test/foo.c
@@ -215,36 +215,36 @@
 typedef struct {
     PyUnicodeObject HEAD;
     int val;
-} FuuObject;
+} UnicodeSubclassObject;
 
 
-static int Fuu_init(FuuObject *self, PyObject *args, PyObject *kwargs) {
+static int UnicodeSubclass_init(UnicodeSubclassObject *self, PyObject *args, PyObject *kwargs) {
     self->val = 42;
     return 0;
 }
 
 static PyObject *
-Fuu_escape(PyTypeObject* type, PyObject *args)
+UnicodeSubclass_escape(PyTypeObject* type, PyObject *args)
 {
     Py_RETURN_TRUE;
 }
 
 static PyObject *
-Fuu_get_val(FuuObject *self) {
+UnicodeSubclass_get_val(UnicodeSubclassObject *self) {
     return PyInt_FromLong(self->val);
 }
 
-static PyMethodDef Fuu_methods[] = {
-    {"escape", (PyCFunction) Fuu_escape, METH_VARARGS, NULL},
-    {"get_val", (PyCFunction) Fuu_get_val, METH_NOARGS, NULL},
+static PyMethodDef UnicodeSubclass_methods[] = {
+    {"escape", (PyCFunction) UnicodeSubclass_escape, METH_VARARGS, NULL},
+    {"get_val", (PyCFunction) UnicodeSubclass_get_val, METH_NOARGS, NULL},
     {NULL}  /* Sentinel */
 };
 
-PyTypeObject FuuType = {
+PyTypeObject UnicodeSubtype = {
     PyObject_HEAD_INIT(NULL)
     0,
     "foo.fuu",
-    sizeof(FuuObject),
+    sizeof(UnicodeSubclassObject),
     0,
     0,          /*tp_dealloc*/
     0,          /*tp_print*/
@@ -277,7 +277,7 @@
 
     /* Attribute descriptor and subclassing stuff */
 
-    Fuu_methods,/*tp_methods*/
+    UnicodeSubclass_methods,/*tp_methods*/
     0,          /*tp_members*/
     0,          /*tp_getset*/
     0,          /*tp_base*/
@@ -287,7 +287,7 @@
     0,          /*tp_descr_set*/
     0,          /*tp_dictoffset*/
 
-    (initproc) Fuu_init, /*tp_init*/
+    (initproc) UnicodeSubclass_init, /*tp_init*/
     0,          /*tp_alloc  will be set to PyType_GenericAlloc in module init*/
     0,          /*tp_new*/
     0,          /*tp_free  Low-level free-memory routine */
@@ -299,11 +299,11 @@
     0           /*tp_weaklist*/
 };
 
-PyTypeObject Fuu2Type = {
+PyTypeObject UnicodeSubtype2 = {
     PyObject_HEAD_INIT(NULL)
     0,
     "foo.fuu2",
-    sizeof(FuuObject),
+    sizeof(UnicodeSubclassObject),
     0,
     0,          /*tp_dealloc*/
     0,          /*tp_print*/
@@ -628,15 +628,15 @@
 
     footype.tp_new = PyType_GenericNew;
 
-    FuuType.tp_base = &PyUnicode_Type;
-    Fuu2Type.tp_base = &FuuType;
+    UnicodeSubtype.tp_base = &PyUnicode_Type;
+    UnicodeSubtype2.tp_base = &UnicodeSubtype;
     MetaType.tp_base = &PyType_Type;
 
     if (PyType_Ready(&footype) < 0)
         return;
-    if (PyType_Ready(&FuuType) < 0)
+    if (PyType_Ready(&UnicodeSubtype) < 0)
         return;
-    if (PyType_Ready(&Fuu2Type) < 0)
+    if (PyType_Ready(&UnicodeSubtype2) < 0)
         return;
     if (PyType_Ready(&MetaType) < 0)
         return;
@@ -655,9 +655,9 @@
         return;
     if (PyDict_SetItemString(d, "fooType", (PyObject *)&footype) < 0)
         return;
-    if (PyDict_SetItemString(d, "FuuType", (PyObject *) &FuuType) < 0)
+    if (PyDict_SetItemString(d, "UnicodeSubtype", (PyObject *) &UnicodeSubtype) < 0)
         return;
-    if(PyDict_SetItemString(d, "Fuu2Type", (PyObject *) &Fuu2Type) < 0)
+    if (PyDict_SetItemString(d, "UnicodeSubtype2", (PyObject *) &UnicodeSubtype2) < 0)
         return;
     if (PyDict_SetItemString(d, "MetaType", (PyObject *) &MetaType) < 0)
         return;
diff --git a/pypy/module/cpyext/test/test_funcobject.py b/pypy/module/cpyext/test/test_funcobject.py
--- a/pypy/module/cpyext/test/test_funcobject.py
+++ b/pypy/module/cpyext/test/test_funcobject.py
@@ -2,8 +2,12 @@
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
 from pypy.module.cpyext.test.test_api import BaseApiTest
 from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref
-from pypy.module.cpyext.funcobject import PyFunctionObject
+from pypy.module.cpyext.funcobject import (
+    PyFunctionObject, PyCodeObject, CODE_FLAGS)
 from pypy.interpreter.function import Function, Method
+from pypy.interpreter.pycode import PyCode
+
+globals().update(CODE_FLAGS)
 
 class TestFunctionObject(BaseApiTest):
     def test_function(self, space, api):
@@ -36,6 +40,38 @@
         w_method2 = api.PyMethod_New(w_function, w_self, w_class)
         assert space.eq_w(w_method, w_method2)
 
+    def test_getcode(self, space, api):
+        w_function = space.appexec([], """():
+            def func(x, y, z): return x
+            return func
+        """)
+        w_code = api.PyFunction_GetCode(w_function)
+        assert w_code.co_name == "func"
+
+        ref = make_ref(space, w_code)
+        assert (from_ref(space, rffi.cast(PyObject, ref.c_ob_type)) is
+                space.gettypeobject(PyCode.typedef))
+        assert "func" == space.unwrap(
+           from_ref(space, rffi.cast(PyCodeObject, ref).c_co_name))
+        assert 3 == rffi.cast(PyCodeObject, ref).c_co_argcount
+        api.Py_DecRef(ref)
+
+    def test_co_flags(self, space, api):
+        def get_flags(signature, body="pass"):
+            w_code = space.appexec([], """():
+                def func(%s): %s
+                return func.__code__
+            """ % (signature, body))
+            ref = make_ref(space, w_code)
+            co_flags = rffi.cast(PyCodeObject, ref).c_co_flags
+            api.Py_DecRef(ref)
+            return co_flags
+        assert get_flags("x") == CO_NESTED | CO_OPTIMIZED | CO_NEWLOCALS
+        assert get_flags("x", "exec x") == CO_NESTED | CO_NEWLOCALS
+        assert get_flags("x, *args") & CO_VARARGS
+        assert get_flags("x, **kw") & CO_VARKEYWORDS
+        assert get_flags("x", "yield x") & CO_GENERATOR
+
     def test_newcode(self, space, api):
         filename = rffi.str2charp('filename')
         funcname = rffi.str2charp('funcname')
diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -119,16 +119,16 @@
         module = self.import_module(name='foo')
         obj = module.new()
         # call __new__
-        newobj = module.FuuType(u"xyz")
+        newobj = module.UnicodeSubtype(u"xyz")
         assert newobj == u"xyz"
-        assert isinstance(newobj, module.FuuType)
+        assert isinstance(newobj, module.UnicodeSubtype)
 
         assert isinstance(module.fooType(), module.fooType)
         class bar(module.fooType):
             pass
         assert isinstance(bar(), bar)
 
-        fuu = module.FuuType
+        fuu = module.UnicodeSubtype
         class fuu2(fuu):
             def baz(self):
                 return self
@@ -137,20 +137,20 @@
 
     def test_init(self):
         module = self.import_module(name="foo")
-        newobj = module.FuuType()
+        newobj = module.UnicodeSubtype()
         assert newobj.get_val() == 42
 
         # this subtype should inherit tp_init
-        newobj = module.Fuu2Type()
+        newobj = module.UnicodeSubtype2()
         assert newobj.get_val() == 42
 
         # this subclass redefines __init__
-        class Fuu2(module.FuuType):
+        class UnicodeSubclass2(module.UnicodeSubtype):
             def __init__(self):
                 self.foobar = 32
-                super(Fuu2, self).__init__()
+                super(UnicodeSubclass2, self).__init__()
         
-        newobj = Fuu2()
+        newobj = UnicodeSubclass2()
         assert newobj.get_val() == 42
         assert newobj.foobar == 32
 
diff --git a/pypy/module/micronumpy/interp_dtype.py b/pypy/module/micronumpy/interp_dtype.py
--- a/pypy/module/micronumpy/interp_dtype.py
+++ b/pypy/module/micronumpy/interp_dtype.py
@@ -317,17 +317,6 @@
 class W_Int8Dtype(IntegerArithmeticDtype, W_Int8Dtype):
     pass
 
-W_UInt8Dtype = create_low_level_dtype(
-    num = 1, kind = SIGNEDLTR, name = "uint8",
-    aliases = ["uint8"],
-    applevel_types = [],
-    T = rffi.UCHAR,
-    valtype = rffi.UCHAR._type,
-    expected_size = 1,
-)
-class W_UInt8Dtype(IntegerArithmeticDtype, W_UInt8Dtype):
-    pass
-
 W_Int16Dtype = create_low_level_dtype(
     num = 3, kind = SIGNEDLTR, name = "int16",
     aliases = ["int16"],
@@ -379,7 +368,6 @@
 ALL_DTYPES = [
     W_BoolDtype,
     W_Int8Dtype, W_Int16Dtype, W_Int32Dtype, W_Int64Dtype,
-    W_UInt8Dtype,
     W_Float64Dtype
 ]
 
diff --git a/pypy/module/micronumpy/test/test_dtypes.py b/pypy/module/micronumpy/test/test_dtypes.py
--- a/pypy/module/micronumpy/test/test_dtypes.py
+++ b/pypy/module/micronumpy/test/test_dtypes.py
@@ -12,7 +12,6 @@
         assert dtype(d) is d
         assert dtype(None) is dtype(float)
         raises(TypeError, dtype, 1042)
-        assert dtype('uint8').num == 1
 
     def test_dtype_with_types(self):
         from numpy import dtype
@@ -91,15 +90,6 @@
         for i in range(5):
             assert b[i] == i * 2
 
-    def test_add_uint8(self):
-        from numpy import array, dtype
-
-        a = array(range(5), dtype="uint8")
-        b = a + a
-        assert b.dtype is dtype("uint8")
-        for i in range(5):
-            assert b[i] == i * 2
-
     def test_add_int16(self):
         from numpy import array, dtype
 
@@ -119,15 +109,3 @@
 
         # You can't subclass dtype
         raises(TypeError, type, "Foo", (dtype,), {})
-
-    def test_int_ranges(self):
-        from numpy import array
-        for dtype, minval, maxval in [("int8", -128, 127),
-                                      ("uint8", 0, 255),
-                                      ("int16", -32768, 32767)]:
-            a = array([minval, maxval, minval-1, maxval+1], dtype)
-            assert a[0] == minval
-            assert a[1] == maxval
-            assert a[2] == maxval
-            assert a[3] == minval
-            
diff --git a/pypy/module/pyexpat/interp_pyexpat.py b/pypy/module/pyexpat/interp_pyexpat.py
--- a/pypy/module/pyexpat/interp_pyexpat.py
+++ b/pypy/module/pyexpat/interp_pyexpat.py
@@ -12,6 +12,7 @@
 from pypy.translator.platform import platform
 
 import sys
+import weakref
 import py
 
 if sys.platform == "win32":
@@ -180,7 +181,7 @@
 class CallbackData(Wrappable):
     def __init__(self, space, parser):
         self.space = space
-        self.parser = parser
+        self.parser = weakref.ref(parser)
 
 SETTERS = {}
 for index, (name, params) in enumerate(HANDLERS.items()):
@@ -257,7 +258,7 @@
         id = rffi.cast(lltype.Signed, %(ll_id)s)
         userdata = global_storage.get_object(id)
         space = userdata.space
-        parser = userdata.parser
+        parser = userdata.parser()
 
         handler = parser.handlers[%(index)s]
         if not handler:
@@ -292,7 +293,7 @@
     id = rffi.cast(lltype.Signed, ll_userdata)
     userdata = global_storage.get_object(id)
     space = userdata.space
-    parser = userdata.parser
+    parser = userdata.parser()
 
     name = rffi.charp2str(name)
 
@@ -409,8 +410,7 @@
         if XML_ParserFree: # careful with CPython interpreter shutdown
             XML_ParserFree(self.itself)
         if global_storage:
-            global_storage.free_nonmoving_id(
-                rffi.cast(lltype.Signed, self.itself))
+            global_storage.free_nonmoving_id(self.id)
 
     @unwrap_spec(flag=int)
     def SetParamEntityParsing(self, space, flag):
diff --git a/pypy/module/pypyjit/policy.py b/pypy/module/pypyjit/policy.py
--- a/pypy/module/pypyjit/policy.py
+++ b/pypy/module/pypyjit/policy.py
@@ -16,7 +16,7 @@
         if modname in ['pypyjit', 'signal', 'micronumpy', 'math', 'exceptions',
                        'imp', 'sys', 'array', '_ffi', 'itertools', 'operator',
                        'posix', '_socket', '_sre', '_lsprof', '_weakref',
-                       '__pypy__', 'cStringIO', '_collections']:
+                       '__pypy__', 'cStringIO', '_collections', 'struct']:
             return True
         return False
 
diff --git a/pypy/module/pypyjit/test_pypy_c/test_call.py b/pypy/module/pypyjit/test_pypy_c/test_call.py
--- a/pypy/module/pypyjit/test_pypy_c/test_call.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_call.py
@@ -337,7 +337,9 @@
         assert loop.match_by_id('append', """
             i13 = getfield_gc(p8, descr=<SignedFieldDescr list.length .*>)
             i15 = int_add(i13, 1)
-            call(ConstClass(_ll_list_resize_ge__listPtr_Signed), p8, i15, descr=<VoidCallDescr>)
+            # Will be killed by the backend
+            i17 = arraylen_gc(p7, descr=<GcPtrArrayDescr>)
+            call(ConstClass(_ll_list_resize_ge), p8, i15, descr=<VoidCallDescr>)
             guard_no_exception(descr=...)
             p17 = getfield_gc(p8, descr=<GcPtrFieldDescr list.items .*>)
             p19 = new_with_vtable(ConstClass(W_IntObject))
diff --git a/pypy/module/pypyjit/test_pypy_c/test_containers.py b/pypy/module/pypyjit/test_pypy_c/test_containers.py
--- a/pypy/module/pypyjit/test_pypy_c/test_containers.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_containers.py
@@ -40,10 +40,10 @@
         log = self.run(fn, [1000])
         assert log.result == 300
         loop, = log.loops_by_filename(self.filepath)
-        # check that the call to ll_dict_lookup is not a call_may_force
+        # check that the call to ll_dict_lookup is not a call_may_force, the
+        # gc_id call is hoisted out of the loop, the id of a value obviously
+        # can't change ;)
         assert loop.match_by_id("getitem", """
-            i25 = call(ConstClass(_ll_1_gc_identityhash__objectPtr), p6, descr=...)
-            ...
             i28 = call(ConstClass(ll_dict_lookup__dicttablePtr_objectPtr_Signed), p18, p6, i25, descr=...)
             ...
             p33 = call(ConstClass(ll_get_value__dicttablePtr_Signed), p18, i28, descr=...)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_misc.py b/pypy/module/pypyjit/test_pypy_c/test_misc.py
--- a/pypy/module/pypyjit/test_pypy_c/test_misc.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_misc.py
@@ -92,7 +92,7 @@
         """)
 
 
-    def test_cached_pure_func_of_equal_fields(self):            
+    def test_cached_pure_func_of_equal_fields(self):
         def main(n):
             class A(object):
                 def __init__(self, val):
@@ -285,3 +285,48 @@
 
         loop, = log.loops_by_id("globalread", is_entry_bridge=True)
         assert len(loop.ops_by_id("globalread")) == 0
+
+    def test_struct_module(self):
+        def main():
+            import struct
+            i = 1
+            while i < 1000:
+                x = struct.unpack("i", struct.pack("i", i))[0] # ID: struct
+                i += x / i
+            return i
+
+        log = self.run(main)
+        assert log.result == main()
+
+        loop, = log.loops_by_id("struct")
+        if sys.maxint == 2 ** 63 - 1:
+            extra = """
+            i8 = int_lt(i4, -2147483648)
+            guard_false(i8, descr=...)
+            """
+        else:
+            extra = ""
+        # This could, of course stand some improvement, to remove all these
+        # arithmatic ops, but we've removed all the core overhead.
+        assert loop.match_by_id("struct", """
+            guard_not_invalidated(descr=...)
+            # struct.pack
+            %(32_bit_only)s
+            i11 = int_and(i4, 255)
+            i13 = int_rshift(i4, 8)
+            i14 = int_and(i13, 255)
+            i16 = int_rshift(i13, 8)
+            i17 = int_and(i16, 255)
+            i19 = int_rshift(i16, 8)
+            i20 = int_and(i19, 255)
+
+            # struct.unpack
+            i22 = int_lshift(i14, 8)
+            i23 = int_or(i11, i22)
+            i25 = int_lshift(i17, 16)
+            i26 = int_or(i23, i25)
+            i28 = int_ge(i20, 128)
+            guard_false(i28, descr=...)
+            i30 = int_lshift(i20, 24)
+            i31 = int_or(i26, i30)
+        """ % {"32_bit_only": extra})
\ No newline at end of file
diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py
--- a/pypy/module/pypyjit/test_pypy_c/test_string.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_string.py
@@ -1,5 +1,6 @@
 from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC
 
+
 class TestString(BaseTestPyPyC):
     def test_lookup_default_encoding(self):
         def main(n):
@@ -107,3 +108,52 @@
             --TICK--
             jump(p0, p1, p2, p3, p4, p5, i58, i7, descr=<Loop4>)
         """)
+
+    def test_str_mod(self):
+        def main(n):
+            s = 0
+            while n > 0:
+                s += len('%d %d' % (n, n))
+                n -= 1
+            return s
+
+        log = self.run(main, [1000])
+        assert log.result == main(1000)
+        loop, = log.loops_by_filename(self.filepath)
+        assert loop.match("""
+            i7 = int_gt(i4, 0)
+            guard_true(i7, descr=...)
+            guard_not_invalidated(descr=...)
+            p9 = call(ConstClass(ll_int2dec__Signed), i4, descr=<GcPtrCallDescr>)
+            guard_no_exception(descr=...)
+            i10 = strlen(p9)
+            i11 = int_is_true(i10)
+            guard_true(i11, descr=...)
+            i13 = strgetitem(p9, 0)
+            i15 = int_eq(i13, 45)
+            guard_false(i15, descr=...)
+            i17 = int_sub(0, i10)
+            i19 = int_gt(i10, 23)
+            guard_false(i19, descr=...)
+            p21 = newstr(23)
+            copystrcontent(p9, p21, 0, 0, i10)
+            i25 = int_add(1, i10)
+            i26 = int_gt(i25, 23)
+            guard_false(i26, descr=...)
+            strsetitem(p21, i10, 32)
+            i29 = int_add(i10, 1)
+            i30 = int_add(i10, i25)
+            i31 = int_gt(i30, 23)
+            guard_false(i31, descr=...)
+            copystrcontent(p9, p21, 0, i25, i10)
+            i33 = int_eq(i30, 23)
+            guard_false(i33, descr=...)
+            p35 = call(ConstClass(ll_shrink_array__rpy_stringPtr_Signed), p21, i30, descr=<GcPtrCallDescr>)
+            guard_no_exception(descr=...)
+            i37 = strlen(p35)
+            i38 = int_add_ovf(i5, i37)
+            guard_no_overflow(descr=...)
+            i40 = int_sub(i4, 1)
+            --TICK--
+            jump(p0, p1, p2, p3, i40, i38, descr=<Loop0>)
+        """)
\ No newline at end of file
diff --git a/pypy/module/struct/formatiterator.py b/pypy/module/struct/formatiterator.py
--- a/pypy/module/struct/formatiterator.py
+++ b/pypy/module/struct/formatiterator.py
@@ -1,9 +1,9 @@
-from pypy.interpreter.error import OperationError
-
+from pypy.rlib import jit
 from pypy.rlib.objectmodel import specialize
 from pypy.rlib.rstruct.error import StructError
+from pypy.rlib.rstruct.formatiterator import FormatIterator
 from pypy.rlib.rstruct.standardfmttable import PACK_ACCEPTS_BROKEN_INPUT
-from pypy.rlib.rstruct.formatiterator import FormatIterator
+from pypy.interpreter.error import OperationError
 
 
 class PackFormatIterator(FormatIterator):
@@ -14,15 +14,20 @@
         self.args_index = 0
         self.result = []      # list of characters
 
+    # This *should* be always unroll safe, the only way to get here is by
+    # unroll the interpret function, which means the fmt is const, and thus
+    # this should be const (in theory ;)
+    @jit.unroll_safe
+    @specialize.arg(1)
     def operate(self, fmtdesc, repetitions):
         if fmtdesc.needcount:
             fmtdesc.pack(self, repetitions)
         else:
             for i in range(repetitions):
                 fmtdesc.pack(self)
-    operate._annspecialcase_ = 'specialize:arg(1)'
     _operate_is_specialized_ = True
 
+    @jit.unroll_safe
     def align(self, mask):
         pad = (-len(self.result)) & mask
         for i in range(pad):
@@ -130,13 +135,15 @@
         self.inputpos = 0
         self.result_w = []     # list of wrapped objects
 
+    # See above comment on operate.
+    @jit.unroll_safe
+    @specialize.arg(1)
     def operate(self, fmtdesc, repetitions):
         if fmtdesc.needcount:
             fmtdesc.unpack(self, repetitions)
         else:
             for i in range(repetitions):
                 fmtdesc.unpack(self)
-    operate._annspecialcase_ = 'specialize:arg(1)'
     _operate_is_specialized_ = True
 
     def align(self, mask):
@@ -154,7 +161,6 @@
         self.inputpos = end
         return s
 
+    @specialize.argtype(1)
     def appendobj(self, value):
         self.result_w.append(self.space.wrap(value))
-    appendobj._annspecialcase_ = 'specialize:argtype(1)'
-
diff --git a/pypy/module/struct/interp_struct.py b/pypy/module/struct/interp_struct.py
--- a/pypy/module/struct/interp_struct.py
+++ b/pypy/module/struct/interp_struct.py
@@ -3,6 +3,7 @@
 from pypy.rlib.rstruct.error import StructError
 from pypy.rlib.rstruct.formatiterator import CalcSizeFormatIterator
 
+
 @unwrap_spec(format=str)
 def calcsize(space, format):
     fmtiter = CalcSizeFormatIterator()
diff --git a/pypy/module/sys/test/test_sysmodule.py b/pypy/module/sys/test/test_sysmodule.py
--- a/pypy/module/sys/test/test_sysmodule.py
+++ b/pypy/module/sys/test/test_sysmodule.py
@@ -6,11 +6,11 @@
 import sys
 
 def test_stdin_exists(space):
-    space.sys.get('stdin') 
+    space.sys.get('stdin')
     space.sys.get('__stdin__')
 
 def test_stdout_exists(space):
-    space.sys.get('stdout') 
+    space.sys.get('stdout')
     space.sys.get('__stdout__')
 
 class AppTestAppSysTests:
@@ -25,7 +25,7 @@
         assert 'sys' in modules, ( "An entry for sys "
                                         "is not in sys.modules.")
         sys2 = sys.modules['sys']
-        assert sys is sys2, "import sys is not sys.modules[sys]." 
+        assert sys is sys2, "import sys is not sys.modules[sys]."
     def test_builtin_in_modules(self):
         import sys
         modules = sys.modules
@@ -89,12 +89,12 @@
         else:
             raise AssertionError, "ZeroDivisionError not caught"
 
-    def test_io(self): 
+    def test_io(self):
         import sys
         assert isinstance(sys.__stdout__, file)
         assert isinstance(sys.__stderr__, file)
         assert isinstance(sys.__stdin__, file)
-    
+
         if self.appdirect and not isinstance(sys.stdin, file):
             return
 
@@ -324,7 +324,7 @@
         import sys
         if self.appdirect:
             skip("not worth running appdirect")
-            
+
         encoding = sys.getdefaultencoding()
         try:
             sys.setdefaultencoding("ascii")
@@ -334,11 +334,11 @@
             sys.setdefaultencoding("latin-1")
             assert sys.getdefaultencoding() == 'latin-1'
             assert unicode('\x80') == u'\u0080'
-            
+
         finally:
             sys.setdefaultencoding(encoding)
 
-            
+
     # testing sys.settrace() is done in test_trace.py
     # testing sys.setprofile() is done in test_profile.py
 
@@ -372,6 +372,21 @@
             assert isinstance(v[3], int)
             assert isinstance(v[4], str)
 
+            assert v[0] == v.major
+            assert v[1] == v.minor
+            assert v[2] == v.build
+            assert v[3] == v.platform
+            assert v[4] == v.service_pack
+
+            assert isinstance(v.service_pack_minor, int)
+            assert isinstance(v.service_pack_major, int)
+            assert isinstance(v.suite_mask, int)
+            assert isinstance(v.product_type, int)
+
+            # This is how platform.py calls it. Make sure tuple still has 5
+            # elements
+            maj, min, buildno, plat, csd = sys.getwindowsversion()
+
     def test_winver(self):
         import sys
         if hasattr(sys, "winver"):
@@ -564,7 +579,7 @@
                 if self.ready: break
                 time.sleep(0.1)
             return sys._current_frames()
-        
+
         frames = f()
         thisframe = frames.pop(thread_id)
         assert thisframe.f_code.co_name == 'f'
diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py
--- a/pypy/module/sys/vm.py
+++ b/pypy/module/sys/vm.py
@@ -1,11 +1,13 @@
 """
 Implementation of interpreter-level 'sys' routines.
 """
+import sys
+
+from pypy.interpreter import gateway
 from pypy.interpreter.error import OperationError
 from pypy.interpreter.gateway import unwrap_spec, NoneNotWrapped
+from pypy.rlib import jit
 from pypy.rlib.runicode import MAXUNICODE
-from pypy.rlib import jit
-import sys
 
 # ____________________________________________________________
 
@@ -50,6 +52,8 @@
     current stack frame.
 
     This function should be used for specialized purposes only."""
+    raise OperationError(space.w_NotImplementedError,
+        space.wrap("XXX sys._current_frames() incompatible with the JIT"))
     w_result = space.newdict()
     ecs = space.threadlocals.getallvalues()
     for thread_ident, ec in ecs.items():
@@ -58,7 +62,7 @@
         space.setitem(w_result,
                       space.wrap(thread_ident),
                       space.wrap(f))
-    return w_result                      
+    return w_result
 
 def setrecursionlimit(space, w_new_limit):
     """setrecursionlimit() sets the maximum number of nested calls that
@@ -124,7 +128,7 @@
     """Set the global debug tracing function.  It will be called on each
 function call.  See the debugger chapter in the library manual."""
     space.getexecutioncontext().settrace(w_func)
-    
+
 def setprofile(space, w_func):
     """Set the profiling function.  It will be called on each function call
 and return.  See the profiler chapter in the library manual."""
@@ -145,14 +149,47 @@
 a debugger from a checkpoint, to recursively debug some other code."""
     return space.getexecutioncontext().call_tracing(w_func, w_args)
 
+
+app = gateway.applevel('''
+"NOT_RPYTHON"
+from _structseq import structseqtype, structseqfield
+
+class windows_version_info:
+    __metaclass__ = structseqtype
+
+    name = "sys.getwindowsversion"
+
+    major = structseqfield(0, "Major version number")
+    minor = structseqfield(1, "Minor version number")
+    build = structseqfield(2, "Build number")
+    platform = structseqfield(3, "Operating system platform")
+    service_pack = structseqfield(4, "Latest Service Pack installed on the system")
+
+    # Because the indices aren't consecutive, they aren't included when
+    # unpacking and other such operations.
+    service_pack_major = structseqfield(10, "Service Pack major version number")
+    service_pack_minor = structseqfield(11, "Service Pack minor version number")
+    suite_mask = structseqfield(12, "Bit mask identifying available product suites")
+    product_type = structseqfield(13, "System product type")
+''')
+
+
 def getwindowsversion(space):
     from pypy.rlib import rwin32
     info = rwin32.GetVersionEx()
-    return space.newtuple([space.wrap(info[0]),
-                           space.wrap(info[1]),
-                           space.wrap(info[2]),
-                           space.wrap(info[3]),
-                           space.wrap(info[4])])
+    w_windows_version_info = app.wget(space, "windows_version_info")
+    raw_version = space.newtuple([
+        space.wrap(info[0]),
+        space.wrap(info[1]),
+        space.wrap(info[2]),
+        space.wrap(info[3]),
+        space.wrap(info[4]),
+        space.wrap(info[5]),
+        space.wrap(info[6]),
+        space.wrap(info[7]),
+        space.wrap(info[8]),
+    ])
+    return space.call_function(w_windows_version_info, raw_version)
 
 @jit.dont_look_inside
 def get_dllhandle(space):
diff --git a/pypy/module/test_lib_pypy/test_greenlet.py b/pypy/module/test_lib_pypy/test_greenlet.py
--- a/pypy/module/test_lib_pypy/test_greenlet.py
+++ b/pypy/module/test_lib_pypy/test_greenlet.py
@@ -3,7 +3,7 @@
 
 class AppTestGreenlet:
     def setup_class(cls):
-        cls.space = gettestobjspace(usemodules=['_continuation'])
+        cls.space = gettestobjspace(usemodules=['_continuation'], continuation=True)
 
     def test_simple(self):
         from greenlet import greenlet
@@ -241,3 +241,20 @@
         g1 = greenlet(f1)
         raises(ValueError, g1.throw, ValueError)
         assert g1.dead
+
+    def test_exc_info_save_restore(self):
+        # sys.exc_info save/restore behaviour is wrong on CPython's greenlet
+        from greenlet import greenlet
+        import sys
+        def f():
+            try:
+                raise ValueError('fun')
+            except:
+                exc_info = sys.exc_info()
+                greenlet(h).switch()
+                assert exc_info == sys.exc_info()
+
+        def h():
+            assert sys.exc_info() == (None, None, None)
+
+        greenlet(f).switch()
diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py
--- a/pypy/objspace/descroperation.py
+++ b/pypy/objspace/descroperation.py
@@ -508,7 +508,7 @@
         return space._type_issubtype(w_sub, w_type)
 
     def isinstance(space, w_inst, w_type):
-        return space._type_isinstance(w_inst, w_type)
+        return space.wrap(space._type_isinstance(w_inst, w_type))
 
     def issubtype_allow_override(space, w_sub, w_type):
         w_check = space.lookup(w_type, "__subclasscheck__")
diff --git a/pypy/objspace/std/bytearrayobject.py b/pypy/objspace/std/bytearrayobject.py
--- a/pypy/objspace/std/bytearrayobject.py
+++ b/pypy/objspace/std/bytearrayobject.py
@@ -250,7 +250,8 @@
 def repr__Bytearray(space, w_bytearray):
     s = w_bytearray.data
 
-    buf = StringBuilder(50)
+    # Good default if there are no replacements.
+    buf = StringBuilder(len("bytearray(b'')") + len(s))
 
     buf.append("bytearray(b'")
 
@@ -369,8 +370,8 @@
     newdata = []
     for i in range(len(list_w)):
         w_s = list_w[i]
-        if not (space.is_true(space.isinstance(w_s, space.w_str)) or
-                space.is_true(space.isinstance(w_s, space.w_bytearray))):
+        if not (space.isinstance_w(w_s, space.w_str) or
+                space.isinstance_w(w_s, space.w_bytearray)):
             raise operationerrfmt(
                 space.w_TypeError,
                 "sequence item %d: expected string, %s "
diff --git a/pypy/objspace/std/complextype.py b/pypy/objspace/std/complextype.py
--- a/pypy/objspace/std/complextype.py
+++ b/pypy/objspace/std/complextype.py
@@ -127,8 +127,8 @@
                and space.is_w(space.type(w_real), space.w_complex)):
         return w_real
 
-    if space.is_true(space.isinstance(w_real, space.w_str)) or \
-            space.is_true(space.isinstance(w_real, space.w_unicode)):
+    if space.isinstance_w(w_real, space.w_str) or \
+            space.isinstance_w(w_real, space.w_unicode):
         # a string argument
         if not noarg2:
             raise OperationError(space.w_TypeError,
@@ -203,8 +203,8 @@
         return (w_complex.realval, w_complex.imagval)
     #
     # Check that it is not a string (on which space.float() would succeed).
-    if (space.is_true(space.isinstance(w_complex, space.w_str)) or
-        space.is_true(space.isinstance(w_complex, space.w_unicode))):
+    if (space.isinstance_w(w_complex, space.w_str) or
+        space.isinstance_w(w_complex, space.w_unicode)):
         raise operationerrfmt(space.w_TypeError,
                               "complex number expected, got '%s'",
                               space.type(w_complex).getname(space))
diff --git a/pypy/objspace/std/floattype.py b/pypy/objspace/std/floattype.py
--- a/pypy/objspace/std/floattype.py
+++ b/pypy/objspace/std/floattype.py
@@ -32,14 +32,14 @@
         if space.is_w(w_floattype, space.w_float):
             return w_obj
         value = space.float_w(w_obj)
-    elif space.is_true(space.isinstance(w_value, space.w_str)):
+    elif space.isinstance_w(w_value, space.w_str):
         strvalue = space.str_w(w_value)
         try:
             value = string_to_float(strvalue)
         except ParseStringError, e:
             raise OperationError(space.w_ValueError,
                                  space.wrap(e.msg))
-    elif space.is_true(space.isinstance(w_value, space.w_unicode)):
+    elif space.isinstance_w(w_value, space.w_unicode):
         if space.config.objspace.std.withropeunicode:
             from pypy.objspace.std.ropeunicodeobject import unicode_to_decimal_w
         else:
diff --git a/pypy/objspace/std/formatting.py b/pypy/objspace/std/formatting.py
--- a/pypy/objspace/std/formatting.py
+++ b/pypy/objspace/std/formatting.py
@@ -1,13 +1,15 @@
 """
 String formatting routines.
 """
-from pypy.rlib.unroll import unrolling_iterable
+from pypy.interpreter.error import OperationError
+from pypy.objspace.std.unicodetype import unicode_from_object
+from pypy.rlib import jit
 from pypy.rlib.rarithmetic import ovfcheck
 from pypy.rlib.rfloat import formatd, DTSF_ALT, isnan, isinf
-from pypy.interpreter.error import OperationError
+from pypy.rlib.rstring import StringBuilder, UnicodeBuilder
+from pypy.rlib.unroll import unrolling_iterable
 from pypy.tool.sourcetools import func_with_new_name
-from pypy.rlib.rstring import StringBuilder, UnicodeBuilder
-from pypy.objspace.std.unicodetype import unicode_from_object
+
 
 class BaseStringFormatter(object):
     def __init__(self, space, values_w, w_valuedict):
@@ -173,6 +175,9 @@
                 raise OperationError(space.w_ValueError,
                                      space.wrap("incomplete format"))
 
+        # Only shows up if we've already started inlining format(), so just
+        # unconditionally unroll this.
+        @jit.unroll_safe
         def getmappingkey(self):
             # return the mapping key in a '%(key)s' specifier
             fmt = self.fmt
@@ -233,6 +238,8 @@
 
             return w_value
 
+        # Same as getmappingkey
+        @jit.unroll_safe
         def peel_flags(self):
             self.f_ljust = False
             self.f_sign  = False
@@ -255,6 +262,8 @@
                     break
                 self.forward()
 
+        # Same as getmappingkey
+        @jit.unroll_safe
         def peel_num(self):
             space = self.space
             c = self.peekchr()
@@ -276,6 +285,7 @@
                 c = self.peekchr()
             return result
 
+        @jit.look_inside_iff(lambda self: jit.isconstant(self.fmt))
         def format(self):
             lgt = len(self.fmt) + 4 * len(self.values_w) + 10
             if do_unicode:
@@ -415,15 +425,15 @@
                                      space.wrap("operand does not support "
                                                 "unary str"))
             w_result = space.get_and_call_function(w_impl, w_value)
-            if space.is_true(space.isinstance(w_result,
-                                              space.w_unicode)):
+            if space.isinstance_w(w_result,
+                                              space.w_unicode):
                 raise NeedUnicodeFormattingError
             return space.str_w(w_result)
 
         def fmt_s(self, w_value):
             space = self.space
-            got_unicode = space.is_true(space.isinstance(w_value,
-                                                         space.w_unicode))
+            got_unicode = space.isinstance_w(w_value,
+                                                         space.w_unicode)
             if not do_unicode:
                 if got_unicode:
                     raise NeedUnicodeFormattingError
@@ -442,13 +452,13 @@
         def fmt_c(self, w_value):
             self.prec = -1     # just because
             space = self.space
-            if space.is_true(space.isinstance(w_value, space.w_str)):
+            if space.isinstance_w(w_value, space.w_str):
                 s = space.str_w(w_value)
                 if len(s) != 1:
                     raise OperationError(space.w_TypeError,
                                          space.wrap("%c requires int or char"))
                 self.std_wp(s)
-            elif space.is_true(space.isinstance(w_value, space.w_unicode)):
+            elif space.isinstance_w(w_value, space.w_unicode):
                 if not do_unicode:
                     raise NeedUnicodeFormattingError
                 ustr = space.unicode_w(w_value)
@@ -510,15 +520,15 @@
     return space.wrap(result)
 
 def mod_format(space, w_format, w_values, do_unicode=False):
-    if space.is_true(space.isinstance(w_values, space.w_tuple)):
+    if space.isinstance_w(w_values, space.w_tuple):
         values_w = space.fixedview(w_values)
         return format(space, w_format, values_w, None, do_unicode)
     else:
         # we check directly for dict to avoid obscure checking
         # in simplest case
-        if space.is_true(space.isinstance(w_values, space.w_dict)) or \
+        if space.isinstance_w(w_values, space.w_dict) or \
            (space.lookup(w_values, '__getitem__') and
-           not space.is_true(space.isinstance(w_values, space.w_basestring))):
+           not space.isinstance_w(w_values, space.w_basestring)):
             return format(space, w_format, [w_values], w_values, do_unicode)
         else:
             return format(space, w_format, [w_values], None, do_unicode)
diff --git a/pypy/objspace/std/inttype.py b/pypy/objspace/std/inttype.py
--- a/pypy/objspace/std/inttype.py
+++ b/pypy/objspace/std/inttype.py
@@ -99,10 +99,10 @@
         if type(w_value) is W_IntObject:
             value = w_value.intval
             ok = True
-        elif space.is_true(space.isinstance(w_value, space.w_str)):
+        elif space.isinstance_w(w_value, space.w_str):
             value, w_longval = string_to_int_or_long(space, space.str_w(w_value))
             ok = True
-        elif space.is_true(space.isinstance(w_value, space.w_unicode)):
+        elif space.isinstance_w(w_value, space.w_unicode):
             if space.config.objspace.std.withropeunicode:
                 from pypy.objspace.std.ropeunicodeobject import unicode_to_decimal_w
             else:
@@ -145,7 +145,7 @@
     else:
         base = space.int_w(w_base)
 
-        if space.is_true(space.isinstance(w_value, space.w_unicode)):
+        if space.isinstance_w(w_value, space.w_unicode):
             if space.config.objspace.std.withropeunicode:
                 from pypy.objspace.std.ropeunicodeobject import unicode_to_decimal_w
             else:
diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py
--- a/pypy/objspace/std/listobject.py
+++ b/pypy/objspace/std/listobject.py
@@ -44,7 +44,7 @@
     if w_iterable is not None:
         # unfortunately this is duplicating space.unpackiterable to avoid
         # assigning a new RPython list to 'wrappeditems', which defeats the
-        # W_FastSeqIterObject optimization.
+        # W_FastListIterObject optimization.
         if isinstance(w_iterable, W_ListObject):
             items_w.extend(w_iterable.wrappeditems)
         elif isinstance(w_iterable, W_TupleObject):
diff --git a/pypy/objspace/std/longtype.py b/pypy/objspace/std/longtype.py
--- a/pypy/objspace/std/longtype.py
+++ b/pypy/objspace/std/longtype.py
@@ -24,9 +24,9 @@
             return w_value
         elif type(w_value) is W_LongObject:
             return newbigint(space, w_longtype, w_value.num)
-        elif space.is_true(space.isinstance(w_value, space.w_str)):
+        elif space.isinstance_w(w_value, space.w_str):
             return string_to_w_long(space, w_longtype, space.str_w(w_value))
-        elif space.is_true(space.isinstance(w_value, space.w_unicode)):
+        elif space.isinstance_w(w_value, space.w_unicode):
             if space.config.objspace.std.withropeunicode:
                 from pypy.objspace.std.ropeunicodeobject import unicode_to_decimal_w
             else:
@@ -51,7 +51,7 @@
     else:
         base = space.int_w(w_base)
 
-        if space.is_true(space.isinstance(w_value, space.w_unicode)):
+        if space.isinstance_w(w_value, space.w_unicode):
             from pypy.objspace.std.unicodeobject import unicode_to_decimal_w
             s = unicode_to_decimal_w(space, w_value)
         else:
diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py
--- a/pypy/objspace/std/mapdict.py
+++ b/pypy/objspace/std/mapdict.py
@@ -132,7 +132,10 @@
             cache[selector] = attr
         return attr
 
-    @jit.unroll_safe
+    @jit.look_inside_iff(lambda self, obj, selector, w_value:
+            jit.isconstant(self) and
+            jit.isconstant(selector[0]) and
+            jit.isconstant(selector[1]))
     def add_attr(self, obj, selector, w_value):
         # grumble, jit needs this
         attr = self._get_new_attr(selector[0], selector[1])
@@ -347,7 +350,7 @@
 SLOTS_STARTING_FROM = 3
 
 
-class BaseMapdictObject: # slightly evil to make it inherit from W_Root
+class BaseMapdictObject:
     _mixin_ = True
 
     def _init_empty(self, map):
diff --git a/pypy/objspace/std/newformat.py b/pypy/objspace/std/newformat.py
--- a/pypy/objspace/std/newformat.py
+++ b/pypy/objspace/std/newformat.py
@@ -3,9 +3,10 @@
 import string
 
 from pypy.interpreter.error import OperationError
-from pypy.rlib import rstring, runicode, rlocale, rarithmetic, rfloat
+from pypy.rlib import rstring, runicode, rlocale, rarithmetic, rfloat, jit
 from pypy.rlib.objectmodel import specialize
 from pypy.rlib.rfloat import copysign, formatd
+from pypy.tool import sourcetools
 
 
 @specialize.argtype(1)
@@ -36,314 +37,321 @@
 ANS_MANUAL = 3
 
 
-class TemplateFormatter(object):
+def make_template_formatting_class():
+    class TemplateFormatter(object):
 
-    _annspecialcase_ = "specialize:ctr_location"
+        parser_list_w = None
 
-    parser_list_w = None
+        def __init__(self, space, is_unicode, template):
+            self.space = space
+            self.is_unicode = is_unicode
+            self.empty = u"" if is_unicode else ""
+            self.template = template
 
-    def __init__(self, space, is_unicode, template):
-        self.space = space
-        self.is_unicode = is_unicode
-        self.empty = u"" if is_unicode else ""
-        self.template = template
+        def build(self, args):
+            self.args, self.kwargs = args.unpack()
+            self.auto_numbering = 0
+            self.auto_numbering_state = ANS_INIT
+            return self._build_string(0, len(self.template), 2)
 
-    def build(self, args):
-        self.args, self.kwargs = args.unpack()
-        self.auto_numbering = 0
-        self.auto_numbering_state = ANS_INIT
-        return self._build_string(0, len(self.template), 2)
+        def _build_string(self, start, end, level):
+            space = self.space
+            if self.is_unicode:
+                out = rstring.UnicodeBuilder()
+            else:
+                out = rstring.StringBuilder()
+            if not level:
+                raise OperationError(space.w_ValueError,
+                                     space.wrap("Recursion depth exceeded"))
+            level -= 1
+            s = self.template
+            return self._do_build_string(start, end, level, out, s)
 
-    def _build_string(self, start, end, level):
-        space = self.space
-        if self.is_unicode:
-            out = rstring.UnicodeBuilder()
-        else:
-            out = rstring.StringBuilder()
-        if not level:
-            raise OperationError(space.w_ValueError,
-                                 space.wrap("Recursion depth exceeded"))
-        level -= 1
-        s = self.template
-        last_literal = i = start
-        while i < end:
-            c = s[i]
-            i += 1
-            if c == "{" or c == "}":
-                at_end = i == end
-                # Find escaped "{" and "}"
-                markup_follows = True
-                if c == "}":
-                    if at_end or s[i] != "}":
-                        raise OperationError(space.w_ValueError,
-                                             space.wrap("Single '}'"))
-                    i += 1
-                    markup_follows = False
-                if c == "{":
-                    if at_end:
-                        raise OperationError(space.w_ValueError,
-                                             space.wrap("Single '{'"))
-                    if s[i] == "{":
+        @jit.look_inside_iff(lambda self, start, end, level, out, s: jit.isconstant(s))
+        def _do_build_string(self, start, end, level, out, s):
+            space = self.space
+            last_literal = i = start
+            while i < end:
+                c = s[i]
+                i += 1
+                if c == "{" or c == "}":
+                    at_end = i == end
+                    # Find escaped "{" and "}"
+                    markup_follows = True
+                    if c == "}":
+                        if at_end or s[i] != "}":
+                            raise OperationError(space.w_ValueError,
+                                                 space.wrap("Single '}'"))
                         i += 1
                         markup_follows = False
-                # Attach literal data
-                out.append_slice(s, last_literal, i - 1)
-                if not markup_follows:
+                    if c == "{":
+                        if at_end:
+                            raise OperationError(space.w_ValueError,
+                                                 space.wrap("Single '{'"))
+                        if s[i] == "{":
+                            i += 1
+                            markup_follows = False
+                    # Attach literal data
+                    out.append_slice(s, last_literal, i - 1)
+                    if not markup_follows:
+                        last_literal = i
+                        continue
+                    nested = 1
+                    field_start = i
+                    recursive = False
+                    while i < end:
+                        c = s[i]
+                        if c == "{":
+                            recursive = True
+                            nested += 1
+                        elif c == "}":
+                            nested -= 1
+                            if not nested:
+                                break
+                        i += 1
+                    if nested:
+                        raise OperationError(space.w_ValueError,
+                                             space.wrap("Unmatched '{'"))
+                    rendered = self._render_field(field_start, i, recursive, level)
+                    out.append(rendered)
+                    i += 1
                     last_literal = i
-                    continue
-                nested = 1
-                field_start = i
-                recursive = False
-                while i < end:
-                    c = s[i]
-                    if c == "{":
-                        recursive = True
-                        nested += 1
-                    elif c == "}":
-                        nested -= 1
-                        if not nested:
-                            break
-                    i += 1
-                if nested:
-                    raise OperationError(space.w_ValueError,
-                                         space.wrap("Unmatched '{'"))
-                rendered = self._render_field(field_start, i, recursive, level)
-                out.append(rendered)
+
+            out.append_slice(s, last_literal, end)
+            return out.build()
+
+        def _parse_field(self, start, end):
+            s = self.template
+            # Find ":" or "!"
+            i = start
+            while i < end:
+                c = s[i]
+                if c == ":" or c == "!":
+                    end_name = i
+                    if c == "!":
+                        i += 1
+                        if i == end:
+                            w_msg = self.space.wrap("expected conversion")
+                            raise OperationError(self.space.w_ValueError, w_msg)
+                        conversion = s[i]
+                        i += 1
+                        if i < end:
+                            if s[i] != ':':
+                                w_msg = self.space.wrap("expected ':' after"
+                                                        " format specifier")
+                                raise OperationError(self.space.w_ValueError,
+                                                     w_msg)
+                            i += 1
+                    else:
+                        conversion = None
+                        i += 1
+                    return s[start:end_name], conversion, i
                 i += 1
-                last_literal = i
+            return s[start:end], None, end
 
-        out.append_slice(s, last_literal, end)
-        return out.build()
-
-    def _parse_field(self, start, end):
-        s = self.template
-        # Find ":" or "!"
-        i = start
-        while i < end:
-            c = s[i]
-            if c == ":" or c == "!":
-                end_name = i
-                if c == "!":
-                    i += 1
-                    if i == end:
-                        w_msg = self.space.wrap("expected conversion")
-                        raise OperationError(self.space.w_ValueError, w_msg)
-                    conversion = s[i]
-                    i += 1
-                    if i < end:
-                        if s[i] != ':':
-                            w_msg = self.space.wrap("expected ':' after"
-                                                    " format specifier")
-                            raise OperationError(self.space.w_ValueError,
-                                                 w_msg)
-                        i += 1
+        def _get_argument(self, name):
+            # First, find the argument.
+            space = self.space
+            i = 0
+            end = len(name)
+            while i < end:
+                c = name[i]
+                if c == "[" or c == ".":
+                    break
+                i += 1
+            empty = not i
+            if empty:
+                index = -1
+            else:
+                index, stop = _parse_int(self.space, name, 0, i)
+                if stop != i:
+                    index = -1
+            use_numeric = empty or index != -1
+            if self.auto_numbering_state == ANS_INIT and use_numeric:
+                if empty:
+                    self.auto_numbering_state = ANS_AUTO
                 else:
-                    conversion = None
-                    i += 1
-                return s[start:end_name], conversion, i
-            i += 1
-        return s[start:end], None, end
-
-    def _get_argument(self, name):
-        # First, find the argument.
-        space = self.space
-        i = 0
-        end = len(name)
-        while i < end:
-            c = name[i]
-            if c == "[" or c == ".":
-                break
-            i += 1
-        empty = not i
-        if empty:
-            index = -1
-        else:
-            index, stop = _parse_int(self.space, name, 0, i)
-            if stop != i:
-                index = -1
-        use_numeric = empty or index != -1
-        if self.auto_numbering_state == ANS_INIT and use_numeric:
-            if empty:
-                self.auto_numbering_state = ANS_AUTO
-            else:
-                self.auto_numbering_state = ANS_MANUAL
-        if use_numeric:
-            if self.auto_numbering_state == ANS_MANUAL:
-                if empty:
-                    msg = "switching from manual to automatic numbering"
+                    self.auto_numbering_state = ANS_MANUAL
+            if use_numeric:
+                if self.auto_numbering_state == ANS_MANUAL:
+                    if empty:
+                        msg = "switching from manual to automatic numbering"
+                        raise OperationError(space.w_ValueError,
+                                             space.wrap(msg))
+                elif not empty:
+                    msg = "switching from automatic to manual numbering"
                     raise OperationError(space.w_ValueError,
                                          space.wrap(msg))
-            elif not empty:
-                msg = "switching from automatic to manual numbering"
-                raise OperationError(space.w_ValueError,
-                                     space.wrap(msg))
-        if empty:
-            index = self.auto_numbering
-            self.auto_numbering += 1
-        if index == -1:
-            kwarg = name[:i]
-            if self.is_unicode:
+            if empty:
+                index = self.auto_numbering
+                self.auto_numbering += 1
+            if index == -1:
+                kwarg = name[:i]
+                if self.is_unicode:
+                    try:
+                        arg_key = kwarg.encode("latin-1")
+                    except UnicodeEncodeError:
+                        # Not going to be found in a dict of strings.
+                        raise OperationError(space.w_KeyError, space.wrap(kwarg))
+                else:
+                    arg_key = kwarg
                 try:
-                    arg_key = kwarg.encode("latin-1")
-                except UnicodeEncodeError:
-                    # Not going to be found in a dict of strings.
-                    raise OperationError(space.w_KeyError, space.wrap(kwarg))
+                    w_arg = self.kwargs[arg_key]
+                except KeyError:
+                    raise OperationError(space.w_KeyError, space.wrap(arg_key))
             else:
-                arg_key = kwarg
-            try:
-                w_arg = self.kwargs[arg_key]
-            except KeyError:
-                raise OperationError(space.w_KeyError, space.wrap(arg_key))
-        else:
-            try:
-                w_arg = self.args[index]
-            except IndexError:
-                w_msg = space.wrap("index out of range")
-                raise OperationError(space.w_IndexError, w_msg)
-        return self._resolve_lookups(w_arg, name, i, end)
+                try:
+                    w_arg = self.args[index]
+                except IndexError:
+                    w_msg = space.wrap("index out of range")
+                    raise OperationError(space.w_IndexError, w_msg)
+            return self._resolve_lookups(w_arg, name, i, end)
 
-    def _resolve_lookups(self, w_obj, name, start, end):
-        # Resolve attribute and item lookups.
-        space = self.space
-        i = start
-        while i < end:
-            c = name[i]
-            if c == ".":
+        def _resolve_lookups(self, w_obj, name, start, end):
+            # Resolve attribute and item lookups.
+            space = self.space
+            i = start
+            while i < end:
+                c = name[i]
+                if c == ".":
+                    i += 1
+                    start = i
+                    while i < end:
+                        c = name[i]
+                        if c == "[" or c == ".":
+                            break
+                        i += 1
+                    if start == i:
+                        w_msg = space.wrap("Empty attribute in format string")
+                        raise OperationError(space.w_ValueError, w_msg)
+                    w_attr = space.wrap(name[start:i])
+                    if w_obj is not None:
+                        w_obj = space.getattr(w_obj, w_attr)
+                    else:
+                        self.parser_list_w.append(space.newtuple([
+                            space.w_True, w_attr]))
+                elif c == "[":
+                    got_bracket = False
+                    i += 1
+                    start = i
+                    while i < end:
+                        c = name[i]
+                        if c == "]":
+                            got_bracket = True
+                            break
+                        i += 1
+                    if not got_bracket:
+                        raise OperationError(space.w_ValueError,
+                                             space.wrap("Missing ']'"))
+                    index, reached = _parse_int(self.space, name, start, i)
+                    if index != -1 and reached == i:
+                        w_item = space.wrap(index)
+                    else:
+                        w_item = space.wrap(name[start:i])
+                    i += 1 # Skip "]"
+                    if w_obj is not None:
+                        w_obj = space.getitem(w_obj, w_item)
+                    else:
+                        self.parser_list_w.append(space.newtuple([
+                            space.w_False, w_item]))
+                else:
+                    msg = "Only '[' and '.' may follow ']'"
+                    raise OperationError(space.w_ValueError, space.wrap(msg))
+            return w_obj
+
+        def formatter_field_name_split(self):
+            space = self.space
+            name = self.template
+            i = 0
+            end = len(name)
+            while i < end:
+                c = name[i]
+                if c == "[" or c == ".":
+                    break
                 i += 1
-                start = i
-                while i < end:
-                    c = name[i]
-                    if c == "[" or c == ".":
-                        break
-                    i += 1
-                if start == i:
-                    w_msg = space.wrap("Empty attribute in format string")
-                    raise OperationError(space.w_ValueError, w_msg)
-                w_attr = space.wrap(name[start:i])
-                if w_obj is not None:
-                    w_obj = space.getattr(w_obj, w_attr)
-                else:
-                    self.parser_list_w.append(space.newtuple([
-                        space.w_True, w_attr]))
-            elif c == "[":
-                got_bracket = False
-                i += 1
-                start = i
-                while i < end:
-                    c = name[i]
-                    if c == "]":
-                        got_bracket = True
-                        break
-                    i += 1
-                if not got_bracket:
-                    raise OperationError(space.w_ValueError,
-                                         space.wrap("Missing ']'"))
-                index, reached = _parse_int(self.space, name, start, i)
-                if index != -1 and reached == i:
-                    w_item = space.wrap(index)
-                else:
-                    w_item = space.wrap(name[start:i])
-                i += 1 # Skip "]"
-                if w_obj is not None:
-                    w_obj = space.getitem(w_obj, w_item)
-                else:
-                    self.parser_list_w.append(space.newtuple([
-                        space.w_False, w_item]))
+            if i == 0:
+                index = -1
             else:
-                msg = "Only '[' and '.' may follow ']'"
-                raise OperationError(space.w_ValueError, space.wrap(msg))
-        return w_obj
+                index, stop = _parse_int(self.space, name, 0, i)
+                if stop != i:
+                    index = -1
+            if index >= 0:
+                w_first = space.wrap(index)
+            else:
+                w_first = space.wrap(name[:i])
+            #
+            self.parser_list_w = []
+            self._resolve_lookups(None, name, i, end)
+            #
+            return space.newtuple([w_first,
+                                   space.iter(space.newlist(self.parser_list_w))])
 
-    def formatter_field_name_split(self):
-        space = self.space
-        name = self.template
-        i = 0
-        end = len(name)
-        while i < end:
-            c = name[i]
-            if c == "[" or c == ".":
-                break
-            i += 1
-        if i == 0:
-            index = -1
-        else:
-            index, stop = _parse_int(self.space, name, 0, i)
-            if stop != i:
-                index = -1
-        if index >= 0:
-            w_first = space.wrap(index)
-        else:
-            w_first = space.wrap(name[:i])
-        #
-        self.parser_list_w = []
-        self._resolve_lookups(None, name, i, end)
-        #
-        return space.newtuple([w_first,
-                               space.iter(space.newlist(self.parser_list_w))])
+        def _convert(self, w_obj, conversion):
+            space = self.space
+            conv = conversion[0]
+            if conv == "r":
+                return space.repr(w_obj)
+            elif conv == "s":
+                if self.is_unicode:
+                    return space.call_function(space.w_unicode, w_obj)
+                return space.str(w_obj)
+            else:
+                raise OperationError(self.space.w_ValueError,
+                                     self.space.wrap("invalid conversion"))
 
-    def _convert(self, w_obj, conversion):
-        space = self.space
-        conv = conversion[0]
-        if conv == "r":
-            return space.repr(w_obj)
-        elif conv == "s":
-            if self.is_unicode:
-                return space.call_function(space.w_unicode, w_obj)
-            return space.str(w_obj)
-        else:
-            raise OperationError(self.space.w_ValueError,
-                                 self.space.wrap("invalid conversion"))
+        def _render_field(self, start, end, recursive, level):
+            name, conversion, spec_start = self._parse_field(start, end)
+            spec = self.template[spec_start:end]
+            #
+            if self.parser_list_w is not None:
+                # used from formatter_parser()
+                if level == 1:    # ignore recursive calls
+                    space = self.space
+                    startm1 = start - 1
+                    assert startm1 >= self.last_end
+                    w_entry = space.newtuple([
+                        space.wrap(self.template[self.last_end:startm1]),
+                        space.wrap(name),
+                        space.wrap(spec),
+                        space.wrap(conversion)])
+                    self.parser_list_w.append(w_entry)
+                    self.last_end = end + 1
+                return self.empty
+            #
+            w_obj = self._get_argument(name)
+            if conversion is not None:
+                w_obj = self._convert(w_obj, conversion)
+            if recursive:
+                spec = self._build_string(spec_start, end, level)
+            w_rendered = self.space.format(w_obj, self.space.wrap(spec))
+            unwrapper = "unicode_w" if self.is_unicode else "str_w"
+            to_interp = getattr(self.space, unwrapper)
+            return to_interp(w_rendered)
 
-    def _render_field(self, start, end, recursive, level):
-        name, conversion, spec_start = self._parse_field(start, end)
-        spec = self.template[spec_start:end]
-        #
-        if self.parser_list_w is not None:
-            # used from formatter_parser()
-            if level == 1:    # ignore recursive calls
-                space = self.space
-                startm1 = start - 1
-                assert startm1 >= self.last_end
-                w_entry = space.newtuple([
-                    space.wrap(self.template[self.last_end:startm1]),
-                    space.wrap(name),
-                    space.wrap(spec),
-                    space.wrap(conversion)])
-                self.parser_list_w.append(w_entry)
-                self.last_end = end + 1
-            return self.empty
-        #
-        w_obj = self._get_argument(name)
-        if conversion is not None:
-            w_obj = self._convert(w_obj, conversion)
-        if recursive:
-            spec = self._build_string(spec_start, end, level)
-        w_rendered = self.space.format(w_obj, self.space.wrap(spec))
-        unwrapper = "unicode_w" if self.is_unicode else "str_w"
-        to_interp = getattr(self.space, unwrapper)
-        return to_interp(w_rendered)
+        def formatter_parser(self):
+            self.parser_list_w = []
+            self.last_end = 0
+            self._build_string(0, len(self.template), 2)
+            #
+            space = self.space
+            if self.last_end < len(self.template):
+                w_lastentry = space.newtuple([
+                    space.wrap(self.template[self.last_end:]),
+                    space.w_None,
+                    space.w_None,
+                    space.w_None])
+                self.parser_list_w.append(w_lastentry)
+            return space.iter(space.newlist(self.parser_list_w))
+    return TemplateFormatter
 
-    def formatter_parser(self):
-        self.parser_list_w = []
-        self.last_end = 0
-        self._build_string(0, len(self.template), 2)
-        #
-        space = self.space
-        if self.last_end < len(self.template):
-            w_lastentry = space.newtuple([
-                space.wrap(self.template[self.last_end:]),
-                space.w_None,
-                space.w_None,
-                space.w_None])
-            self.parser_list_w.append(w_lastentry)
-        return space.iter(space.newlist(self.parser_list_w))
-
+StrTemplateFormatter = make_template_formatting_class()
+UnicodeTemplateFormatter = make_template_formatting_class()
 
 def str_template_formatter(space, template):
-    return TemplateFormatter(space, False, template)
+    return StrTemplateFormatter(space, False, template)
 
 def unicode_template_formatter(space, template):
-    return TemplateFormatter(space, True, template)
+    return UnicodeTemplateFormatter(space, True, template)
 
 
 def format_method(space, w_string, args, is_unicode):
@@ -380,756 +388,759 @@
 
 LONG_DIGITS = string.digits + string.ascii_lowercase
 
-class Formatter(BaseFormatter):
-    """__format__ implementation for builtin types."""
+def make_formatting_class():
+    class Formatter(BaseFormatter):
+        """__format__ implementation for builtin types."""
 
-    _annspecialcase_ = "specialize:ctr_location"
-    _grouped_digits = None
+        _grouped_digits = None
 
-    def __init__(self, space, is_unicode, spec):
-        self.space = space
-        self.is_unicode = is_unicode
-        self.empty = u"" if is_unicode else ""
-        self.spec = spec
+        def __init__(self, space, is_unicode, spec):
+            self.space = space
+            self.is_unicode = is_unicode
+            self.empty = u"" if is_unicode else ""
+            self.spec = spec
 
-    def _is_alignment(self, c):
-        return (c == "<" or
-                c == ">" or
-                c == "=" or
-                c == "^")
+        def _is_alignment(self, c):
+            return (c == "<" or
+                    c == ">" or
+                    c == "=" or
+                    c == "^")
 
-    def _is_sign(self, c):
-        return (c == " " or
-                c == "+" or
-                c == "-")
+        def _is_sign(self, c):
+            return (c == " " or
+                    c == "+" or
+                    c == "-")
 
-    def _parse_spec(self, default_type, default_align):
-        space = self.space
-        self._fill_char = self._lit("\0")[0]
-        self._align = default_align
-        self._alternate = False
-        self._sign = "\0"
-        self._thousands_sep = False
-        self._precision = -1
-        the_type = default_type
-        spec = self.spec
-        if not spec:
-            return True
-        length = len(spec)
-        i = 0
-        got_align = True
-        if length - i >= 2 and self._is_alignment(spec[i + 1]):
-            self._align = spec[i + 1]
-            self._fill_char = spec[i]
-            i += 2
-        elif length - i >= 1 and self._is_alignment(spec[i]):
-            self._align = spec[i]
-            i += 1
-        else:
-            got_align = False
-        if length - i >= 1 and self._is_sign(spec[i]):
-            self._sign = spec[i]
-            i += 1
-        if length - i >= 1 and spec[i] == "#":
-            self._alternate = True
-            i += 1
-        if self._fill_char == "\0" and length - i >= 1 and spec[i] == "0":
-            self._fill_char = self._lit("0")[0]
-            if not got_align:
-                self._align = "="
-            i += 1
-        start_i = i
-        self._width, i = _parse_int(self.space, spec, i, length)
-        if length != i and spec[i] == ",":
-            self._thousands_sep = True
-            i += 1
-        if length != i and spec[i] == ".":
-            i += 1
-            self._precision, i = _parse_int(self.space, spec, i, length)
-            if self._precision == -1:
+        def _parse_spec(self, default_type, default_align):
+            space = self.space
+            self._fill_char = self._lit("\0")[0]
+            self._align = default_align
+            self._alternate = False
+            self._sign = "\0"
+            self._thousands_sep = False
+            self._precision = -1
+            the_type = default_type
+            spec = self.spec
+            if not spec:
+                return True
+            length = len(spec)
+            i = 0
+            got_align = True
+            if length - i >= 2 and self._is_alignment(spec[i + 1]):
+                self._align = spec[i + 1]
+                self._fill_char = spec[i]
+                i += 2
+            elif length - i >= 1 and self._is_alignment(spec[i]):
+                self._align = spec[i]
+                i += 1
+            else:
+                got_align = False
+            if length - i >= 1 and self._is_sign(spec[i]):
+                self._sign = spec[i]
+                i += 1
+            if length - i >= 1 and spec[i] == "#":
+                self._alternate = True
+                i += 1
+            if self._fill_char == "\0" and length - i >= 1 and spec[i] == "0":
+                self._fill_char = self._lit("0")[0]
+                if not got_align:
+                    self._align = "="
+                i += 1
+            start_i = i
+            self._width, i = _parse_int(self.space, spec, i, length)
+            if length != i and spec[i] == ",":
+                self._thousands_sep = True
+                i += 1
+            if length != i and spec[i] == ".":
+                i += 1
+                self._precision, i = _parse_int(self.space, spec, i, length)
+                if self._precision == -1:
+                    raise OperationError(space.w_ValueError,
+                                         space.wrap("no precision given"))
+            if length - i > 1:
                 raise OperationError(space.w_ValueError,
-                                     space.wrap("no precision given"))
-        if length - i > 1:
-            raise OperationError(space.w_ValueError,
-                                 space.wrap("invalid format spec"))
-        if length - i == 1:
-            presentation_type = spec[i]
-            if self.is_unicode:
-                try:
-                    the_type = spec[i].encode("ascii")[0]
-                except UnicodeEncodeError:
+                                     space.wrap("invalid format spec"))
+            if length - i == 1:
+                presentation_type = spec[i]
+                if self.is_unicode:
+                    try:
+                        the_type = spec[i].encode("ascii")[0]
+                    except UnicodeEncodeError:
+                        raise OperationError(space.w_ValueError,
+                                             space.wrap("invalid presentation type"))
+                else:
+                    the_type = presentation_type
+                i += 1
+            self._type = the_type
+            if self._thousands_sep:
+                tp = self._type
+                if (tp == "d" or
+                    tp == "e" or
+                    tp == "f" or
+                    tp == "g" or
+                    tp == "E" or
+                    tp == "G" or
+                    tp == "%" or
+                    tp == "F" or
+                    tp == "\0"):
+                    # ok
+                    pass
+                else:
                     raise OperationError(space.w_ValueError,
-                                         space.wrap("invalid presentation type"))
+                                         space.wrap("invalid type with ','"))
+            return False
+
+        def _calc_padding(self, string, length):
+            """compute left and right padding, return total width of string"""
+            if self._width != -1 and length < self._width:
+                total = self._width
             else:
-                the_type = presentation_type
-            i += 1
-        self._type = the_type
-        if self._thousands_sep:
-            tp = self._type
-            if (tp == "d" or
-                tp == "e" or
-                tp == "f" or
-                tp == "g" or
-                tp == "E" or
-                tp == "G" or
-                tp == "%" or
-                tp == "F" or
-                tp == "\0"):
-                # ok
-                pass
+                total = length
+            align = self._align
+            if align == ">":
+                left = total - length
+            elif align == "^":
+                left = (total - length) / 2
+            elif align == "<" or align == "=":
+                left = 0
             else:
-                raise OperationError(space.w_ValueError,
-                                     space.wrap("invalid type with ','"))
-        return False
+                raise AssertionError("shouldn't be here")
+            right = total - length - left
+            self._left_pad = left
+            self._right_pad = right
+            return total
 
-    def _calc_padding(self, string, length):
-        """compute left and right padding, return total width of string"""
-        if self._width != -1 and length < self._width:
-            total = self._width
-        else:
-            total = length
-        align = self._align
-        if align == ">":
-            left = total - length
-        elif align == "^":
-            left = (total - length) / 2
-        elif align == "<" or align == "=":
-            left = 0
-        else:
-            raise AssertionError("shouldn't be here")
-        right = total - length - left
-        self._left_pad = left
-        self._right_pad = right
-        return total
-
-    def _lit(self, s):
-        if self.is_unicode:
-            return s.decode("ascii")
-        else:
-            return s
-
-    def _pad(self, string):
-        builder = self._builder()
-        builder.append_multiple_char(self._fill_char, self._left_pad)
-        builder.append(string)
-        builder.append_multiple_char(self._fill_char, self._right_pad)
-        return builder.build()
-
-    def _builder(self):
-        if self.is_unicode:
-            return rstring.UnicodeBuilder()
-        else:
-            return rstring.StringBuilder()
-
-    def _unknown_presentation(self, tp):
-        msg = "unknown presentation for %s: '%s'"
-        w_msg = self.space.wrap(msg  % (tp, self._type))
-        raise OperationError(self.space.w_ValueError, w_msg)
-
-    def format_string(self, string):
-        space = self.space
-        if self._parse_spec("s", "<"):
-            return space.wrap(string)
-        if self._type != "s":
-            self._unknown_presentation("string")
-        if self._sign != "\0":
-            msg = "Sign not allowed in string format specifier"
-            raise OperationError(space.w_ValueError, space.wrap(msg))
-        if self._alternate:
-            msg = "Alternate form not allowed in string format specifier"
-            raise OperationError(space.w_ValueError, space.wrap(msg))
-        if self._align == "=":
-            msg = "'=' alignment not allowed in string format specifier"
-            raise OperationError(space.w_ValueError, space.wrap(msg))
-        length = len(string)
-        precision = self._precision
-        if precision != -1 and length >= precision:
-            assert precision >= 0
-            length = precision
-            string = string[:precision]
-        if self._fill_char == "\0":
-            self._fill_char = self._lit(" ")[0]
-        self._calc_padding(string, length)
-        return space.wrap(self._pad(string))
-
-    def _get_locale(self, tp):
-        space = self.space
-        if tp == "n":
-            dec, thousands, grouping = rlocale.numeric_formatting()
-        elif self._thousands_sep:
-            dec = "."
-            thousands = ","
-            grouping = "\3\0"
-        else:
-            dec = "."
-            thousands = ""
-            grouping = "\256"
-        if self.is_unicode:
-            self._loc_dec = dec.decode("ascii")
-            self._loc_thousands = thousands.decode("ascii")
-        else:
-            self._loc_dec = dec
-            self._loc_thousands = thousands
-        self._loc_grouping = grouping
-
-    def _calc_num_width(self, n_prefix, sign_char, to_number, n_number,
-                        n_remainder, has_dec, digits):
-        """Calculate widths of all parts of formatted number.
-
-        Output will look like:
-
-            <lpadding> <sign> <prefix> <spadding> <grouped_digits> <decimal>
-            <remainder> <rpadding>
-
-        sign is computed from self._sign, and the sign of the number
-        prefix is given
-        digits is known
-        """
-        spec = NumberSpec()
-        spec.n_digits = n_number - n_remainder - has_dec
-        spec.n_prefix = n_prefix
-        spec.n_lpadding = 0
-        spec.n_decimal = int(has_dec)
-        spec.n_remainder = n_remainder
-        spec.n_spadding = 0
-        spec.n_rpadding = 0
-        spec.n_min_width = 0
-        spec.n_total = 0
-        spec.sign = "\0"
-        spec.n_sign = 0
-        sign = self._sign
-        if sign == "+":
-            spec.n_sign = 1
-            spec.sign = "-" if sign_char == "-" else "+"
-        elif sign == " ":
-            spec.n_sign = 1
-            spec.sign = "-" if sign_char == "-" else " "
-        elif sign_char == "-":
-            spec.n_sign = 1
-            spec.sign = "-"
-        extra_length = (spec.n_sign + spec.n_prefix + spec.n_decimal +
-                        spec.n_remainder) # Not padding or digits
-        if self._fill_char == "0" and self._align == "=":
-            spec.n_min_width = self._width - extra_length
-        if self._loc_thousands:
-            self._group_digits(spec, digits[to_number:])
-            n_grouped_digits = len(self._grouped_digits)
-        else:
-            n_grouped_digits = spec.n_digits
-        n_padding = self._width - (extra_length + n_grouped_digits)
-        if n_padding > 0:
-            align = self._align
-            if align == "<":
-                spec.n_rpadding = n_padding
-            elif align == ">":
-                spec.n_lpadding = n_padding
-            elif align == "^":
-                spec.n_lpadding = n_padding // 2
-                spec.n_rpadding = n_padding - spec.n_lpadding
-            elif align == "=":
-                spec.n_spadding = n_padding
-            else:
-                raise AssertionError("shouldn't reach")
-        spec.n_total = spec.n_lpadding + spec.n_sign + spec.n_prefix + \
-                       spec.n_spadding + n_grouped_digits + \
-                       spec.n_decimal + spec.n_remainder + spec.n_rpadding
-        return spec
-
-    def _fill_digits(self, buf, digits, d_state, n_chars, n_zeros,
-                     thousands_sep):
-        if thousands_sep:
-            for c in thousands_sep:
-                buf.append(c)
-        for i in range(d_state - 1, d_state - n_chars - 1, -1):
-            buf.append(digits[i])
-        for i in range(n_zeros):
-            buf.append("0")
-
-    def _group_digits(self, spec, digits):
-        buf = []
-        grouping = self._loc_grouping
-        min_width = spec.n_min_width
-        grouping_state = 0
-        count = 0
-        left = spec.n_digits
-        n_ts = len(self._loc_thousands)
-        need_separator = False
-        done = False
-        groupings = len(grouping)
-        previous = 0
-        while True:
-            group = ord(grouping[grouping_state])
-            if group > 0:
-                if group == 256:
-                    break
-                grouping_state += 1
-                previous = group
-            else:
-                group = previous
-            final_grouping = min(group, max(left, max(min_width, 1)))
-            n_zeros = max(0, final_grouping - left)
-            n_chars = max(0, min(left, final_grouping))
-            ts = self._loc_thousands if need_separator else None
-            self._fill_digits(buf, digits, left, n_chars, n_zeros, ts)
-            need_separator = True
-            left -= n_chars
-            min_width -= final_grouping
-            if left <= 0 and min_width <= 0:
-                done = True
-                break
-            min_width -= n_ts
-        if not done:
-            group = max(max(left, min_width), 1)
-            n_zeros = max(0, group - left)
-            n_chars = max(0, min(left, group))
-            ts = self._loc_thousands if need_separator else None
-            self._fill_digits(buf, digits, left, n_chars, n_zeros, ts)
-        buf.reverse()
-        self._grouped_digits = self.empty.join(buf)
-
-    def _upcase_string(self, s):
-        buf = []
-        for c in s:
-            index = ord(c)
-            if ord("a") <= index <= ord("z"):
-                c = chr(index - 32)
-            buf.append(c)
-        return self.empty.join(buf)
-
-
-    def _fill_number(self, spec, num, to_digits, to_prefix, fill_char,
-                     to_remainder, upper, grouped_digits=None):
-        out = self._builder()
-        if spec.n_lpadding:
-            out.append_multiple_char(fill_char[0], spec.n_lpadding)
-        if spec.n_sign:
-            if self.is_unicode:
-                sign = spec.sign.decode("ascii")
-            else:
-                sign = spec.sign
-            out.append(sign)
-        if spec.n_prefix:
-            pref = num[to_prefix:to_prefix + spec.n_prefix]
-            if upper:
-                pref = self._upcase_string(pref)
-            out.append(pref)
-        if spec.n_spadding:
-            out.append_multiple_char(fill_char[0], spec.n_spadding)
-        if spec.n_digits != 0:
-            if self._loc_thousands:
-                if grouped_digits is not None:
-                    digits = grouped_digits
-                else:
-                    digits = self._grouped_digits
-                    assert digits is not None
-            else:
-                stop = to_digits + spec.n_digits
-                assert stop >= 0
-                digits = num[to_digits:stop]
-            if upper:
-                digits = self._upcase_string(digits)
-            out.append(digits)
-        if spec.n_decimal:
-            out.append(self._lit(".")[0])
-        if spec.n_remainder:
-            out.append(num[to_remainder:])
-        if spec.n_rpadding:
-            out.append_multiple_char(fill_char[0], spec.n_rpadding)
-        #if complex, need to call twice - just retun the buffer
-        return out.build()
-
-    def _format_int_or_long(self, w_num, kind):
-        space = self.space
-        if self._precision != -1:
-            msg = "precision not allowed in integer type"
-            raise OperationError(space.w_ValueError, space.wrap(msg))
-        sign_char = "\0"
-        tp = self._type
-        if tp == "c":
-            if self._sign != "\0":
-                msg = "sign not allowed with 'c' presentation type"
-                raise OperationError(space.w_ValueError, space.wrap(msg))
-            value = space.int_w(w_num)
-            if self.is_unicode:
-                result = runicode.UNICHR(value)
-            else:
-                result = chr(value)
-            n_digits = 1
-            n_remainder = 1
-            to_remainder = 0
-            n_prefix = 0
-            to_prefix = 0
-            to_numeric = 0
-        else:
-            if tp == "b":
-                base = 2
-                skip_leading = 2
-            elif tp == "o":
-                base = 8
-                skip_leading = 2
-            elif tp == "x" or tp == "X":
-                base = 16
-                skip_leading = 2
-            elif tp == "n" or tp == "d":
-                base = 10
-                skip_leading = 0
-            else:
-                raise AssertionError("shouldn't reach")
-            if kind == INT_KIND:
-                result = self._int_to_base(base, space.int_w(w_num))
-            else:
-                result = self._long_to_base(base, space.bigint_w(w_num))
-            n_prefix = skip_leading if self._alternate else 0
-            to_prefix = 0
-            if result[0] == "-":
-                sign_char = "-"
-                skip_leading += 1
-                to_prefix += 1
-            n_digits = len(result) - skip_leading
-            n_remainder = 0
-            to_remainder = 0
-            to_numeric = skip_leading
-        self._get_locale(tp)
-        spec = self._calc_num_width(n_prefix, sign_char, to_numeric, n_digits,
-                                    n_remainder, False, result)
-        fill = self._lit(" ") if self._fill_char == "\0" else self._fill_char
-        upper = self._type == "X"
-        return self.space.wrap(self._fill_number(spec, result, to_numeric,
-                                 to_prefix, fill, to_remainder, upper))
-
-    def _long_to_base(self, base, value):
-        prefix = ""
-        if base == 2:
-            prefix = "0b"
-        elif base == 8:
-            prefix = "0o"
-        elif base == 16:
-            prefix = "0x"
-        as_str = value.format(LONG_DIGITS[:base], prefix)
-        if self.is_unicode:
-            return as_str.decode("ascii")
-        return as_str
-
-    def _int_to_base(self, base, value):
-        if base == 10:
-            s = str(value)
+        def _lit(self, s):
             if self.is_unicode:
                 return s.decode("ascii")
-            return s
-        # This part is slow.
-        negative = value < 0
-        value = abs(value)
-        buf = ["\0"] * (8 * 8 + 6) # Too much on 32 bit, but who cares?
-        i = len(buf) - 1
-        while True:
-            div = value // base
-            mod = value - div * base
-            digit = abs(mod)
-            digit += ord("0") if digit < 10 else ord("a") - 10
-            buf[i] = chr(digit)
-            value = div
+            else:
+                return s
+
+        def _pad(self, string):
+            builder = self._builder()
+            builder.append_multiple_char(self._fill_char, self._left_pad)
+            builder.append(string)
+            builder.append_multiple_char(self._fill_char, self._right_pad)
+            return builder.build()
+
+        def _builder(self):
+            if self.is_unicode:
+                return rstring.UnicodeBuilder()
+            else:
+                return rstring.StringBuilder()
+
+        def _unknown_presentation(self, tp):
+            msg = "unknown presentation for %s: '%s'"
+            w_msg = self.space.wrap(msg  % (tp, self._type))
+            raise OperationError(self.space.w_ValueError, w_msg)
+
+        def format_string(self, string):
+            space = self.space
+            if self._parse_spec("s", "<"):
+                return space.wrap(string)
+            if self._type != "s":
+                self._unknown_presentation("string")
+            if self._sign != "\0":
+                msg = "Sign not allowed in string format specifier"
+                raise OperationError(space.w_ValueError, space.wrap(msg))
+            if self._alternate:
+                msg = "Alternate form not allowed in string format specifier"
+                raise OperationError(space.w_ValueError, space.wrap(msg))
+            if self._align == "=":
+                msg = "'=' alignment not allowed in string format specifier"
+                raise OperationError(space.w_ValueError, space.wrap(msg))
+            length = len(string)
+            precision = self._precision
+            if precision != -1 and length >= precision:
+                assert precision >= 0
+                length = precision
+                string = string[:precision]
+            if self._fill_char == "\0":
+                self._fill_char = self._lit(" ")[0]
+            self._calc_padding(string, length)
+            return space.wrap(self._pad(string))
+
+        def _get_locale(self, tp):
+            space = self.space
+            if tp == "n":
+                dec, thousands, grouping = rlocale.numeric_formatting()
+            elif self._thousands_sep:
+                dec = "."
+                thousands = ","
+                grouping = "\3\0"
+            else:
+                dec = "."
+                thousands = ""
+                grouping = "\256"
+            if self.is_unicode:
+                self._loc_dec = dec.decode("ascii")
+                self._loc_thousands = thousands.decode("ascii")
+            else:
+                self._loc_dec = dec
+                self._loc_thousands = thousands
+            self._loc_grouping = grouping
+
+        def _calc_num_width(self, n_prefix, sign_char, to_number, n_number,
+                            n_remainder, has_dec, digits):
+            """Calculate widths of all parts of formatted number.
+
+            Output will look like:
+
+                <lpadding> <sign> <prefix> <spadding> <grouped_digits> <decimal>
+                <remainder> <rpadding>
+
+            sign is computed from self._sign, and the sign of the number
+            prefix is given
+            digits is known
+            """
+            spec = NumberSpec()
+            spec.n_digits = n_number - n_remainder - has_dec
+            spec.n_prefix = n_prefix
+            spec.n_lpadding = 0
+            spec.n_decimal = int(has_dec)
+            spec.n_remainder = n_remainder
+            spec.n_spadding = 0
+            spec.n_rpadding = 0
+            spec.n_min_width = 0
+            spec.n_total = 0
+            spec.sign = "\0"
+            spec.n_sign = 0
+            sign = self._sign
+            if sign == "+":
+                spec.n_sign = 1
+                spec.sign = "-" if sign_char == "-" else "+"
+            elif sign == " ":
+                spec.n_sign = 1
+                spec.sign = "-" if sign_char == "-" else " "
+            elif sign_char == "-":
+                spec.n_sign = 1
+                spec.sign = "-"
+            extra_length = (spec.n_sign + spec.n_prefix + spec.n_decimal +
+                            spec.n_remainder) # Not padding or digits
+            if self._fill_char == "0" and self._align == "=":
+                spec.n_min_width = self._width - extra_length
+            if self._loc_thousands:
+                self._group_digits(spec, digits[to_number:])
+                n_grouped_digits = len(self._grouped_digits)
+            else:
+                n_grouped_digits = spec.n_digits
+            n_padding = self._width - (extra_length + n_grouped_digits)
+            if n_padding > 0:
+                align = self._align
+                if align == "<":
+                    spec.n_rpadding = n_padding
+                elif align == ">":
+                    spec.n_lpadding = n_padding
+                elif align == "^":
+                    spec.n_lpadding = n_padding // 2
+                    spec.n_rpadding = n_padding - spec.n_lpadding
+                elif align == "=":
+                    spec.n_spadding = n_padding
+                else:
+                    raise AssertionError("shouldn't reach")
+            spec.n_total = spec.n_lpadding + spec.n_sign + spec.n_prefix + \
+                           spec.n_spadding + n_grouped_digits + \
+                           spec.n_decimal + spec.n_remainder + spec.n_rpadding
+            return spec
+
+        def _fill_digits(self, buf, digits, d_state, n_chars, n_zeros,
+                         thousands_sep):
+            if thousands_sep:
+                for c in thousands_sep:
+                    buf.append(c)
+            for i in range(d_state - 1, d_state - n_chars - 1, -1):
+                buf.append(digits[i])
+            for i in range(n_zeros):
+                buf.append("0")
+
+        def _group_digits(self, spec, digits):
+            buf = []
+            grouping = self._loc_grouping
+            min_width = spec.n_min_width
+            grouping_state = 0
+            count = 0
+            left = spec.n_digits
+            n_ts = len(self._loc_thousands)
+            need_separator = False
+            done = False
+            groupings = len(grouping)
+            previous = 0
+            while True:
+                group = ord(grouping[grouping_state])
+                if group > 0:
+                    if group == 256:
+                        break
+                    grouping_state += 1
+                    previous = group
+                else:
+                    group = previous
+                final_grouping = min(group, max(left, max(min_width, 1)))
+                n_zeros = max(0, final_grouping - left)
+                n_chars = max(0, min(left, final_grouping))
+                ts = self._loc_thousands if need_separator else None
+                self._fill_digits(buf, digits, left, n_chars, n_zeros, ts)
+                need_separator = True
+                left -= n_chars
+                min_width -= final_grouping
+                if left <= 0 and min_width <= 0:
+                    done = True
+                    break
+                min_width -= n_ts
+            if not done:
+                group = max(max(left, min_width), 1)
+                n_zeros = max(0, group - left)
+                n_chars = max(0, min(left, group))
+                ts = self._loc_thousands if need_separator else None
+                self._fill_digits(buf, digits, left, n_chars, n_zeros, ts)
+            buf.reverse()
+            self._grouped_digits = self.empty.join(buf)
+
+        def _upcase_string(self, s):
+            buf = []
+            for c in s:
+                index = ord(c)
+                if ord("a") <= index <= ord("z"):
+                    c = chr(index - 32)
+                buf.append(c)
+            return self.empty.join(buf)
+
+
+        def _fill_number(self, spec, num, to_digits, to_prefix, fill_char,
+                         to_remainder, upper, grouped_digits=None):
+            out = self._builder()
+            if spec.n_lpadding:
+                out.append_multiple_char(fill_char[0], spec.n_lpadding)
+            if spec.n_sign:
+                if self.is_unicode:
+                    sign = spec.sign.decode("ascii")
+                else:
+                    sign = spec.sign
+                out.append(sign)
+            if spec.n_prefix:
+                pref = num[to_prefix:to_prefix + spec.n_prefix]
+                if upper:
+                    pref = self._upcase_string(pref)
+                out.append(pref)
+            if spec.n_spadding:
+                out.append_multiple_char(fill_char[0], spec.n_spadding)
+            if spec.n_digits != 0:
+                if self._loc_thousands:
+                    if grouped_digits is not None:
+                        digits = grouped_digits
+                    else:
+                        digits = self._grouped_digits
+                        assert digits is not None
+                else:
+                    stop = to_digits + spec.n_digits
+                    assert stop >= 0
+                    digits = num[to_digits:stop]
+                if upper:
+                    digits = self._upcase_string(digits)
+                out.append(digits)
+            if spec.n_decimal:
+                out.append(self._lit(".")[0])
+            if spec.n_remainder:
+                out.append(num[to_remainder:])
+            if spec.n_rpadding:
+                out.append_multiple_char(fill_char[0], spec.n_rpadding)
+            #if complex, need to call twice - just retun the buffer
+            return out.build()
+
+        def _format_int_or_long(self, w_num, kind):
+            space = self.space
+            if self._precision != -1:
+                msg = "precision not allowed in integer type"
+                raise OperationError(space.w_ValueError, space.wrap(msg))
+            sign_char = "\0"
+            tp = self._type
+            if tp == "c":
+                if self._sign != "\0":
+                    msg = "sign not allowed with 'c' presentation type"
+                    raise OperationError(space.w_ValueError, space.wrap(msg))
+                value = space.int_w(w_num)
+                if self.is_unicode:
+                    result = runicode.UNICHR(value)
+                else:
+                    result = chr(value)
+                n_digits = 1
+                n_remainder = 1
+                to_remainder = 0
+                n_prefix = 0
+                to_prefix = 0
+                to_numeric = 0
+            else:
+                if tp == "b":
+                    base = 2
+                    skip_leading = 2
+                elif tp == "o":
+                    base = 8
+                    skip_leading = 2
+                elif tp == "x" or tp == "X":
+                    base = 16
+                    skip_leading = 2
+                elif tp == "n" or tp == "d":
+                    base = 10
+                    skip_leading = 0
+                else:
+                    raise AssertionError("shouldn't reach")
+                if kind == INT_KIND:
+                    result = self._int_to_base(base, space.int_w(w_num))
+                else:
+                    result = self._long_to_base(base, space.bigint_w(w_num))
+                n_prefix = skip_leading if self._alternate else 0
+                to_prefix = 0
+                if result[0] == "-":
+                    sign_char = "-"
+                    skip_leading += 1
+                    to_prefix += 1
+                n_digits = len(result) - skip_leading
+                n_remainder = 0
+                to_remainder = 0
+                to_numeric = skip_leading
+            self._get_locale(tp)
+            spec = self._calc_num_width(n_prefix, sign_char, to_numeric, n_digits,
+                                        n_remainder, False, result)
+            fill = self._lit(" ") if self._fill_char == "\0" else self._fill_char
+            upper = self._type == "X"
+            return self.space.wrap(self._fill_number(spec, result, to_numeric,
+                                     to_prefix, fill, to_remainder, upper))
+
+        def _long_to_base(self, base, value):
+            prefix = ""
+            if base == 2:
+                prefix = "0b"
+            elif base == 8:
+                prefix = "0o"
+            elif base == 16:
+                prefix = "0x"
+            as_str = value.format(LONG_DIGITS[:base], prefix)
+            if self.is_unicode:
+                return as_str.decode("ascii")
+            return as_str
+
+        def _int_to_base(self, base, value):
+            if base == 10:
+                s = str(value)
+                if self.is_unicode:
+                    return s.decode("ascii")
+                return s
+            # This part is slow.
+            negative = value < 0
+            value = abs(value)
+            buf = ["\0"] * (8 * 8 + 6) # Too much on 32 bit, but who cares?
+            i = len(buf) - 1
+            while True:
+                div = value // base
+                mod = value - div * base
+                digit = abs(mod)
+                digit += ord("0") if digit < 10 else ord("a") - 10
+                buf[i] = chr(digit)
+                value = div
+                i -= 1
+                if not value:
+                    break
+            if base == 2:
+                buf[i] = "b"
+                buf[i - 1] = "0"
+            elif base == 8:
+                buf[i] = "o"
+                buf[i - 1] = "0"
+            elif base == 16:
+                buf[i] = "x"
+                buf[i - 1] = "0"
+            else:
+                buf[i] = "#"
+                buf[i - 1] = chr(ord("0") + base % 10)
+                if base > 10:
+                    buf[i - 2] = chr(ord("0") + base // 10)
+                    i -= 1
             i -= 1
-            if not value:
-                break
-        if base == 2:
-            buf[i] = "b"
-            buf[i - 1] = "0"
-        elif base == 8:
-            buf[i] = "o"
-            buf[i - 1] = "0"
-        elif base == 16:
-            buf[i] = "x"
-            buf[i - 1] = "0"
-        else:
-            buf[i] = "#"
-            buf[i - 1] = chr(ord("0") + base % 10)
-            if base > 10:
-                buf[i - 2] = chr(ord("0") + base // 10)
+            if negative:
                 i -= 1
-        i -= 1
-        if negative:
-            i -= 1
-            buf[i] = "-"
-        assert i >= 0
-        return self.empty.join(buf[i:])
+                buf[i] = "-"
+            assert i >= 0
+            return self.empty.join(buf[i:])
 
-    def format_int_or_long(self, w_num, kind):
-        space = self.space
-        if self._parse_spec("d", ">"):
+        def format_int_or_long(self, w_num, kind):
+            space = self.space
+            if self._parse_spec("d", ">"):
+                if self.is_unicode:
+                    return space.call_function(space.w_unicode, w_num)
+                return self.space.str(w_num)
+            tp = self._type
+            if (tp == "b" or
+                tp == "c" or
+                tp == "d" or
+                tp == "o" or
+                tp == "x" or
+                tp == "X" or
+                tp == "n"):
+                return self._format_int_or_long(w_num, kind)
+            elif (tp == "e" or
+                  tp == "E" or
+                  tp == "f" or
+                  tp == "F" or
+                  tp == "g" or
+                  tp == "G" or
+                  tp == "%"):
+                w_float = space.float(w_num)
+                return self._format_float(w_float)
+            else:
+                self._unknown_presentation("int" if kind == INT_KIND else "long")
+
+        def _parse_number(self, s, i):
+            """Determine if s has a decimal point, and the index of the first #
+            after the decimal, or the end of the number."""
+            length = len(s)
+            while i < length and "0" <= s[i] <= "9":
+                i += 1
+            rest = i
+            dec_point = i < length and s[i] == "."
+            if dec_point:
+                rest += 1
+            #differs from CPython method - CPython sets n_remainder
+            return dec_point, rest
+
+        def _format_float(self, w_float):
+            """helper for format_float"""
+            space = self.space
+            flags = 0
+            default_precision = 6
+            if self._alternate:
+                msg = "alternate form not allowed in float formats"
+                raise OperationError(space.w_ValueError, space.wrap(msg))
+            tp = self._type
+            self._get_locale(tp)
+            if tp == "\0":
+                tp = "g"
+                default_precision = 12
+                flags |= rfloat.DTSF_ADD_DOT_0
+            elif tp == "n":
+                tp = "g"
+            value = space.float_w(w_float)
+            if tp == "%":
+                tp = "f"
+                value *= 100
+                add_pct = True
+            else:
+                add_pct = False
+            if self._precision == -1:
+                self._precision = default_precision
+            result, special = rfloat.double_to_string(value, tp,
+                                                      self._precision, flags)
+            if add_pct:
+                result += "%"
+            n_digits = len(result)
+            if result[0] == "-":
+                sign = "-"
+                to_number = 1
+                n_digits -= 1
+            else:
+                sign = "\0"
+                to_number = 0
+            have_dec_point, to_remainder = self._parse_number(result, to_number)
+            n_remainder = len(result) - to_remainder
             if self.is_unicode:
-                return space.call_function(space.w_unicode, w_num)
-            return self.space.str(w_num)
-        tp = self._type
-        if (tp == "b" or
-            tp == "c" or
-            tp == "d" or
-            tp == "o" or
-            tp == "x" or
-            tp == "X" or
-            tp == "n"):
-            return self._format_int_or_long(w_num, kind)
-        elif (tp == "e" or
-              tp == "E" or
-              tp == "f" or
-              tp == "F" or
-              tp == "g" or
-              tp == "G" or
-              tp == "%"):
-            w_float = space.float(w_num)
-            return self._format_float(w_float)
-        else:
-            self._unknown_presentation("int" if kind == INT_KIND else "long")
+                digits = result.decode("ascii")
+            else:
+                digits = result
+            spec = self._calc_num_width(0, sign, to_number, n_digits,
+                                        n_remainder, have_dec_point, digits)
+            fill = self._lit(" ") if self._fill_char == "\0" else self._fill_char
+            return self.space.wrap(self._fill_number(spec, digits, to_number, 0,
+                                      fill, to_remainder, False))
 
-    def _parse_number(self, s, i):
-        """Determine if s has a decimal point, and the index of the first #
-        after the decimal, or the end of the number."""
-        length = len(s)
-        while i < length and "0" <= s[i] <= "9":
-            i += 1
-        rest = i
-        dec_point = i < length and s[i] == "."
-        if dec_point:
-            rest += 1
-        #differs from CPython method - CPython sets n_remainder
-        return dec_point, rest
+        def format_float(self, w_float):
+            space = self.space
+            if self._parse_spec("\0", ">"):
+                if self.is_unicode:
+                    return space.call_function(space.w_unicode, w_float)
+                return space.str(w_float)
+            tp = self._type
+            if (tp == "\0" or
+                tp == "e" or
+                tp == "E" or
+                tp == "f" or
+                tp == "F" or
+                tp == "g" or
+                tp == "G" or
+                tp == "n" or
+                tp == "%"):
+                return self._format_float(w_float)
+            self._unknown_presentation("float")
 
-    def _format_float(self, w_float):
-        """helper for format_float"""
-        space = self.space
-        flags = 0
-        default_precision = 6
-        if self._alternate:
-            msg = "alternate form not allowed in float formats"
-            raise OperationError(space.w_ValueError, space.wrap(msg))
-        tp = self._type
-        self._get_locale(tp)
-        if tp == "\0":
-            tp = "g"
-            default_precision = 12
-            flags |= rfloat.DTSF_ADD_DOT_0
-        elif tp == "n":
-            tp = "g"
-        value = space.float_w(w_float)
-        if tp == "%":
-            tp = "f"
-            value *= 100
-            add_pct = True
-        else:
-            add_pct = False
-        if self._precision == -1:
-            self._precision = default_precision
-        result, special = rfloat.double_to_string(value, tp,
-                                                  self._precision, flags)
-        if add_pct:
-            result += "%"
-        n_digits = len(result)
-        if result[0] == "-":
-            sign = "-"
-            to_number = 1
-            n_digits -= 1
-        else:
-            sign = "\0"
-            to_number = 0
-        have_dec_point, to_remainder = self._parse_number(result, to_number)
-        n_remainder = len(result) - to_remainder
-        if self.is_unicode:
-            digits = result.decode("ascii")
-        else:
-            digits = result
-        spec = self._calc_num_width(0, sign, to_number, n_digits,
-                                    n_remainder, have_dec_point, digits)
-        fill = self._lit(" ") if self._fill_char == "\0" else self._fill_char
-        return self.space.wrap(self._fill_number(spec, digits, to_number, 0,
-                                  fill, to_remainder, False))
+        def _format_complex(self, w_complex):
+            space = self.space
+            tp = self._type
+            self._get_locale(tp)
+            default_precision = 6
+            if self._align == "=":
+                # '=' alignment is invalid
+                msg = ("'=' alignment flag is not allowed in"
+                       " complex format specifier")
+                raise OperationError(space.w_ValueError, space.wrap(msg))
+            if self._fill_char == "0":
+                #zero padding is invalid
+                msg = "Zero padding is not allowed in complex format specifier"
+                raise OperationError(space.w_ValueError, space.wrap(msg))
+            if self._alternate:
+                #alternate is invalid
+                msg = "Alternate form %s not allowed in complex format specifier"
+                raise OperationError(space.w_ValueError,
+                                     space.wrap(msg % (self._alternate)))
+            skip_re = 0
+            add_parens = 0
+            if tp == "\0":
+                #should mirror str() output
+                tp = "g"
+                default_precision = 12
+                #test if real part is non-zero
+                if (w_complex.realval == 0 and
+                    copysign(1., w_complex.realval) == 1.):
+                    skip_re = 1
+                else:
+                    add_parens = 1
 
-    def format_float(self, w_float):
-        space = self.space
-        if self._parse_spec("\0", ">"):
+            if tp == "n":
+                #same as 'g' except for locale, taken care of later
+                tp = "g"
+
+            #check if precision not set
+            if self._precision == -1:
+                self._precision = default_precision
+
+            #might want to switch to double_to_string from formatd
+            #in CPython it's named 're' - clashes with re module
+            re_num = formatd(w_complex.realval, tp, self._precision)
+            im_num = formatd(w_complex.imagval, tp, self._precision)
+            n_re_digits = len(re_num)
+            n_im_digits = len(im_num)
+
+            to_real_number = 0
+            to_imag_number = 0
+            re_sign = im_sign = ''
+            #if a sign character is in the output, remember it and skip
+            if re_num[0] == "-":
+                re_sign = "-"
+                to_real_number = 1
+                n_re_digits -= 1
+            if im_num[0] == "-":
+                im_sign = "-"
+                to_imag_number = 1
+                n_im_digits -= 1
+
+            #turn off padding - do it after number composition
+            #calc_num_width uses self._width, so assign to temporary variable,
+            #calculate width of real and imag parts, then reassign padding, align
+            tmp_fill_char = self._fill_char
+            tmp_align = self._align
+            tmp_width = self._width
+            self._fill_char = "\0"
+            self._align = "<"
+            self._width = -1
+
+            #determine if we have remainder, might include dec or exponent or both
+            re_have_dec, re_remainder_ptr = self._parse_number(re_num,
+                                                               to_real_number)
+            im_have_dec, im_remainder_ptr = self._parse_number(im_num,
+                                                               to_imag_number)
+
             if self.is_unicode:
-                return space.call_function(space.w_unicode, w_float)
-            return space.str(w_float)
-        tp = self._type
-        if (tp == "\0" or
-            tp == "e" or
-            tp == "E" or
-            tp == "f" or
-            tp == "F" or
-            tp == "g" or
-            tp == "G" or
-            tp == "n" or
-            tp == "%"):
-            return self._format_float(w_float)
-        self._unknown_presentation("float")
+                re_num = re_num.decode("ascii")
+                im_num = im_num.decode("ascii")
 
-    def _format_complex(self, w_complex):
-        space = self.space
-        tp = self._type
-        self._get_locale(tp)
-        default_precision = 6
-        if self._align == "=":
-            # '=' alignment is invalid
-            msg = ("'=' alignment flag is not allowed in"
-                   " complex format specifier")
-            raise OperationError(space.w_ValueError, space.wrap(msg))
-        if self._fill_char == "0":
-            #zero padding is invalid
-            msg = "Zero padding is not allowed in complex format specifier"
-            raise OperationError(space.w_ValueError, space.wrap(msg))
-        if self._alternate:
-            #alternate is invalid
-            msg = "Alternate form %s not allowed in complex format specifier"
-            raise OperationError(space.w_ValueError,
-                                 space.wrap(msg % (self._alternate)))
-        skip_re = 0
-        add_parens = 0
-        if tp == "\0":
-            #should mirror str() output
-            tp = "g"
-            default_precision = 12
-            #test if real part is non-zero
-            if (w_complex.realval == 0 and
-                copysign(1., w_complex.realval) == 1.):
-                skip_re = 1
-            else:
-                add_parens = 1
+            #set remainder, in CPython _parse_number sets this
+            #using n_re_digits causes tests to fail
+            re_n_remainder = len(re_num) - re_remainder_ptr
+            im_n_remainder = len(im_num) - im_remainder_ptr
+            re_spec = self._calc_num_width(0, re_sign, to_real_number, n_re_digits,
+                                           re_n_remainder, re_have_dec,
+                                           re_num)
 
-        if tp == "n":
-            #same as 'g' except for locale, taken care of later
-            tp = "g"
+            #capture grouped digits b/c _fill_number reads from self._grouped_digits
+            #self._grouped_digits will get overwritten in imaginary calc_num_width
+            re_grouped_digits = self._grouped_digits
+            if not skip_re:
+                self._sign = "+"
+            im_spec = self._calc_num_width(0, im_sign, to_imag_number, n_im_digits,
+                                           im_n_remainder, im_have_dec,
+                                           im_num)
 
-        #check if precision not set
-        if self._precision == -1:
-            self._precision = default_precision
+            im_grouped_digits = self._grouped_digits
+            if skip_re:
+                re_spec.n_total = 0
 
-        #might want to switch to double_to_string from formatd
-        #in CPython it's named 're' - clashes with re module
-        re_num = formatd(w_complex.realval, tp, self._precision)
-        im_num = formatd(w_complex.imagval, tp, self._precision)
-        n_re_digits = len(re_num)
-        n_im_digits = len(im_num)
+            #reassign width, alignment, fill character
+            self._align = tmp_align
+            self._width = tmp_width
+            self._fill_char = tmp_fill_char
 
-        to_real_number = 0
-        to_imag_number = 0
-        re_sign = im_sign = ''
-        #if a sign character is in the output, remember it and skip
-        if re_num[0] == "-":
-            re_sign = "-"
-            to_real_number = 1
-            n_re_digits -= 1
-        if im_num[0] == "-":
-            im_sign = "-"
-            to_imag_number = 1
-            n_im_digits -= 1
+            #compute L and R padding - stored in self._left_pad and self._right_pad
+            self._calc_padding(self.empty, re_spec.n_total + im_spec.n_total + 1 +
+                                           add_parens * 2)
 
-        #turn off padding - do it after number composition
-        #calc_num_width uses self._width, so assign to temporary variable,
-        #calculate width of real and imag parts, then reassign padding, align
-        tmp_fill_char = self._fill_char
-        tmp_align = self._align
-        tmp_width = self._width
-        self._fill_char = "\0"
-        self._align = "<"
-        self._width = -1
+            out = self._builder()
+            fill = self._fill_char
+            if fill == "\0":
+                fill = self._lit(" ")[0]
 
-        #determine if we have remainder, might include dec or exponent or both
-        re_have_dec, re_remainder_ptr = self._parse_number(re_num,
-                                                           to_real_number)
-        im_have_dec, im_remainder_ptr = self._parse_number(im_num,
-                                                           to_imag_number)
+            #compose the string
+            #add left padding
+            out.append_multiple_char(fill, self._left_pad)
+            if add_parens:
+                out.append(self._lit('(')[0])
 
-        if self.is_unicode:
-            re_num = re_num.decode("ascii")
-            im_num = im_num.decode("ascii")
+            #if the no. has a real component, add it
+            if not skip_re:
+                out.append(self._fill_number(re_spec, re_num, to_real_number, 0,
+                                             fill, re_remainder_ptr, False,
+                                             re_grouped_digits))
 
-        #set remainder, in CPython _parse_number sets this
-        #using n_re_digits causes tests to fail
-        re_n_remainder = len(re_num) - re_remainder_ptr
-        im_n_remainder = len(im_num) - im_remainder_ptr
-        re_spec = self._calc_num_width(0, re_sign, to_real_number, n_re_digits,
-                                       re_n_remainder, re_have_dec,
-                                       re_num)
+            #add imaginary component
+            out.append(self._fill_number(im_spec, im_num, to_imag_number, 0,
+                                         fill, im_remainder_ptr, False,
+                                         im_grouped_digits))
 
-        #capture grouped digits b/c _fill_number reads from self._grouped_digits
-        #self._grouped_digits will get overwritten in imaginary calc_num_width
-        re_grouped_digits = self._grouped_digits
-        if not skip_re:
-            self._sign = "+"
-        im_spec = self._calc_num_width(0, im_sign, to_imag_number, n_im_digits,
-                                       im_n_remainder, im_have_dec,
-                                       im_num)
+            #add 'j' character
+            out.append(self._lit('j')[0])
 
-        im_grouped_digits = self._grouped_digits
-        if skip_re:
-            re_spec.n_total = 0
+            if add_parens:
+                out.append(self._lit(')')[0])
 
-        #reassign width, alignment, fill character
-        self._align = tmp_align
-        self._width = tmp_width
-        self._fill_char = tmp_fill_char
+            #add right padding
+            out.append_multiple_char(fill, self._right_pad)
 
-        #compute L and R padding - stored in self._left_pad and self._right_pad
-        self._calc_padding(self.empty, re_spec.n_total + im_spec.n_total + 1 +
-                                       add_parens * 2)
+            return self.space.wrap(out.build())
 
-        out = self._builder()
-        fill = self._fill_char
-        if fill == "\0":
-            fill = self._lit(" ")[0]
 
-        #compose the string
-        #add left padding
-        out.append_multiple_char(fill, self._left_pad)
-        if add_parens:
-            out.append(self._lit('(')[0])
+        def format_complex(self, w_complex):
+            """return the string representation of a complex number"""
+            space = self.space
+            #parse format specification, set associated variables
+            if self._parse_spec("\0", ">"):
+                return space.str(w_complex)
+            tp = self._type
+            if (tp == "\0" or
+                tp == "e" or
+                tp == "E" or
+                tp == "f" or
+                tp == "F" or
+                tp == "g" or
+                tp == "G" or
+                tp == "n"):
+                return self._format_complex(w_complex)
+            self._unknown_presentation("complex")
+    return Formatter
 
-        #if the no. has a real component, add it
-        if not skip_re:
-            out.append(self._fill_number(re_spec, re_num, to_real_number, 0,
-                                         fill, re_remainder_ptr, False,
-                                         re_grouped_digits))
-
-        #add imaginary component
-        out.append(self._fill_number(im_spec, im_num, to_imag_number, 0,
-                                     fill, im_remainder_ptr, False,
-                                     im_grouped_digits))
-
-        #add 'j' character
-        out.append(self._lit('j')[0])
-
-        if add_parens:
-            out.append(self._lit(')')[0])
-
-        #add right padding
-        out.append_multiple_char(fill, self._right_pad)
-
-        return self.space.wrap(out.build())
-
-
-    def format_complex(self, w_complex):
-        """return the string representation of a complex number"""
-        space = self.space
-        #parse format specification, set associated variables
-        if self._parse_spec("\0", ">"):
-            return space.str(w_complex)
-        tp = self._type
-        if (tp == "\0" or
-            tp == "e" or
-            tp == "E" or
-            tp == "f" or
-            tp == "F" or
-            tp == "g" or
-            tp == "G" or
-            tp == "n"):
-            return self._format_complex(w_complex)
-        self._unknown_presentation("complex")
+StrFormatter = make_formatting_class()
+UnicodeFormatter = make_formatting_class()
 
 
 def unicode_formatter(space, spec):
-    return Formatter(space, True, spec)
-
+    return StrFormatter(space, True, spec)
 
 def str_formatter(space, spec):
-    return Formatter(space, False, spec)
+    return UnicodeFormatter(space, False, spec)
 
 
 @specialize.arg(2)
diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py
--- a/pypy/objspace/std/objspace.py
+++ b/pypy/objspace/std/objspace.py
@@ -571,5 +571,8 @@
 
     def _type_isinstance(self, w_inst, w_type):
         if isinstance(w_type, W_TypeObject):
-            return self.wrap(self.type(w_inst).issubtype(w_type))
+            return self.type(w_inst).issubtype(w_type)
         raise OperationError(self.w_TypeError, self.wrap("need type object"))
+
+    def isinstance_w(space, w_inst, w_type):
+        return space._type_isinstance(w_inst, w_type)
diff --git a/pypy/objspace/std/ropeobject.py b/pypy/objspace/std/ropeobject.py
--- a/pypy/objspace/std/ropeobject.py
+++ b/pypy/objspace/std/ropeobject.py
@@ -292,8 +292,8 @@
     l = []
     for i in range(size):
         w_s = list_w[i]
-        if not space.is_true(space.isinstance(w_s, space.w_str)):
-            if space.is_true(space.isinstance(w_s, space.w_unicode)):
+        if not space.isinstance_w(w_s, space.w_str):
+            if space.isinstance_w(w_s, space.w_unicode):
                 w_u = space.call_function(space.w_unicode, w_self)
                 return space.call_method(w_u, "join", space.newlist(list_w))
             raise operationerrfmt(
@@ -556,7 +556,7 @@
                                                 W_RopeObject.EMPTY, w_start,
                                                 w_end, True)
     for w_suffix in space.fixedview(w_suffixes):
-        if space.is_true(space.isinstance(w_suffix, space.w_unicode)):
+        if space.isinstance_w(w_suffix, space.w_unicode):
             w_u = space.call_function(space.w_unicode, w_self)
             return space.call_method(w_u, "endswith", w_suffixes, w_start,
                                      w_end)
@@ -576,7 +576,7 @@
     (self, _, start, end) = _convert_idx_params(space, w_self, W_RopeObject.EMPTY,
                                                   w_start, w_end, True)
     for w_prefix in space.fixedview(w_prefixes):
-        if space.is_true(space.isinstance(w_prefix, space.w_unicode)):
+        if space.isinstance_w(w_prefix, space.w_unicode):
             w_u = space.call_function(space.w_unicode, w_self)
             return space.call_method(w_u, "startswith", w_prefixes, w_start,
                                      w_end)
diff --git a/pypy/objspace/std/ropeunicodeobject.py b/pypy/objspace/std/ropeunicodeobject.py
--- a/pypy/objspace/std/ropeunicodeobject.py
+++ b/pypy/objspace/std/ropeunicodeobject.py
@@ -29,7 +29,7 @@
     assert isinstance(w_str, W_RopeObject)
     encoding = getdefaultencoding(space)
     w_retval = decode_string(space, w_str, encoding, "strict")
-    if not space.is_true(space.isinstance(w_retval, space.w_unicode)):
+    if not space.isinstance_w(w_retval, space.w_unicode):
         raise operationerrfmt(
             space.w_TypeError,
             "decoder did not return an unicode object (type '%s')",
@@ -254,7 +254,7 @@
         if isinstance(w_item, W_RopeUnicodeObject):
             # shortcut for performane
             item = w_item._node
-        elif space.is_true(space.isinstance(w_item, space.w_str)):
+        elif space.isinstance_w(w_item, space.w_str):
             item = unicode_from_string(space, w_item)._node
         else:
             msg = 'sequence item %d: expected string or Unicode'
@@ -828,14 +828,14 @@
         else:
             if space.is_w(w_newval, space.w_None):
                 continue
-            elif space.is_true(space.isinstance(w_newval, space.w_int)):
+            elif space.isinstance_w(w_newval, space.w_int):
                 newval = space.int_w(w_newval)
                 if newval < 0 or newval > maxunicode:
                     raise OperationError(
                             space.w_TypeError,
                             space.wrap("character mapping must be in range(0x%x)" % (maxunicode + 1,)))
                 result.append(rope.rope_from_unichar(unichr(newval)))
-            elif space.is_true(space.isinstance(w_newval, space.w_unicode)):
+            elif space.isinstance_w(w_newval, space.w_unicode):
                 result.append(ropeunicode_w(space, w_newval))
             else:
                 raise OperationError(
diff --git a/pypy/objspace/std/setobject.py b/pypy/objspace/std/setobject.py
--- a/pypy/objspace/std/setobject.py
+++ b/pypy/objspace/std/setobject.py
@@ -132,7 +132,7 @@
         w_obj.setdata = make_setdata_from_w_iterable(space, w_iterable)
 
 def _convert_set_to_frozenset(space, w_obj):
-    if space.is_true(space.isinstance(w_obj, space.w_set)):
+    if space.isinstance_w(w_obj, space.w_set):
         return W_FrozensetObject(space,
                                  make_setdata_from_w_iterable(space, w_obj))
     else:
diff --git a/pypy/objspace/std/stringobject.py b/pypy/objspace/std/stringobject.py
--- a/pypy/objspace/std/stringobject.py
+++ b/pypy/objspace/std/stringobject.py
@@ -364,8 +364,8 @@
     reslen = len(self) * (size - 1)
     for i in range(size):
         w_s = list_w[i]
-        if not space.is_true(space.isinstance(w_s, space.w_str)):
-            if space.is_true(space.isinstance(w_s, space.w_unicode)):
+        if not space.isinstance_w(w_s, space.w_str):
+            if space.isinstance_w(w_s, space.w_unicode):
                 # we need to rebuild w_list here, because the original
                 # w_list might be an iterable which we already consumed
                 w_list = space.newlist(list_w)
@@ -646,7 +646,7 @@
                                                   space.wrap(''), w_start,
                                                   w_end, True)
     for w_suffix in space.fixedview(w_suffixes):
-        if space.is_true(space.isinstance(w_suffix, space.w_unicode)):
+        if space.isinstance_w(w_suffix, space.w_unicode):
             w_u = space.call_function(space.w_unicode, w_self)
             return space.call_method(w_u, "endswith", w_suffixes, w_start,
                                      w_end)
@@ -665,7 +665,7 @@
     (u_self, _, start, end) = _convert_idx_params(space, w_self, space.wrap(''),
                                                   w_start, w_end, True)
     for w_prefix in space.fixedview(w_prefixes):
-        if space.is_true(space.isinstance(w_prefix, space.w_unicode)):
+        if space.isinstance_w(w_prefix, space.w_unicode):
             w_u = space.call_function(space.w_unicode, w_self)
             return space.call_method(w_u, "startswith", w_prefixes, w_start,
                                      w_end)
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -508,15 +508,15 @@
             # type name.  That's a hack, so we're allowed to use a different
             # hack...
             if ('__module__' in w_self.dict_w and
-                space.is_true(space.isinstance(w_self.getdictvalue(space, '__module__'),
-                                               space.w_str))):
+                space.isinstance_w(w_self.getdictvalue(space, '__module__'),
+                                               space.w_str)):
                 return w_self.getdictvalue(space, '__module__')
             return space.wrap('__builtin__')
 
     def get_module_type_name(w_self):
         space = w_self.space
         w_mod = w_self.get_module()
-        if not space.is_true(space.isinstance(w_mod, space.w_str)):
+        if not space.isinstance_w(w_mod, space.w_str):
             mod = '__builtin__'
         else:
             mod = space.str_w(w_mod)
@@ -850,7 +850,7 @@
             not space.is_w(w_newtype, space.w_type)):
             w_type.w_bltin_new = w_newfunc
         w_newobject = space.call_obj_args(w_newfunc, w_type, __args__)
-        call_init = space.is_true(space.isinstance(w_newobject, w_type))
+        call_init = space.isinstance_w(w_newobject, w_type)
 
     # maybe invoke the __init__ of the type
     if call_init:
@@ -876,7 +876,7 @@
 
 def repr__Type(space, w_obj):
     w_mod = w_obj.get_module()
-    if not space.is_true(space.isinstance(w_mod, space.w_str)):
+    if not space.isinstance_w(w_mod, space.w_str):
         mod = None
     else:
         mod = space.str_w(w_mod)
diff --git a/pypy/objspace/std/typetype.py b/pypy/objspace/std/typetype.py
--- a/pypy/objspace/std/typetype.py
+++ b/pypy/objspace/std/typetype.py
@@ -110,7 +110,7 @@
     if not w_type.is_heaptype():
         raise operationerrfmt(space.w_TypeError,
                               "can't set %s.__bases__", w_type.name)
-    if not space.is_true(space.isinstance(w_value, space.w_tuple)):
+    if not space.isinstance_w(w_value, space.w_tuple):
         raise operationerrfmt(space.w_TypeError,
                               "can only assign tuple to %s.__bases__, not %s",
                               w_type.name,
diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py
--- a/pypy/objspace/std/unicodeobject.py
+++ b/pypy/objspace/std/unicodeobject.py
@@ -900,14 +900,14 @@
         else:
             if space.is_w(w_newval, space.w_None):
                 continue
-            elif space.is_true(space.isinstance(w_newval, space.w_int)):
+            elif space.isinstance_w(w_newval, space.w_int):
                 newval = space.int_w(w_newval)
                 if newval < 0 or newval > maxunicode:
                     raise OperationError(
                             space.w_TypeError,
                             space.wrap("character mapping must be in range(0x%x)" % (maxunicode + 1,)))
                 result.append(unichr(newval))
-            elif space.is_true(space.isinstance(w_newval, space.w_unicode)):
+            elif space.isinstance_w(w_newval, space.w_unicode):
                 result.append(space.unicode_w(w_newval))
             else:
                 raise OperationError(
diff --git a/pypy/objspace/std/unicodetype.py b/pypy/objspace/std/unicodetype.py
--- a/pypy/objspace/std/unicodetype.py
+++ b/pypy/objspace/std/unicodetype.py
@@ -251,7 +251,7 @@
         w_errors = space.wrap(errors)
     w_restuple = space.call_function(w_encoder, w_object, w_errors)
     w_retval = space.getitem(w_restuple, space.wrap(0))
-    if not space.is_true(space.isinstance(w_retval, space.w_str)):
+    if not space.isinstance_w(w_retval, space.w_str):
         raise operationerrfmt(space.w_TypeError,
             "encoder did not return an string object (type '%s')",
             space.type(w_retval).getname(space))
@@ -286,7 +286,7 @@
 
 def unicode_from_encoded_object(space, w_obj, encoding, errors):
     w_retval = decode_object(space, w_obj, encoding, errors)
-    if not space.is_true(space.isinstance(w_retval, space.w_unicode)):
+    if not space.isinstance_w(w_retval, space.w_unicode):
         raise operationerrfmt(space.w_TypeError,
             "decoder did not return an unicode object (type '%s')",
             space.type(w_retval).getname(space))
@@ -309,7 +309,7 @@
             w_res = space.get_and_call_function(w_unicode_method, w_obj)
         else:
             w_res = space.str(w_obj)
-        if space.is_true(space.isinstance(w_res, space.w_unicode)):
+        if space.isinstance_w(w_res, space.w_unicode):
             return w_res
     return unicode_from_encoded_object(space, w_res, None, "strict")
 
@@ -346,7 +346,7 @@
     # convoluted logic for the case when unicode subclass has a __unicode__
     # method, we need to call this method
     if (space.is_w(space.type(w_obj), space.w_unicode) or
-        (space.is_true(space.isinstance(w_obj, space.w_unicode)) and
+        (space.isinstance_w(w_obj, space.w_unicode) and
          space.findattr(w_obj, space.wrap('__unicode__')) is None)):
         if encoding is not None or errors is not None:
             raise OperationError(space.w_TypeError,
diff --git a/pypy/rlib/_jit_vref.py b/pypy/rlib/_jit_vref.py
--- a/pypy/rlib/_jit_vref.py
+++ b/pypy/rlib/_jit_vref.py
@@ -46,6 +46,7 @@
     def specialize_call(self, hop):
         r_generic_object = getinstancerepr(hop.rtyper, None)
         [v] = hop.inputargs(r_generic_object)   # might generate a cast_pointer
+        hop.exception_cannot_occur()
         return v
 
     def rtype_simple_call(self, hop):
diff --git a/pypy/rlib/_rweakkeydict.py b/pypy/rlib/_rweakkeydict.py
--- a/pypy/rlib/_rweakkeydict.py
+++ b/pypy/rlib/_rweakkeydict.py
@@ -117,7 +117,7 @@
     d = lltype.malloc(WEAKDICT)
     d.entries = WEAKDICT.entries.TO.allocate(rdict.DICT_INITSIZE)
     d.num_items = 0
-    d.num_pristine_entries = rdict.DICT_INITSIZE
+    d.resize_counter = rdict.DICT_INITSIZE * 2
     return d
 
 @jit.dont_look_inside
@@ -152,8 +152,8 @@
     #                 ll_debugrepr(llkey),
     #                 ll_debugrepr(llvalue))
     if not everused:
-        d.num_pristine_entries -= 1
-        if d.num_pristine_entries * 3 <= len(d.entries):
+        d.resize_counter -= 3
+        if d.resize_counter <= 0:
             #llop.debug_print(lltype.Void, 'RESIZE')
             ll_weakdict_resize(d)
 
@@ -206,6 +206,6 @@
 
 WEAKDICT = lltype.GcStruct("weakkeydict",
                            ("num_items", lltype.Signed),
-                           ("num_pristine_entries", lltype.Signed),
+                           ("resize_counter", lltype.Signed),
                            ("entries", lltype.Ptr(WEAKDICTENTRYARRAY)),
                            adtmeths=dictmeths)
diff --git a/pypy/rlib/_rweakvaldict.py b/pypy/rlib/_rweakvaldict.py
--- a/pypy/rlib/_rweakvaldict.py
+++ b/pypy/rlib/_rweakvaldict.py
@@ -53,7 +53,7 @@
         self.WEAKDICT = lltype.GcStruct(
             "weakvaldict",
             ("num_items", lltype.Signed),
-            ("num_pristine_entries", lltype.Signed),
+            ("resize_counter", lltype.Signed),
             ("entries", lltype.Ptr(WEAKDICTENTRYARRAY)),
             adtmeths=dictmeths)
 
@@ -107,7 +107,7 @@
         d = lltype.malloc(self.WEAKDICT)
         d.entries = self.WEAKDICT.entries.TO.allocate(rdict.DICT_INITSIZE)
         d.num_items = 0
-        d.num_pristine_entries = rdict.DICT_INITSIZE
+        d.resize_counter = rdict.DICT_INITSIZE * 2
         return d
 
     @jit.dont_look_inside
@@ -138,8 +138,8 @@
         d.entries[i].value = valueref
         #llop.debug_print(lltype.Void, i, 'stored')
         if not everused:
-            d.num_pristine_entries -= 1
-            if d.num_pristine_entries * 3 <= len(d.entries):
+            d.resize_counter -= 3
+            if d.resize_counter <= 0:
                 #llop.debug_print(lltype.Void, 'RESIZE')
                 self.ll_weakdict_resize(d)
 
diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py
--- a/pypy/rlib/jit.py
+++ b/pypy/rlib/jit.py
@@ -1,10 +1,13 @@
+import sys
+
 import py
-import sys
+
+from pypy.rlib.nonconst import NonConstant
+from pypy.rlib.objectmodel import CDefinedIntSymbolic, keepalive_until_here, specialize
+from pypy.rlib.unroll import unrolling_iterable
 from pypy.rpython.extregistry import ExtRegistryEntry
-from pypy.rlib.objectmodel import CDefinedIntSymbolic
-from pypy.rlib.objectmodel import keepalive_until_here, specialize
-from pypy.rlib.unroll import unrolling_iterable
-from pypy.rlib.nonconst import NonConstant
+from pypy.tool.sourcetools import func_with_new_name
+
 
 def elidable(func):
     """ Decorate a function as "trace-elidable". This means precisely that:
@@ -72,17 +75,22 @@
     func._jit_loop_invariant_ = True
     return func
 
+def _get_args(func):
+    import inspect
+
+    args, varargs, varkw, defaults = inspect.getargspec(func)
+    args = ["v%s" % (i, ) for i in range(len(args))]
+    assert varargs is None and varkw is None
+    assert not defaults
+    return args
+
 def elidable_promote(promote_args='all'):
     """ A decorator that promotes all arguments and then calls the supplied
     function
     """
     def decorator(func):
-        import inspect
         elidable(func)
-        args, varargs, varkw, defaults = inspect.getargspec(func)
-        args = ["v%s" % (i, ) for i in range(len(args))]
-        assert varargs is None and varkw is None
-        assert not defaults
+        args = _get_args(func)
         argstring = ", ".join(args)
         code = ["def f(%s):\n" % (argstring, )]
         if promote_args != 'all':
@@ -102,6 +110,46 @@
     warnings.warn("purefunction_promote is deprecated, use elidable_promote instead", DeprecationWarning)
     return elidable_promote(*args, **kwargs)
 
+def look_inside_iff(predicate):
+    """
+    look inside (including unrolling loops) the target function, if and only if
+    predicate(*args) returns True
+    """
+    def inner(func):
+        func = unroll_safe(func)
+        # When we return the new function, it might be specialized in some
+        # way. We "propogate" this specialization by using
+        # specialize:call_location on relevant functions.
+        for thing in [func, predicate]:
+            thing._annspecialcase_ = "specialize:call_location"
+
+        args = _get_args(func)
+        d = {
+            "dont_look_inside": dont_look_inside,
+            "predicate": predicate,
+            "func": func,
+            "we_are_jitted": we_are_jitted,
+        }
+        exec py.code.Source("""
+            @dont_look_inside
+            def trampoline(%(arguments)s):
+                return func(%(arguments)s)
+            if hasattr(func, "oopspec"):
+                # XXX: This seems like it should be here, but it causes errors.
+                # trampoline.oopspec = func.oopspec
+                del func.oopspec
+            trampoline.__name__ = func.__name__ + "_trampoline"
+            trampoline._annspecialcase_ = "specialize:call_location"
+
+            def f(%(arguments)s):
+                if not we_are_jitted() or predicate(%(arguments)s):
+                    return func(%(arguments)s)
+                else:
+                    return trampoline(%(arguments)s)
+            f.__name__ = func.__name__ + "_look_inside_iff"
+        """ % {"arguments": ", ".join(args)}).compile() in d
+        return d["f"]
+    return inner
 
 def oopspec(spec):
     def decorator(func):
@@ -109,6 +157,34 @@
         return func
     return decorator
 
+ at oopspec("jit.isconstant(value)")
+ at specialize.argtype(0)
+def isconstant(value):
+    """
+    While tracing, returns whether or not the value is currently known to be
+    constant. This is not perfect, values can become constant later. Mostly for
+    use with @look_inside_iff.
+
+    This is for advanced usage only.
+    """
+    # I hate the annotator so much.
+    if NonConstant(False):
+        return True
+    return False
+
+ at oopspec("jit.isvirtual(value)")
+ at specialize.ll()
+def isvirtual(value):
+    """
+    Returns if this value is virtual, while tracing, it's relatively
+    conservative and will miss some cases.
+
+    This is for advanced usage only.
+    """
+    if NonConstant(False):
+        return True
+    return False
+
 class Entry(ExtRegistryEntry):
     _about_ = hint
 
@@ -291,10 +367,10 @@
 class JitHintError(Exception):
     """Inconsistency in the JIT hints."""
 
-PARAMETERS = {'threshold': 1032, # just above 1024
-              'function_threshold': 1617, # slightly more than one above
+PARAMETERS = {'threshold': 1039, # just above 1024, prime
+              'function_threshold': 1619, # slightly more than one above, also prime
               'trace_eagerness': 200,
-              'trace_limit': 12000,
+              'trace_limit': 6000,
               'inlining': 1,
               'loop_longevity': 1000,
               'retrace_limit': 5,
diff --git a/pypy/rlib/objectmodel.py b/pypy/rlib/objectmodel.py
--- a/pypy/rlib/objectmodel.py
+++ b/pypy/rlib/objectmodel.py
@@ -19,6 +19,8 @@
 # def f(...
 #
 
+from pypy.rpython.extregistry import ExtRegistryEntry
+
 class _Specialize(object):
     def memo(self):
         """ Specialize functions based on argument values. All arguments has
@@ -177,6 +179,34 @@
     obj.__class__ = FREED_OBJECT
 
 # ____________________________________________________________
+
+def newlist(sizehint=0):
+    """ Create a new list, but pass a hint how big the size should be
+    preallocated
+    """
+    return []
+
+class Entry(ExtRegistryEntry):
+    _about_ = newlist
+
+    def compute_result_annotation(self, s_sizehint):
+        from pypy.annotation.model import SomeInteger
+        
+        assert isinstance(s_sizehint, SomeInteger)
+        return self.bookkeeper.newlist()
+
+    def specialize_call(self, orig_hop, i_sizehint=None):
+        from pypy.rpython.rlist import rtype_newlist
+        # fish a bit hop
+        hop = orig_hop.copy()
+        v = hop.args_v[0]
+        r, s = hop.r_s_popfirstarg()
+        if s.is_constant():
+            v = hop.inputconst(r, s.const)
+        hop.exception_is_here()
+        return rtype_newlist(hop, v_sizehint=v)
+
+# ____________________________________________________________
 #
 # id-like functions.  The idea is that calling hash() or id() is not
 # allowed in RPython.  You have to call one of the following more
@@ -301,8 +331,6 @@
 
 # ----------
 
-from pypy.rpython.extregistry import ExtRegistryEntry
-
 class Entry(ExtRegistryEntry):
     _about_ = compute_hash
 
diff --git a/pypy/rlib/parsing/codebuilder.py b/pypy/rlib/parsing/codebuilder.py
--- a/pypy/rlib/parsing/codebuilder.py
+++ b/pypy/rlib/parsing/codebuilder.py
@@ -1,3 +1,5 @@
+import contextlib
+
 class Codebuilder(object):
     def __init__(self):
         self.blocks = []
@@ -27,10 +29,12 @@
         assert blockstarter.endswith(":")
         self.emit(blockstarter)
         self.blocks.append(blockstarter)
-        def BlockEnder():
-            yield None
-            self.end_block(blockstarter)
-        return BlockEnder()
+
+    @contextlib.contextmanager
+    def block(self, blockstarter):
+        self.start_block(blockstarter)
+        yield None
+        self.end_block(blockstarter)
 
     def end_block(self, starterpart=""):
         block = self.blocks.pop()
diff --git a/pypy/rlib/parsing/deterministic.py b/pypy/rlib/parsing/deterministic.py
--- a/pypy/rlib/parsing/deterministic.py
+++ b/pypy/rlib/parsing/deterministic.py
@@ -1,3 +1,4 @@
+from __future__ import with_statement
 import py
 
 try:
@@ -228,11 +229,11 @@
         above = set()
         for state, nextstates in state_to_chars.iteritems():
             above.add(state)
-            for _ in result.start_block("if state == %s:" % (state, )):
-                for _ in result.start_block("if i < len(input):"):
+            with result.block("if state == %s:" % (state, )):
+                with result.block("if i < len(input):"):
                     result.emit("char = input[i]")
                     result.emit("i += 1")
-                for _ in result.start_block("else:"):
+                with result.block("else:"):
                     if state in self.final_states:
                         result.emit("return True")
                     else:
@@ -248,7 +249,7 @@
                     for i, (a, num) in enumerate(compressed):
                         if num < 5:
                             for charord in range(ord(a), ord(a) + num):
-                                for _ in result.start_block(
+                                with result.block(
                                     "%sif char == %r:" % (
                                         elif_prefix, chr(charord))):
                                     result.emit("state = %s" % (nextstate, ))
@@ -256,23 +257,23 @@
                                 if not elif_prefix:
                                     elif_prefix = "el"
                         else:
-                            for _ in result.start_block(
+                            with result.block(
                                 "%sif %r <= char <= %r:" % (
                                     elif_prefix, a, chr(ord(a) + num - 1))):
                                 result.emit("state = %s""" % (nextstate, ))
                                 result.emit(continue_prefix)
                             if not elif_prefix:
                                 elif_prefix = "el"
-                for _ in result.start_block("else:"):
+                with result.block("else:"):
                     result.emit("break") 
         #print state_to_chars.keys()
         for state in range(self.num_states):
             if state in state_to_chars:
                 continue
-            for _ in result.start_block("if state == %s:" % (state, )):
-                for _ in result.start_block("if i == len(input):"):
+            with result.block("if state == %s:" % (state, )):
+                with result.block("if i == len(input):"):
                     result.emit("return True")
-                for _ in result.start_block("else:"):
+                with result.block("else:"):
                     result.emit("break")
         result.emit("break")
         result.end_block("while")
@@ -303,14 +304,14 @@
         above = set()
         for state, nextstates in state_to_chars_sorted:
             above.add(state)
-            for _ in result.start_block("if state == %s:" % (state, )):
+            with result.block("if state == %s:" % (state, )):
                 if state in self.final_states:
                     result.emit("runner.last_matched_index = i - 1")
                     result.emit("runner.last_matched_state = state")
-                for _ in result.start_block("try:"):
+                with result.block("try:"):
                     result.emit("char = input[i]")
                     result.emit("i += 1")
-                for _ in result.start_block("except IndexError:"):
+                with result.block("except IndexError:"):
                     result.emit("runner.state = %s" % (state, ))
                     if state in self.final_states:
                         result.emit("return i")
@@ -327,21 +328,21 @@
                     for i, (a, num) in enumerate(compressed):
                         if num < 3:
                             for charord in range(ord(a), ord(a) + num):
-                                for _ in result.start_block("%sif char == %r:"
+                                with result.block("%sif char == %r:"
                                         % (elif_prefix, chr(charord))):
                                     result.emit("state = %s" % (nextstate, ))
                                     result.emit(continue_prefix)
                                 if not elif_prefix:
                                     elif_prefix = "el"
                         else:
-                            for _ in result.start_block(
+                            with result.block(
                                 "%sif %r <= char <= %r:" % (
                                     elif_prefix, a, chr(ord(a) + num - 1))):
                                     result.emit("state = %s" % (nextstate, ))
                                     result.emit(continue_prefix)
                             if not elif_prefix:
                                 elif_prefix = "el"
-                for _ in result.start_block("else:"):
+                with result.block("else:"):
                     result.emit("break")
         #print state_to_chars.keys()
         for state in range(self.num_states):
diff --git a/pypy/rlib/parsing/makepackrat.py b/pypy/rlib/parsing/makepackrat.py
--- a/pypy/rlib/parsing/makepackrat.py
+++ b/pypy/rlib/parsing/makepackrat.py
@@ -1,3 +1,4 @@
+from __future__ import with_statement
 import py
 import sys
 from pypy.rlib.parsing.tree import Nonterminal, Symbol, RPythonVisitor
@@ -321,27 +322,27 @@
         else:
             self.emit("_key = self._pos")
         self.emit("_status = self.%s.get(_key, None)" % (dictname, ))
-        for _ in self.start_block("if _status is None:"):
+        with self.block("if _status is None:"):
             self.emit("_status = self.%s[_key] = Status()" % (
                 dictname, ))
-        for _ in self.start_block("else:"):
+        with self.block("else:"):
             self.emit("_statusstatus = _status.status")
-            for _ in self.start_block("if _statusstatus == _status.NORMAL:"):
+            with self.block("if _statusstatus == _status.NORMAL:"):
                 self.emit("self._pos = _status.pos")
                 self.emit("return _status")
-            for _ in self.start_block("elif _statusstatus == _status.ERROR:"):
+            with self.block("elif _statusstatus == _status.ERROR:"):
                 self.emit("raise BacktrackException(_status.error)")
             if self.have_call:
-                for _ in self.start_block(
+                with self.block(
                     "elif (_statusstatus == _status.INPROGRESS or\n"
                     "      _statusstatus == _status.LEFTRECURSION):"):
                     self.emit("_status.status = _status.LEFTRECURSION")
-                    for _ in self.start_block("if _status.result is not None:"):
+                    with self.block("if _status.result is not None:"):
                         self.emit("self._pos = _status.pos")
                         self.emit("return _status")
-                    for _ in self.start_block("else:"):
+                    with self.block("else:"):
                         self.emit("raise BacktrackException(None)")
-                for _ in self.start_block(
+                with self.block(
                     "elif _statusstatus == _status.SOMESOLUTIONS:"):
                     self.emit("_status.status = _status.INPROGRESS")
         self.emit("_startingpos = self._pos")
@@ -352,10 +353,10 @@
     def memoize_footer(self, name, args):
         dictname = "_dict_%s" % (name, )
         if self.have_call:
-            for _ in self.start_block(
+            with self.block(
                 "if _status.status == _status.LEFTRECURSION:"):
-                for _ in self.start_block("if _status.result is not None:"):
-                    for _ in self.start_block("if _status.pos >= self._pos:"):
+                with self.block("if _status.result is not None:"):
+                    with self.block("if _status.pos >= self._pos:"):
                         self.emit("_status.status = _status.NORMAL")
                         self.emit("self._pos = _status.pos")
                         self.emit("return _status")
@@ -373,7 +374,7 @@
         self.emit("_status.error = _error")
         self.emit("return _status")
         self.end_block("try")
-        for _ in self.start_block("except BacktrackException, _exc:"):
+        with self.block("except BacktrackException, _exc:"):
             self.emit("_status.pos = -1")
             self.emit("_status.result = None")
             self.combine_error('_exc.error')
@@ -394,7 +395,7 @@
         self.start_block("class Parser(object):")
         for elt in t.children:
             self.dispatch(elt)
-        for _ in self.start_block("def __init__(self, inputstream):"):
+        with self.block("def __init__(self, inputstream):"):
             for line in self.initcode:
                 self.emit(line)
             self.emit("self._pos = 0")
@@ -405,7 +406,7 @@
 
     def emit_regex_code(self):
         for regex, matcher in self.matchers.iteritems():
-            for _ in  self.start_block(
+            with  self.block(
                     "def _regex%s(self):" % (abs(hash(regex)), )):
                 c = self.choice_point()
                 self.emit("_runner = self._Runner(self._inputstream, self._pos)")
@@ -423,8 +424,8 @@
                 self.emit("self._pos = _upto")
                 self.emit("return _result")
 
-        for _ in self.start_block("class _Runner(object):"):
-            for _ in self.start_block("def __init__(self, text, pos):"):
+        with self.block("class _Runner(object):"):
+            with self.block("def __init__(self, text, pos):"):
                 self.emit("self.text = text")
                 self.emit("self.pos = pos")
                 self.emit("self.last_matched_state = -1")
@@ -444,7 +445,7 @@
         otherargs = t.children[1].children
         argswithself = ", ".join(["self"] + otherargs)
         argswithoutself = ", ".join(otherargs)
-        for _ in self.start_block("def %s(%s):" % (name, argswithself)):
+        with self.block("def %s(%s):" % (name, argswithself)):
             self.emit("return self._%s(%s).result" % (name, argswithoutself))
         self.start_block("def _%s(%s):" % (name, argswithself, ))
         self.namecount = 0
@@ -465,10 +466,10 @@
             self.start_block("while 1:")
         for i, p in enumerate(possibilities):
             c = self.choice_point()
-            for _ in self.start_block("try:"):
+            with self.block("try:"):
                 self.dispatch(p)
                 self.emit("break")
-            for _ in self.start_block("except BacktrackException, _exc:"):
+            with self.block("except BacktrackException, _exc:"):
                 self.combine_error('_exc.error')
                 self.revert(c)
                 if i == len(possibilities) - 1:
@@ -484,9 +485,9 @@
 
     def visit_maybe(self, t):
         c = self.choice_point()
-        for _ in self.start_block("try:"):
+        with self.block("try:"):
             self.dispatch(t.children[0])
-        for _ in self.start_block("except BacktrackException:"):
+        with self.block("except BacktrackException:"):
             self.revert(c)
 
     def visit_repetition(self, t):
@@ -496,12 +497,12 @@
         if t.children[0] == '+':
             self.dispatch(t.children[1])
             self.emit("%s.append(_result)"  % (name, ))
-        for _ in self.start_block("while 1:"):
+        with self.block("while 1:"):
             c = self.choice_point()
-            for _ in self.start_block("try:"):
+            with self.block("try:"):
                 self.dispatch(t.children[1])
                 self.emit("%s.append(_result)" % (name, ))
-            for _ in self.start_block("except BacktrackException, _exc:"):
+            with self.block("except BacktrackException, _exc:"):
                 self.combine_error('_exc.error')
                 self.revert(c)
                 self.emit("break")
@@ -525,12 +526,12 @@
         self.namecount += 1
         child = t.children[0]
         self.emit("%s = _result" % (resultname, ))
-        for _ in self.start_block("try:"):
+        with self.block("try:"):
             self.dispatch(child)
-        for _ in self.start_block("except BacktrackException:"):
+        with self.block("except BacktrackException:"):
             self.revert(c)
             self.emit("_result = %s" % (resultname, ))
-        for _ in self.start_block("else:"):
+        with self.block("else:"):
             # heuristic to get nice error messages sometimes
             if isinstance(child, Symbol) and child.symbol == "QUOTE":
 
@@ -559,21 +560,21 @@
     def visit_if(self, t):
         if len(t.children) == 2:
             self.dispatch(t.children[0])
-        for _ in self.start_block("if not (%s):" % (
+        with self.block("if not (%s):" % (
             t.children[-1].additional_info[1:-1], )):
             self.emit("raise BacktrackException(")
             self.emit("    self._ErrorInformation(")
             self.emit("         _startingpos, ['condition not met']))")
-    
+
     def visit_choose(self, t):
-        for _ in self.start_block("for %s in (%s):" % (
+        with self.block("for %s in (%s):" % (
             t.children[0], t.children[1].additional_info[1:-1], )):
-            for _ in self.start_block("try:"):
+            with self.block("try:"):
                 self.dispatch(t.children[2])
                 self.emit("break")
-            for _ in self.start_block("except BacktrackException, _exc:"):
+            with self.block("except BacktrackException, _exc:"):
                 self.combine_error('_exc.error')
-        for _ in self.start_block("else:"):
+        with self.block("else:"):
             self.emit("raise BacktrackException(_error)")
 
     def visit_call(self, t):
diff --git a/pypy/rlib/rgc.py b/pypy/rlib/rgc.py
--- a/pypy/rlib/rgc.py
+++ b/pypy/rlib/rgc.py
@@ -1,6 +1,9 @@
-import gc, types
+import gc
+import types
+
+from pypy.rlib import jit
+from pypy.rlib.objectmodel import we_are_translated, enforceargs, specialize
 from pypy.rpython.extregistry import ExtRegistryEntry
-from pypy.rlib.objectmodel import we_are_translated
 from pypy.rpython.lltypesystem import lltype, llmemory
 
 # ____________________________________________________________
@@ -32,7 +35,7 @@
         if len(hop.args_s) == 1:
             args_v = hop.inputargs(lltype.Signed)
         return hop.genop('gc__collect', args_v, resulttype=hop.r_result)
-    
+
 class SetMaxHeapSizeEntry(ExtRegistryEntry):
     _about_ = set_max_heap_size
 
@@ -133,6 +136,9 @@
         hop.exception_cannot_occur()
         return hop.genop(opname, vlist, resulttype = hop.r_result.lowleveltype)
 
+ at jit.oopspec('list.ll_arraycopy(source, dest, source_start, dest_start, length)')
+ at specialize.ll()
+ at enforceargs(None, None, int, int, int)
 def ll_arraycopy(source, dest, source_start, dest_start, length):
     from pypy.rpython.lltypesystem.lloperation import llop
     from pypy.rlib.objectmodel import keepalive_until_here
@@ -161,14 +167,11 @@
                       llmemory.sizeof(TP.OF) * source_start)
     cp_dest_addr = (dest_addr + llmemory.itemoffsetof(TP, 0) +
                     llmemory.sizeof(TP.OF) * dest_start)
-    
+
     llmemory.raw_memcopy(cp_source_addr, cp_dest_addr,
                          llmemory.sizeof(TP.OF) * length)
     keepalive_until_here(source)
     keepalive_until_here(dest)
-ll_arraycopy._annenforceargs_ = [None, None, int, int, int]
-ll_arraycopy._annspecialcase_ = 'specialize:ll'
-ll_arraycopy.oopspec = 'list.ll_arraycopy(source, dest, source_start, dest_start, length)'
 
 def ll_shrink_array(p, smallerlength):
     from pypy.rpython.lltypesystem.lloperation import llop
@@ -192,7 +195,7 @@
               llmemory.itemoffsetof(ARRAY, 0))
     source_addr = llmemory.cast_ptr_to_adr(p)    + offset
     dest_addr   = llmemory.cast_ptr_to_adr(newp) + offset
-    llmemory.raw_memcopy(source_addr, dest_addr, 
+    llmemory.raw_memcopy(source_addr, dest_addr,
                          llmemory.sizeof(ARRAY.OF) * smallerlength)
 
     keepalive_until_here(p)
diff --git a/pypy/rlib/ropenssl.py b/pypy/rlib/ropenssl.py
--- a/pypy/rlib/ropenssl.py
+++ b/pypy/rlib/ropenssl.py
@@ -62,6 +62,8 @@
         "OPENSSL_VERSION_NUMBER")
     SSLEAY_VERSION = rffi_platform.DefinedConstantString(
         "SSLEAY_VERSION", "SSLeay_version(SSLEAY_VERSION)")
+    OPENSSL_NO_SSL2 = rffi_platform.DefinedConstantInteger(
+        "OPENSSL_NO_SSL2")
     SSL_FILETYPE_PEM = rffi_platform.ConstantInteger("SSL_FILETYPE_PEM")
     SSL_OP_ALL = rffi_platform.ConstantInteger("SSL_OP_ALL")
     SSL_VERIFY_NONE = rffi_platform.ConstantInteger("SSL_VERIFY_NONE")
diff --git a/pypy/rlib/rstacklet.py b/pypy/rlib/rstacklet.py
--- a/pypy/rlib/rstacklet.py
+++ b/pypy/rlib/rstacklet.py
@@ -1,24 +1,47 @@
 from pypy.rlib import _rffi_stacklet as _c
+from pypy.rlib import jit
+from pypy.rlib.objectmodel import we_are_translated
 from pypy.rpython.lltypesystem import lltype, llmemory
 
+DEBUG = False
+
 
 class StackletThread(object):
 
+    @jit.dont_look_inside
     def __init__(self, config):
-        self._gcrootfinder = _getgcrootfinder(config)
+        self._gcrootfinder = _getgcrootfinder(config, we_are_translated())
         self._thrd = _c.newthread()
         if not self._thrd:
             raise MemoryError
         self._thrd_deleter = StackletThreadDeleter(self._thrd)
+        if DEBUG:
+            assert debug.sthread is None, "multithread debug support missing"
+            debug.sthread = self
 
+    @jit.dont_look_inside
     def new(self, callback, arg=llmemory.NULL):
-        return self._gcrootfinder.new(self, callback, arg)
+        if DEBUG:
+            callback = _debug_wrapper(callback)
+        h = self._gcrootfinder.new(self, callback, arg)
+        if DEBUG:
+            debug.add(h)
+        return h
     new._annspecialcase_ = 'specialize:arg(1)'
 
+    @jit.dont_look_inside
     def switch(self, stacklet):
-        return self._gcrootfinder.switch(self, stacklet)
+        if DEBUG:
+            debug.remove(stacklet)
+        h = self._gcrootfinder.switch(self, stacklet)
+        if DEBUG:
+            debug.add(h)
+        return h
 
+    @jit.dont_look_inside
     def destroy(self, stacklet):
+        if DEBUG:
+            debug.remove(stacklet)
         self._gcrootfinder.destroy(self, stacklet)
 
     def is_empty_handle(self, stacklet):
@@ -45,7 +68,13 @@
 
 # ____________________________________________________________
 
-def _getgcrootfinder(config):
+def _getgcrootfinder(config, translated):
+    if translated:
+        assert config is not None, ("you have to pass a valid config, "
+                                    "e.g. from 'driver.config'")
+    if config is not None:
+        assert config.translation.continuation, (
+            "stacklet: you have to translate with --continuation")
     if (config is None or
         config.translation.gc in ('ref', 'boehm', 'none')):   # for tests
         gcrootfinder = 'n/a'
@@ -56,3 +85,34 @@
                         None, None, ['__doc__'])
     return module.gcrootfinder
 _getgcrootfinder._annspecialcase_ = 'specialize:memo'
+
+
+class StackletDebugError(Exception):
+    pass
+
+class Debug(object):
+    def __init__(self):
+        self.sthread = None
+        self.active = []
+    def _freeze_(self):
+        self.__init__()
+        return False
+    def add(self, h):
+        if not self.sthread.is_empty_handle(h):
+            self.active.append(h)
+    def remove(self, h):
+        try:
+            i = self.active.index(h)
+        except ValueError:
+            raise StackletDebugError
+        del self.active[i]
+debug = Debug()
+
+def _debug_wrapper(callback):
+    def wrapper(h, arg):
+        debug.add(h)
+        h = callback(h, arg)
+        debug.remove(h)
+        return h
+    return wrapper
+_debug_wrapper._annspecialcase_ = 'specialize:memo'
diff --git a/pypy/rlib/rstring.py b/pypy/rlib/rstring.py
--- a/pypy/rlib/rstring.py
+++ b/pypy/rlib/rstring.py
@@ -2,7 +2,8 @@
 """
 
 from pypy.annotation.model import (SomeObject, SomeString, s_None, SomeChar,
-    SomeInteger, SomeUnicodeCodePoint, SomeUnicodeString, SomePtr)
+    SomeInteger, SomeUnicodeCodePoint, SomeUnicodeString, SomePtr, SomePBC)
+from pypy.tool.pairtype import pair, pairtype
 from pypy.rpython.extregistry import ExtRegistryEntry
 
 
@@ -170,3 +171,24 @@
 class UnicodeBuilderEntry(BaseEntry, ExtRegistryEntry):
     _about_ = UnicodeBuilder
     use_unicode = True
+
+class __extend__(pairtype(SomeStringBuilder, SomePBC)):
+    def union((sb, p)):
+        assert p.const is None
+        return SomeStringBuilder(can_be_None=True)
+
+class __extend__(pairtype(SomePBC, SomeStringBuilder)):
+    def union((p, sb)):
+        assert p.const is None
+        return SomeStringBuilder(can_be_None=True)
+
+class __extend__(pairtype(SomeUnicodeBuilder, SomePBC)):
+    def union((sb, p)):
+        assert p.const is None
+        return SomeUnicodeBuilder(can_be_None=True)
+
+class __extend__(pairtype(SomePBC, SomeUnicodeBuilder)):
+    def union((p, sb)):
+        assert p.const is None
+        return SomeUnicodeBuilder(can_be_None=True)
+
diff --git a/pypy/rlib/rstruct/formatiterator.py b/pypy/rlib/rstruct/formatiterator.py
--- a/pypy/rlib/rstruct/formatiterator.py
+++ b/pypy/rlib/rstruct/formatiterator.py
@@ -1,10 +1,10 @@
-
-from pypy.rlib.rstruct.nativefmttable import native_is_bigendian
-from pypy.rlib.unroll import unrolling_iterable
+from pypy.rlib import jit
 from pypy.rlib.rarithmetic import ovfcheck
 from pypy.rlib.rstruct.error import StructError
+from pypy.rlib.rstruct.nativefmttable import native_is_bigendian, native_fmttable
 from pypy.rlib.rstruct.standardfmttable import standard_fmttable
-from pypy.rlib.rstruct.nativefmttable import native_fmttable
+from pypy.rlib.unroll import unrolling_iterable
+
 
 class FormatIterator(object):
     """
@@ -16,6 +16,7 @@
     _mixin_ = True
     _operate_is_specialized_ = False
 
+    @jit.look_inside_iff(lambda self, fmt: jit.isconstant(fmt))
     def interpret(self, fmt):
         # decode the byte order, size and alignment based on the 1st char
         table = unroll_native_fmtdescs
diff --git a/pypy/rlib/rwin32.py b/pypy/rlib/rwin32.py
--- a/pypy/rlib/rwin32.py
+++ b/pypy/rlib/rwin32.py
@@ -55,14 +55,19 @@
         SYSTEMTIME = rffi_platform.Struct('SYSTEMTIME',
                                           [])
 
-        OSVERSIONINFO = rffi_platform.Struct(
-            'OSVERSIONINFO',
+        OSVERSIONINFOEX = rffi_platform.Struct(
+            'OSVERSIONINFOEX',
             [('dwOSVersionInfoSize', rffi.UINT),
              ('dwMajorVersion', rffi.UINT),
              ('dwMinorVersion', rffi.UINT),
              ('dwBuildNumber',  rffi.UINT),
              ('dwPlatformId',  rffi.UINT),
-             ('szCSDVersion', rffi.CFixedArray(lltype.Char, 1))])
+             ('szCSDVersion', rffi.CFixedArray(lltype.Char, 1)),
+             ('wServicePackMajor', rffi.USHORT),
+             ('wServicePackMinor', rffi.USHORT),
+             ('wSuiteMask', rffi.USHORT),
+             ('wProductType', rffi.UCHAR),
+         ])
 
         LPSECURITY_ATTRIBUTES = rffi_platform.SimpleType(
             "LPSECURITY_ATTRIBUTES", rffi.CCHARP)
@@ -225,14 +230,14 @@
             lltype.free(buf, flavor='raw')
 
     _GetVersionEx = winexternal('GetVersionExA',
-                                [lltype.Ptr(OSVERSIONINFO)],
+                                [lltype.Ptr(OSVERSIONINFOEX)],
                                 DWORD)
 
     @jit.dont_look_inside
     def GetVersionEx():
-        info = lltype.malloc(OSVERSIONINFO, flavor='raw')
+        info = lltype.malloc(OSVERSIONINFOEX, flavor='raw')
         rffi.setintfield(info, 'c_dwOSVersionInfoSize',
-                         rffi.sizeof(OSVERSIONINFO))
+                         rffi.sizeof(OSVERSIONINFOEX))
         try:
             if not _GetVersionEx(info):
                 raise lastWindowsError()
@@ -241,7 +246,11 @@
                     rffi.cast(lltype.Signed, info.c_dwBuildNumber),
                     rffi.cast(lltype.Signed, info.c_dwPlatformId),
                     rffi.charp2str(rffi.cast(rffi.CCHARP,
-                                             info.c_szCSDVersion)))
+                                             info.c_szCSDVersion)),
+                    rffi.cast(lltype.Signed, info.c_wServicePackMajor),
+                    rffi.cast(lltype.Signed, info.c_wServicePackMinor),
+                    rffi.cast(lltype.Signed, info.c_wSuiteMask),
+                    rffi.cast(lltype.Signed, info.c_wProductType))
         finally:
             lltype.free(info, flavor='raw')
 
diff --git a/pypy/rlib/rzlib.py b/pypy/rlib/rzlib.py
--- a/pypy/rlib/rzlib.py
+++ b/pypy/rlib/rzlib.py
@@ -1,3 +1,4 @@
+from __future__ import with_statement
 import sys
 
 from pypy.rlib.rstring import StringBuilder
diff --git a/pypy/rlib/test/test_objectmodel.py b/pypy/rlib/test/test_objectmodel.py
--- a/pypy/rlib/test/test_objectmodel.py
+++ b/pypy/rlib/test/test_objectmodel.py
@@ -424,3 +424,32 @@
     if option.view:
         graph.show()
     return graph
+
+
+def test_newlist():
+    from pypy.annotation.model import SomeInteger
+    def f(z):
+        x = newlist(sizehint=38)
+        if z < 0:
+            x.append(1)
+        return len(x)
+
+    graph = getgraph(f, [SomeInteger()])
+    for llop in graph.startblock.operations:
+        if llop.opname == 'malloc_varsize':
+            break
+    assert llop.args[2].value == 38
+
+def test_newlist_nonconst():
+    from pypy.annotation.model import SomeInteger
+    def f(z):
+        x = newlist(sizehint=z)
+        return len(x)
+
+    graph = getgraph(f, [SomeInteger()])
+    for llop in graph.startblock.operations:
+        if llop.opname == 'malloc_varsize':
+            break
+    assert llop.args[2] is graph.startblock.inputargs[0]
+
+    
diff --git a/pypy/rlib/test/test_rstring.py b/pypy/rlib/test/test_rstring.py
--- a/pypy/rlib/test/test_rstring.py
+++ b/pypy/rlib/test/test_rstring.py
@@ -2,7 +2,6 @@
 
 from pypy.rlib.rstring import StringBuilder, UnicodeBuilder, split, rsplit
 
-
 def test_split():
     assert split("", 'x') == ['']
     assert split("a", "a", 1) == ['', '']
@@ -42,4 +41,5 @@
     assert s.getlength() == len('aabcb')
     s.append_multiple_char(u'd', 4)
     assert s.build() == 'aabcbdddd'
-    assert isinstance(s.build(), unicode)
\ No newline at end of file
+    assert isinstance(s.build(), unicode)
+        
diff --git a/pypy/rpython/lltypesystem/ll_str.py b/pypy/rpython/lltypesystem/ll_str.py
--- a/pypy/rpython/lltypesystem/ll_str.py
+++ b/pypy/rpython/lltypesystem/ll_str.py
@@ -16,34 +16,31 @@
         return r_uint(i)
 
 @jit.elidable
-def ll_int2dec(i):
+def ll_int2dec(val):
     from pypy.rpython.lltypesystem.rstr import mallocstr
-    temp = malloc(CHAR_ARRAY, 20)
+
+    sign = int(val < 0)
+    if sign:
+        val = ll_unsigned(-val)
+    else:
+        val = ll_unsigned(val)
     len = 0
-    sign = 0
-    if i < 0:
-        sign = 1
-        i = ll_unsigned(-i)
-    else:
-        i = ll_unsigned(i)
-    if i == 0:
-        len = 1
-        temp[0] = '0'
-    else:
-        while i:
-            temp[len] = chr(i%10+ord('0'))
-            i //= 10
-            len += 1
-    len += sign
-    result = mallocstr(len)
-    result.hash = 0
+    i = val
+    while i:
+        len += 1
+        i //= 10
+
+    total_len = sign + len + int(val == 0)
+    result = mallocstr(total_len)
     if sign:
         result.chars[0] = '-'
-        j = 1
-    else:
-        j = 0
+    elif val == 0:
+        result.chars[0] = '0'
+
+    j = 0
     while j < len:
-        result.chars[j] = temp[len-j-1]
+        result.chars[total_len - j - 1] = chr(val % 10 + ord('0'))
+        val //= 10
         j += 1
     return result
 
diff --git a/pypy/rpython/lltypesystem/rbuilder.py b/pypy/rpython/lltypesystem/rbuilder.py
--- a/pypy/rpython/lltypesystem/rbuilder.py
+++ b/pypy/rpython/lltypesystem/rbuilder.py
@@ -1,10 +1,10 @@
-from pypy.rlib import rgc
+from pypy.rlib import rgc, jit
 from pypy.rlib.objectmodel import enforceargs
 from pypy.rlib.rarithmetic import ovfcheck
 from pypy.rpython.annlowlevel import llstr
 from pypy.rpython.rptr import PtrRepr
 from pypy.rpython.lltypesystem import lltype, rstr
-from pypy.rpython.lltypesystem.lltype import staticAdtMethod
+from pypy.rpython.lltypesystem.lltype import staticAdtMethod, nullptr
 from pypy.rpython.lltypesystem.rstr import (STR, UNICODE, char_repr,
     string_repr, unichar_repr, unicode_repr)
 from pypy.rpython.rbuilder import AbstractStringBuilderRepr
@@ -54,6 +54,9 @@
 MAX = 16*1024*1024
 
 class BaseStringBuilderRepr(AbstractStringBuilderRepr):
+    def empty(self):
+        return nullptr(self.lowleveltype.TO)
+    
     @classmethod
     def ll_new(cls, init_size):
         if init_size < 0 or init_size > MAX:
@@ -92,6 +95,7 @@
         ll_builder.used = needed + used
 
     @staticmethod
+    @jit.look_inside_iff(lambda ll_builder, char, times: jit.isconstant(times) and times <= 4)
     def ll_append_multiple_char(ll_builder, char, times):
         used = ll_builder.used
         if times + used > ll_builder.allocated:
@@ -123,6 +127,10 @@
             return ll_builder.buf
         return rgc.ll_shrink_array(ll_builder.buf, final_size)
 
+    @classmethod
+    def ll_is_true(cls, ll_builder):
+        return ll_builder != nullptr(cls.lowleveltype.TO)
+
 class StringBuilderRepr(BaseStringBuilderRepr):
     lowleveltype = lltype.Ptr(STRINGBUILDER)
     basetp = STR
diff --git a/pypy/rpython/lltypesystem/rdict.py b/pypy/rpython/lltypesystem/rdict.py
--- a/pypy/rpython/lltypesystem/rdict.py
+++ b/pypy/rpython/lltypesystem/rdict.py
@@ -20,7 +20,7 @@
 #  DICTVALUE types.
 #
 #  XXX for immutable dicts, the array should be inlined and
-#      num_pristine_entries and everused are not needed.
+#      resize_counter and everused are not needed.
 #
 #    struct dictentry {
 #        DICTKEY key;
@@ -32,7 +32,7 @@
 #
 #    struct dicttable {
 #        int num_items;
-#        int num_pristine_entries;  # never used entries
+#        int resize_counter;
 #        Array *entries;
 #        (Function DICTKEY, DICTKEY -> bool) *fnkeyeq;
 #        (Function DICTKEY -> int) *fnkeyhash;
@@ -176,7 +176,7 @@
             self.DICTENTRYARRAY = lltype.GcArray(self.DICTENTRY,
                                                  adtmeths=entrymeths)
             fields =          [ ("num_items", lltype.Signed),
-                                ("num_pristine_entries", lltype.Signed),
+                                ("resize_counter", lltype.Signed),
                                 ("entries", lltype.Ptr(self.DICTENTRYARRAY)) ]
             if self.custom_eq_hash:
                 self.r_rdict_eqfn, self.r_rdict_hashfn = self._custom_eq_hash_repr()
@@ -465,8 +465,8 @@
     d.num_items += 1
     if not everused:
         if hasattr(ENTRY, 'f_everused'): entry.f_everused = True
-        d.num_pristine_entries -= 1
-        if d.num_pristine_entries <= len(d.entries) / 3:
+        d.resize_counter -= 3
+        if d.resize_counter <= 0:
             ll_dict_resize(d)
 
 def ll_dict_insertclean(d, key, value, hash):
@@ -484,7 +484,7 @@
     if hasattr(ENTRY, 'f_valid'):    entry.f_valid = True
     if hasattr(ENTRY, 'f_everused'): entry.f_everused = True
     d.num_items += 1
-    d.num_pristine_entries -= 1
+    d.resize_counter -= 3
 
 def ll_dict_delitem(d, key):
     i = ll_dict_lookup(d, key, d.keyhash(key))
@@ -518,7 +518,7 @@
         new_size /= 2
     d.entries = lltype.typeOf(old_entries).TO.allocate(new_size)
     d.num_items = 0
-    d.num_pristine_entries = new_size
+    d.resize_counter = new_size * 2
     i = 0
     while i < old_size:
         if old_entries.valid(i):
@@ -619,7 +619,7 @@
     d = DICT.allocate()
     d.entries = DICT.entries.TO.allocate(DICT_INITSIZE)
     d.num_items = 0
-    d.num_pristine_entries = DICT_INITSIZE
+    d.resize_counter = DICT_INITSIZE * 2
     return d
 ll_newdict.oopspec = 'newdict()'
 
@@ -631,7 +631,7 @@
     d = DICT.allocate()
     d.entries = DICT.entries.TO.allocate(n)
     d.num_items = 0
-    d.num_pristine_entries = n
+    d.resize_counter = n * 2
     return d
 ll_newdict_size.oopspec = 'newdict()'
 
@@ -749,7 +749,7 @@
     d = DICT.allocate()
     d.entries = DICT.entries.TO.allocate(dictsize)
     d.num_items = dict.num_items
-    d.num_pristine_entries = dict.num_pristine_entries
+    d.resize_counter = dict.resize_counter
     if hasattr(DICT, 'fnkeyeq'):   d.fnkeyeq   = dict.fnkeyeq
     if hasattr(DICT, 'fnkeyhash'): d.fnkeyhash = dict.fnkeyhash
     i = 0
@@ -767,12 +767,13 @@
 ll_copy.oopspec = 'dict.copy(dict)'
 
 def ll_clear(d):
-    if len(d.entries) == d.num_pristine_entries == DICT_INITSIZE:
+    if (len(d.entries) == DICT_INITSIZE and
+        d.resize_counter == DICT_INITSIZE * 2):
         return
     old_entries = d.entries
     d.entries = lltype.typeOf(old_entries).TO.allocate(DICT_INITSIZE)
     d.num_items = 0
-    d.num_pristine_entries = DICT_INITSIZE
+    d.resize_counter = DICT_INITSIZE * 2
     old_entries.delete()
 ll_clear.oopspec = 'dict.clear(d)'
 
diff --git a/pypy/rpython/lltypesystem/rlist.py b/pypy/rpython/lltypesystem/rlist.py
--- a/pypy/rpython/lltypesystem/rlist.py
+++ b/pypy/rpython/lltypesystem/rlist.py
@@ -1,22 +1,15 @@
 from pypy.tool.pairtype import pairtype, pair
-from pypy.annotation import model as annmodel
-from pypy.rpython.error import TyperError
-from pypy.rpython.rmodel import Repr, IntegerRepr, inputconst
+from pypy.rpython.rmodel import Repr, inputconst
 from pypy.rpython.rmodel import externalvsinternal
 from pypy.rpython.rlist import AbstractBaseListRepr, AbstractListRepr, \
-        AbstractFixedSizeListRepr, AbstractListIteratorRepr, rtype_newlist, \
-        rtype_alloc_and_set, ll_setitem_nonneg, ADTIList, ADTIFixedList
-from pypy.rpython.rlist import dum_nocheck, dum_checkidx
-from pypy.rpython.lltypesystem.lltype import \
-     GcForwardReference, Ptr, GcArray, GcStruct, \
-     Void, Signed, malloc, typeOf, Primitive, \
-     Bool, nullptr, typeMethod
+        AbstractFixedSizeListRepr, AbstractListIteratorRepr, \
+        ll_setitem_nonneg, ADTIList, ADTIFixedList
+from pypy.rpython.rlist import dum_nocheck
+from pypy.rpython.lltypesystem.lltype import GcForwardReference, Ptr, GcArray,\
+     GcStruct, Void, Signed, malloc, typeOf, nullptr, typeMethod
 from pypy.rpython.lltypesystem import rstr
-from pypy.rpython import robject
 from pypy.rlib.debug import ll_assert
-from pypy.rpython.lltypesystem import rffi
-from pypy.rpython.lltypesystem.lloperation import llop
-from pypy.rlib import rgc
+from pypy.rlib import rgc, jit
 
 # ____________________________________________________________
 #
@@ -67,6 +60,7 @@
         ITEMARRAY = GcArray(ITEM,
                             adtmeths = ADTIFixedList({
                                  "ll_newlist": ll_fixed_newlist,
+                                 "ll_newlist_hint": ll_fixed_newlist,
                                  "ll_newemptylist": ll_fixed_newemptylist,
                                  "ll_length": ll_fixed_length,
                                  "ll_items": ll_fixed_items,
@@ -100,6 +94,7 @@
                                               ("items", Ptr(ITEMARRAY)),
                                       adtmeths = ADTIList({
                                           "ll_newlist": ll_newlist,
+                                          "ll_newlist_hint": ll_newlist_hint,
                                           "ll_newemptylist": ll_newemptylist,
                                           "ll_length": ll_length,
                                           "ll_items": ll_items,
@@ -230,20 +225,22 @@
     else:
         _ll_list_resize_really(l, newsize)
 
+ at jit.look_inside_iff(lambda l, newsize: jit.isconstant(len(l.items)) and jit.isconstant(newsize))
+ at jit.oopspec("list._resize_ge(l, newsize)")
 def _ll_list_resize_ge(l, newsize):
     if len(l.items) >= newsize:
         l.length = newsize
     else:
         _ll_list_resize_really(l, newsize)
-_ll_list_resize_ge.oopspec = 'list._resize_ge(l, newsize)'
 
+ at jit.look_inside_iff(lambda l, newsize: jit.isconstant(len(l.items)) and jit.isconstant(newsize))
+ at jit.oopspec("list._resize_le(l, newsize)")
 def _ll_list_resize_le(l, newsize):
     if newsize >= (len(l.items) >> 1) - 5:
         l.length = newsize
     else:
         _ll_list_resize_really(l, newsize)
 
-
 def ll_append_noresize(l, newitem):
     length = l.length
     l.length = length + 1
@@ -267,6 +264,15 @@
 ll_newlist = typeMethod(ll_newlist)
 ll_newlist.oopspec = 'newlist(length)'
 
+def ll_newlist_hint(LIST, lengthhint):
+    ll_assert(lengthhint >= 0, "negative list length")
+    l = malloc(LIST)
+    l.length = 0
+    l.items = malloc(LIST.items.TO, lengthhint)
+    return l
+ll_newlist_hint = typeMethod(ll_newlist_hint)
+ll_newlist_hint.oopspec = 'newlist(lengthhint)'
+
 # should empty lists start with no allocated memory, or with a preallocated
 # minimal number of entries?  XXX compare memory usage versus speed, and
 # check how many always-empty lists there are in a typical pypy-c run...
@@ -337,11 +343,15 @@
     l[index] = item
 ll_fixed_setitem_fast.oopspec = 'list.setitem(l, index, item)'
 
-def newlist(llops, r_list, items_v):
+def newlist(llops, r_list, items_v, v_sizehint=None):
     LIST = r_list.LIST
     if len(items_v) == 0:
-        v_result = llops.gendirectcall(LIST.ll_newemptylist)
+        if v_sizehint is None:
+            v_result = llops.gendirectcall(LIST.ll_newemptylist)
+        else:
+            v_result = llops.gendirectcall(LIST.ll_newlist_hint, v_sizehint)
     else:
+        assert v_sizehint is None
         cno = inputconst(Signed, len(items_v))
         v_result = llops.gendirectcall(LIST.ll_newlist, cno)
     v_func = inputconst(Void, dum_nocheck)
diff --git a/pypy/rpython/lltypesystem/rpbc.py b/pypy/rpython/lltypesystem/rpbc.py
--- a/pypy/rpython/lltypesystem/rpbc.py
+++ b/pypy/rpython/lltypesystem/rpbc.py
@@ -230,7 +230,8 @@
         args = bk.build_args(opname, hop.args_s[1:])
         s_pbc = hop.args_s[0]   # possibly more precise than self.s_pbc
         descs = list(s_pbc.descriptions)
-        shape, index = description.FunctionDesc.variant_for_call_site(bk, self.callfamily, descs, args)
+        vfcs = description.FunctionDesc.variant_for_call_site
+        shape, index = vfcs(bk, self.callfamily, descs, args, hop.spaceop)
         row_of_graphs = self.callfamily.calltables[shape][index]
         anygraph = row_of_graphs.itervalues().next()  # pick any witness
         vlist = [hop.inputarg(self, arg=0)]
diff --git a/pypy/rpython/lltypesystem/rstr.py b/pypy/rpython/lltypesystem/rstr.py
--- a/pypy/rpython/lltypesystem/rstr.py
+++ b/pypy/rpython/lltypesystem/rstr.py
@@ -5,7 +5,7 @@
 from pypy.rlib.objectmodel import _hash_string, enforceargs
 from pypy.rlib.objectmodel import keepalive_until_here
 from pypy.rlib.debug import ll_assert
-from pypy.rlib.jit import elidable, we_are_jitted, dont_look_inside
+from pypy.rlib import jit
 from pypy.rlib.rarithmetic import ovfcheck
 from pypy.rpython.robject import PyObjRepr, pyobj_repr
 from pypy.rpython.rmodel import inputconst, IntegerRepr
@@ -58,8 +58,7 @@
                 llmemory.itemoffsetof(TP.chars, 0) +
                 llmemory.sizeof(CHAR_TP) * item)
 
-    # It'd be nice to be able to look inside this function.
-    @dont_look_inside
+    @jit.oopspec('stroruni.copy_contents(src, dst, srcstart, dststart, length)')
     @enforceargs(None, None, int, int, int)
     def copy_string_contents(src, dst, srcstart, dststart, length):
         assert srcstart >= 0
@@ -71,8 +70,6 @@
         keepalive_until_here(src)
         keepalive_until_here(dst)
     copy_string_contents._always_inline_ = True
-    #copy_string_contents.oopspec = (
-    #    '%s.copy_contents(src, dst, srcstart, dststart, length)' % name)
     return func_with_new_name(copy_string_contents, 'copy_%s_contents' % name)
 
 copy_string_contents = _new_copy_contents_fun(STR, Char, 'string')
@@ -147,7 +144,7 @@
         self.ll = LLHelpers
         self.malloc = mallocunicode
 
-    @elidable
+    @jit.elidable
     def ll_str(self, s):
         # XXX crazy that this is here, but I don't want to break
         #     rmodel logic
@@ -162,7 +159,7 @@
             result.chars[i] = cast_primitive(Char, c)
         return result
 
-    @elidable
+    @jit.elidable
     def ll_encode_latin1(self, s):
         length = len(s.chars)
         result = mallocstr(length)
@@ -261,7 +258,7 @@
 
 
 class LLHelpers(AbstractLLHelpers):
-    @elidable
+    @jit.elidable
     def ll_str_mul(s, times):
         if times < 0:
             times = 0
@@ -283,7 +280,7 @@
             i += j
         return newstr
 
-    @elidable
+    @jit.elidable
     def ll_char_mul(ch, times):
         if typeOf(ch) is Char:
             malloc = mallocstr
@@ -328,7 +325,7 @@
         return s
     ll_str2unicode.oopspec = 'str.str2unicode(str)'
 
-    @elidable
+    @jit.elidable
     def ll_strhash(s):
         # unlike CPython, there is no reason to avoid to return -1
         # but our malloc initializes the memory to zero, so we use zero as the
@@ -344,7 +341,7 @@
     def ll_strfasthash(s):
         return s.hash     # assumes that the hash is already computed
 
-    @elidable
+    @jit.elidable
     def ll_strconcat(s1, s2):
         len1 = len(s1.chars)
         len2 = len(s2.chars)
@@ -356,7 +353,7 @@
         return newstr
     ll_strconcat.oopspec = 'stroruni.concat(s1, s2)'
 
-    @elidable
+    @jit.elidable
     def ll_strip(s, ch, left, right):
         s_len = len(s.chars)
         if s_len == 0:
@@ -374,7 +371,7 @@
         s.copy_contents(s, result, lpos, 0, r_len)
         return result
 
-    @elidable
+    @jit.elidable
     def ll_upper(s):
         s_chars = s.chars
         s_len = len(s_chars)
@@ -391,7 +388,7 @@
             i += 1
         return result
 
-    @elidable
+    @jit.elidable
     def ll_lower(s):
         s_chars = s.chars
         s_len = len(s_chars)
@@ -441,7 +438,7 @@
             i += 1
         return result
 
-    @elidable
+    @jit.elidable
     def ll_strcmp(s1, s2):
         if not s1 and not s2:
             return True
@@ -464,7 +461,7 @@
             i += 1
         return len1 - len2
 
-    @elidable
+    @jit.elidable
     def ll_streq(s1, s2):
         if s1 == s2:       # also if both are NULLs
             return True
@@ -484,7 +481,7 @@
         return True
     ll_streq.oopspec = 'stroruni.equal(s1, s2)'
 
-    @elidable
+    @jit.elidable
     def ll_startswith(s1, s2):
         len1 = len(s1.chars)
         len2 = len(s2.chars)
@@ -505,7 +502,7 @@
             return False
         return s.chars[0] == ch
 
-    @elidable
+    @jit.elidable
     def ll_endswith(s1, s2):
         len1 = len(s1.chars)
         len2 = len(s2.chars)
@@ -527,7 +524,7 @@
             return False
         return s.chars[len(s.chars) - 1] == ch
 
-    @elidable
+    @jit.elidable
     def ll_find_char(s, ch, start, end):
         i = start
         if end > len(s.chars):
@@ -539,7 +536,7 @@
         return -1
     ll_find_char._annenforceargs_ = [None, None, int, int]
 
-    @elidable
+    @jit.elidable
     def ll_rfind_char(s, ch, start, end):
         if end > len(s.chars):
             end = len(s.chars)
@@ -550,7 +547,7 @@
                 return i
         return -1
 
-    @elidable
+    @jit.elidable
     def ll_count_char(s, ch, start, end):
         count = 0
         i = start
@@ -618,7 +615,7 @@
             res = 0
         return res
 
-    @elidable
+    @jit.elidable
     def ll_search(s1, s2, start, end, mode):
         count = 0
         n = end - start
@@ -697,7 +694,13 @@
             return -1
         return count
 
+    @jit.look_inside_iff(lambda length, items: jit.isconstant(length) and length <= 2)
+    @enforceargs(int, None)
     def ll_join_strs(length, items):
+        # Special case for length 1 items, helps both the JIT and other code
+        if length == 1:
+            return items[0]
+
         num_items = length
         itemslen = 0
         i = 0
@@ -724,8 +727,8 @@
             res_index += item_len
             i += 1
         return result
-    ll_join_strs._annenforceargs_ = [int, None]
 
+    @jit.look_inside_iff(lambda length, chars, RES: jit.isconstant(length) and jit.isvirtual(chars))
     def ll_join_chars(length, chars, RES):
         # no need to optimize this, will be replaced by string builder
         # at some point soon
@@ -744,7 +747,7 @@
             i += 1
         return result
 
-    @elidable
+    @jit.elidable
     def _ll_stringslice(s1, start, stop):
         lgt = stop - start
         assert start >= 0
@@ -759,7 +762,7 @@
         return LLHelpers._ll_stringslice(s1, start, len(s1.chars))
 
     def ll_stringslice_startstop(s1, start, stop):
-        if we_are_jitted():
+        if jit.we_are_jitted():
             if stop > len(s1.chars):
                 stop = len(s1.chars)
         else:
@@ -842,7 +845,7 @@
         item.copy_contents(s, item, j, 0, i - j)
         return res
 
-    @elidable
+    @jit.elidable
     def ll_replace_chr_chr(s, c1, c2):
         length = len(s.chars)
         newstr = s.malloc(length)
@@ -857,7 +860,7 @@
             j += 1
         return newstr
 
-    @elidable
+    @jit.elidable
     def ll_contains(s, c):
         chars = s.chars
         strlen = len(chars)
@@ -868,7 +871,7 @@
             i += 1
         return False
 
-    @elidable
+    @jit.elidable
     def ll_int(s, base):
         if not 2 <= base <= 36:
             raise ValueError
diff --git a/pypy/rpython/lltypesystem/test/test_ll2ctypes.py b/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
--- a/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
+++ b/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
@@ -81,6 +81,26 @@
         lltype.free(s, flavor='raw')
         assert not ALLOCATED     # detects memory leaks in the test
 
+    def test_get_pointer(self):
+        py.test.skip("FIXME")
+        # Equivalent of the C code::
+        #     struct S1 { struct S2 *ptr; struct S2 buf; };
+        #     struct S1 s1;
+        #     s1.ptr = & s1.buf;
+        S2 = lltype.Struct('S2', ('y', lltype.Signed))
+        S1 = lltype.Struct('S',
+                           ('sub', lltype.Struct('SUB', 
+                                                 ('ptr', lltype.Ptr(S2)))),
+                           ('ptr', lltype.Ptr(S2)),
+                           ('buf', S2), # Works when this field is first!
+                           )
+        s1 = lltype.malloc(S1, flavor='raw')
+        s1.ptr = s1.buf
+        s1.sub.ptr = s1.buf
+
+        x = rffi.cast(rffi.CCHARP, s1)
+        lltype.free(s1, flavor='raw')
+
     def test_struct_ptrs(self):
         S2 = lltype.Struct('S2', ('y', lltype.Signed))
         S1 = lltype.Struct('S', ('x', lltype.Signed), ('p', lltype.Ptr(S2)))
diff --git a/pypy/rpython/memory/gctransform/asmgcroot.py b/pypy/rpython/memory/gctransform/asmgcroot.py
--- a/pypy/rpython/memory/gctransform/asmgcroot.py
+++ b/pypy/rpython/memory/gctransform/asmgcroot.py
@@ -636,7 +636,8 @@
                                       ASM_FRAMEDATA_HEAD_PTR],
                                      lltype.Signed,
                                      sandboxsafe=True,
-                                     _nowrapper=True)
+                                     _nowrapper=True,
+                                     random_effects_on_gcobjs=True)
 c_asm_stackwalk = Constant(pypy_asm_stackwalk,
                            lltype.typeOf(pypy_asm_stackwalk))
 
@@ -662,4 +663,5 @@
                          QSORT_CALLBACK_PTR],
                         lltype.Void,
                         sandboxsafe=True,
+                        random_effects_on_gcobjs=False,  # but has a callback
                         _nowrapper=True)
diff --git a/pypy/rpython/memory/gctransform/framework.py b/pypy/rpython/memory/gctransform/framework.py
--- a/pypy/rpython/memory/gctransform/framework.py
+++ b/pypy/rpython/memory/gctransform/framework.py
@@ -41,7 +41,7 @@
                                                               seen)
     def analyze_external_call(self, op, seen=None):
         funcobj = op.args[0].value._obj
-        if funcobj._name == 'pypy_asm_stackwalk':
+        if getattr(funcobj, 'random_effects_on_gcobjs', False):
             return True
         return graphanalyze.GraphAnalyzer.analyze_external_call(self, op,
                                                                 seen)
@@ -626,8 +626,8 @@
         func = getattr(graph, 'func', None)
         if func and getattr(func, '_gc_no_collect_', False):
             if self.collect_analyzer.analyze_direct_call(graph):
-                raise Exception("no_collect function can trigger collection: %s"
-                                % func.__name__)
+                raise Exception("'no_collect' function can trigger collection:"
+                                " %s" % func)
             
         if self.write_barrier_ptr:
             self.clean_sets = (
@@ -812,6 +812,7 @@
                   resultvar=op.result)
 
     def gct_gc_shadowstackref_destroy(self, hop):
+        op = hop.spaceop
         hop.genop("direct_call",
                   [self.root_walker.gc_shadowstackref_destroy_ptr, op.args[0]])
 
diff --git a/pypy/rpython/memory/gctransform/test/test_framework.py b/pypy/rpython/memory/gctransform/test/test_framework.py
--- a/pypy/rpython/memory/gctransform/test/test_framework.py
+++ b/pypy/rpython/memory/gctransform/test/test_framework.py
@@ -139,7 +139,8 @@
     cbuild = CStandaloneBuilder(t, entrypoint, t.config,
                                 gcpolicy=FrameworkGcPolicy2)
     f = py.test.raises(Exception, cbuild.generate_graphs_for_llinterp)
-    assert str(f.value) == 'no_collect function can trigger collection: g'
+    expected = "'no_collect' function can trigger collection: <function g at "
+    assert str(f.value).startswith(expected)
 
 class WriteBarrierTransformer(FrameworkGCTransformer):
     clean_sets = {}
diff --git a/pypy/rpython/memory/lldict.py b/pypy/rpython/memory/lldict.py
--- a/pypy/rpython/memory/lldict.py
+++ b/pypy/rpython/memory/lldict.py
@@ -83,7 +83,7 @@
                        })
 DICT = lltype.Struct('DICT', ('entries', lltype.Ptr(ENTRIES)),
                              ('num_items', lltype.Signed),
-                             ('num_pristine_entries', lltype.Signed),
+                             ('resize_counter', lltype.Signed),
                      adtmeths = {
                          'allocate': dict_allocate,
                          'delete': dict_delete,
diff --git a/pypy/rpython/ootypesystem/rbuilder.py b/pypy/rpython/ootypesystem/rbuilder.py
--- a/pypy/rpython/ootypesystem/rbuilder.py
+++ b/pypy/rpython/ootypesystem/rbuilder.py
@@ -7,6 +7,9 @@
 MAX = 16*1024*1024
 
 class BaseBuilderRepr(AbstractStringBuilderRepr):
+    def empty(self):
+        return ootype.null(self.lowleveltype)
+    
     @classmethod
     def ll_new(cls, init_size):
         if init_size < 0 or init_size > MAX:
@@ -36,6 +39,10 @@
     def ll_build(builder):
         return builder.ll_build()
 
+    @staticmethod
+    def ll_is_true(builder):
+        return bool(builder)
+
 class StringBuilderRepr(BaseBuilderRepr):
     lowleveltype = ootype.StringBuilder
     string_repr = string_repr
diff --git a/pypy/rpython/ootypesystem/rdict.py b/pypy/rpython/ootypesystem/rdict.py
--- a/pypy/rpython/ootypesystem/rdict.py
+++ b/pypy/rpython/ootypesystem/rdict.py
@@ -247,7 +247,7 @@
         fn = None
         v_obj = hop.inputarg(r_func, arg=arg)
         s_pbc_fn = hop.args_s[arg]
-        methodname = r_func._get_method_name("simple_call", s_pbc_fn, params_annotation)
+        methodname = r_func._get_method_name("simple_call", s_pbc_fn, params_annotation, hop)
     elif isinstance(r_func, MethodOfFrozenPBCRepr):
         r_impl, nimplicitarg = r_func.get_r_implfunc()
         fn = r_impl.get_unique_llfn().value
diff --git a/pypy/rpython/ootypesystem/rlist.py b/pypy/rpython/ootypesystem/rlist.py
--- a/pypy/rpython/ootypesystem/rlist.py
+++ b/pypy/rpython/ootypesystem/rlist.py
@@ -124,7 +124,7 @@
         else:
             return ootype.List()
 
-    def _generate_newlist(self, llops, items_v):
+    def _generate_newlist(self, llops, items_v, v_sizehint):
         c_list = inputconst(ootype.Void, self.lowleveltype)
         v_result = llops.genop("new", [c_list], resulttype=self.lowleveltype)
         c_resize = inputconst(ootype.Void, "_ll_resize")
@@ -150,8 +150,8 @@
 
 
 
-def newlist(llops, r_list, items_v):
-    v_result = r_list._generate_newlist(llops, items_v)
+def newlist(llops, r_list, items_v, v_sizehint=None):
+    v_result = r_list._generate_newlist(llops, items_v, v_sizehint)
 
     c_setitem = inputconst(ootype.Void, "ll_setitem_fast")
     for i, v_item in enumerate(items_v):
@@ -224,7 +224,7 @@
     def make_iterator_repr(self):
         return ListIteratorRepr(self)
 
-    def _generate_newlist(self, llops, items_v):
+    def _generate_newlist(self, llops, items_v, v_sizehint):
         c_array = inputconst(ootype.Void, self.lowleveltype)
         c_length = inputconst(ootype.Signed, len(items_v))
         v_result = llops.genop("oonewarray", [c_array, c_length], resulttype=self.lowleveltype)
diff --git a/pypy/rpython/ootypesystem/rpbc.py b/pypy/rpython/ootypesystem/rpbc.py
--- a/pypy/rpython/ootypesystem/rpbc.py
+++ b/pypy/rpython/ootypesystem/rpbc.py
@@ -130,14 +130,14 @@
     def call(self, opname, hop):
         s_pbc = hop.args_s[0]   # possibly more precise than self.s_pbc        
         args_s = hop.args_s[1:]
-        shape, index, callfamily = self._get_shape_index_callfamily(opname, s_pbc, args_s)
+        shape, index, callfamily = self._get_shape_index_callfamily(opname, s_pbc, args_s, hop)
         row_of_graphs = callfamily.calltables[shape][index]
         anygraph = row_of_graphs.itervalues().next()  # pick any witness
         hop2 = self.add_instance_arg_to_hop(hop, opname == "call_args")
         vlist = callparse.callparse(self.rtyper, anygraph, hop2, opname,
                                     r_self = self.r_im_self)
         rresult = callparse.getrresult(self.rtyper, anygraph)
-        derived_mangled = self._get_method_name(opname, s_pbc, args_s)
+        derived_mangled = self._get_method_name(opname, s_pbc, args_s, hop)
         cname = hop.inputconst(ootype.Void, derived_mangled)
         hop.exception_is_here()
         # sanity check: make sure that INSTANCE has the method
@@ -151,18 +151,18 @@
         else:
             return hop.llops.convertvar(v, rresult, hop.r_result)
 
-    def _get_shape_index_callfamily(self, opname, s_pbc, args_s):
+    def _get_shape_index_callfamily(self, opname, s_pbc, args_s, hop):
         bk = self.rtyper.annotator.bookkeeper
         args = bk.build_args(opname, args_s)
         args = args.prepend(self.s_im_self)
         descs = [desc.funcdesc for desc in s_pbc.descriptions]
         callfamily = descs[0].getcallfamily()
         shape, index = description.FunctionDesc.variant_for_call_site(
-                bk, callfamily, descs, args)
+                bk, callfamily, descs, args, hop.spaceop)
         return shape, index, callfamily
 
-    def _get_method_name(self, opname, s_pbc, args_s):
-        shape, index, callfamily = self._get_shape_index_callfamily(opname, s_pbc, args_s)
+    def _get_method_name(self, opname, s_pbc, args_s, hop):
+        shape, index, callfamily = self._get_shape_index_callfamily(opname, s_pbc, args_s, hop)
         mangled = mangle(self.methodname, self.rtyper.getconfig())
         row = self.concretetable[shape, index]
         derived_mangled = row_method_name(mangled, row.attrname)
diff --git a/pypy/rpython/rbuilder.py b/pypy/rpython/rbuilder.py
--- a/pypy/rpython/rbuilder.py
+++ b/pypy/rpython/rbuilder.py
@@ -1,13 +1,11 @@
 
 from pypy.rpython.rmodel import Repr
-from pypy.rpython.annlowlevel import llhelper
 from pypy.rpython.lltypesystem import lltype
 from pypy.rlib.rstring import INIT_SIZE
 from pypy.annotation.model import SomeChar, SomeUnicodeCodePoint
 
 class AbstractStringBuilderRepr(Repr):
     def rtyper_new(self, hop):
-        repr = hop.r_result
         if len(hop.args_v) == 0:
             v_arg = hop.inputconst(lltype.Signed, INIT_SIZE)
         else:
@@ -50,3 +48,13 @@
         vlist = hop.inputargs(self)
         hop.exception_cannot_occur()
         return hop.gendirectcall(self.ll_build, *vlist)
+
+    def rtype_is_true(self, hop):
+        vlist = hop.inputargs(self)
+        hop.exception_cannot_occur()
+        return hop.gendirectcall(self.ll_is_true, *vlist)
+        
+    def convert_const(self, value):
+        if not value is None:
+            raise TypeError("Prebuilt builedrs that are not none unsupported")
+        return self.empty()
diff --git a/pypy/rpython/rlist.py b/pypy/rpython/rlist.py
--- a/pypy/rpython/rlist.py
+++ b/pypy/rpython/rlist.py
@@ -2,7 +2,7 @@
 from pypy.objspace.flow.model import Constant
 from pypy.annotation import model as annmodel
 from pypy.rpython.error import TyperError
-from pypy.rpython.rmodel import Repr, IteratorRepr, IntegerRepr, inputconst
+from pypy.rpython.rmodel import Repr, IteratorRepr, IntegerRepr
 from pypy.rpython.rstr import AbstractStringRepr, AbstractCharRepr
 from pypy.rpython.lltypesystem.lltype import typeOf, Ptr, Void, Signed, Bool
 from pypy.rpython.lltypesystem.lltype import nullptr, Char, UniChar, Number
@@ -116,7 +116,7 @@
         v_lst = hop.inputarg(self, 0)
         cRESLIST = hop.inputconst(Void, hop.r_result.LIST)
         return hop.gendirectcall(ll_copy, cRESLIST, v_lst)
-    
+
     def rtype_len(self, hop):
         v_lst, = hop.inputargs(self)
         if hop.args_s[0].listdef.listitem.resized:
@@ -132,7 +132,7 @@
         else:
             ll_func = ll_list_is_true_foldable
         return hop.gendirectcall(ll_func, v_lst)
-    
+
     def rtype_method_reverse(self, hop):
         v_lst, = hop.inputargs(self)
         hop.exception_cannot_occur()
@@ -273,7 +273,7 @@
         return pair(r_lst, r_int).rtype_getitem(hop, checkidx=True)
 
     rtype_getitem_idx_key = rtype_getitem_idx
-    
+
     def rtype_setitem((r_lst, r_int), hop):
         if hop.has_implicit_exception(IndexError):
             spec = dum_checkidx
@@ -331,7 +331,7 @@
 ##            return hop.gendirectcall(ll_both_none, v_lst1, v_lst2)
 
 ##        return pairtype(Repr, Repr).rtype_is_(pair(r_lst1, r_lst2), hop)
- 
+
     def rtype_eq((r_lst1, r_lst2), hop):
         assert r_lst1.item_repr == r_lst2.item_repr
         v_lst1, v_lst2 = hop.inputargs(r_lst1, r_lst2)
@@ -344,7 +344,7 @@
         return hop.genop('bool_not', [flag], resulttype=Bool)
 
 
-def rtype_newlist(hop):
+def rtype_newlist(hop, v_sizehint=None):
     nb_args = hop.nb_args
     r_list = hop.r_result
     if r_list == robject.pyobj_repr: # special case: SomeObject lists!
@@ -358,7 +358,8 @@
         return v_result
     r_listitem = r_list.item_repr
     items_v = [hop.inputarg(r_listitem, arg=i) for i in range(nb_args)]
-    return hop.rtyper.type_system.rlist.newlist(hop.llops, r_list, items_v)
+    return hop.rtyper.type_system.rlist.newlist(hop.llops, r_list, items_v,
+                                                v_sizehint=v_sizehint)
 
 def rtype_alloc_and_set(hop):
     r_list = hop.r_result
@@ -498,7 +499,7 @@
     else:
         check = item
     if (not malloc_zero_filled) or check: # as long as malloc it is known to zero the allocated memory avoid zeroing twice
-    
+
         i = 0
         while i < count:
             l.ll_setitem_fast(i, item)
@@ -632,7 +633,6 @@
         l.ll_setitem_fast(index, null)
     l._ll_resize_le(newlength)
     return res
-ll_pop_default.oopspec = 'list.pop(l)'
 
 def ll_pop_zero(func, l):
     length = l.ll_length()
diff --git a/pypy/rpython/rpbc.py b/pypy/rpython/rpbc.py
--- a/pypy/rpython/rpbc.py
+++ b/pypy/rpython/rpbc.py
@@ -322,7 +322,8 @@
         args = bk.build_args(opname, hop.args_s[1:])
         s_pbc = hop.args_s[0]   # possibly more precise than self.s_pbc
         descs = list(s_pbc.descriptions)
-        shape, index = description.FunctionDesc.variant_for_call_site(bk, self.callfamily, descs, args)
+        vfcs = description.FunctionDesc.variant_for_call_site
+        shape, index = vfcs(bk, self.callfamily, descs, args, hop.spaceop)
         row_of_graphs = self.callfamily.calltables[shape][index]
         anygraph = row_of_graphs.itervalues().next()  # pick any witness
         vfn = hop.inputarg(self, arg=0)
diff --git a/pypy/rpython/test/test_rbuilder.py b/pypy/rpython/test/test_rbuilder.py
--- a/pypy/rpython/test/test_rbuilder.py
+++ b/pypy/rpython/test/test_rbuilder.py
@@ -84,6 +84,41 @@
         res = self.ll_to_string(self.interpret(func, [5]))
         assert res == "hello"
 
+    def test_builder_or_none(self):
+        def g(s):
+            if s:
+                s.append("3")
+            return bool(s)
+        
+        def func(i):
+            if i:
+                s = StringBuilder()
+            else:
+                s = None
+            return g(s)
+        res = self.interpret(func, [0])
+        assert not res
+        res = self.interpret(func, [1])
+        assert res
+
+    def test_unicode_builder_or_none(self):
+        def g(s):
+            if s:
+                s.append(u"3")
+            return bool(s)
+        
+        def func(i):
+            if i:
+                s = UnicodeBuilder()
+            else:
+                s = None
+            return g(s)
+        res = self.interpret(func, [0])
+        assert not res
+        res = self.interpret(func, [1])
+        assert res
+
+
 class TestLLtype(BaseTestStringBuilder, LLRtypeMixin):
     pass
 
@@ -93,4 +128,4 @@
     def test_unicode_getlength(self):
         py.test.skip("getlength(): not implemented on ootype")
     def test_append_charpsize(self):
-        py.test.skip("append_charpsize(): not implemented on ootype")
\ No newline at end of file
+        py.test.skip("append_charpsize(): not implemented on ootype")
diff --git a/pypy/rpython/test/test_rint.py b/pypy/rpython/test/test_rint.py
--- a/pypy/rpython/test/test_rint.py
+++ b/pypy/rpython/test/test_rint.py
@@ -18,8 +18,8 @@
         t = TranslationContext()
         t.buildannotator().build_types(func, types)
         t.buildrtyper().specialize()
-        t.checkgraphs()    
-     
+        t.checkgraphs()
+
     def test_not1(self):
         self._test(snippet.not1, [int])
 
@@ -44,7 +44,7 @@
 
 
 class BaseTestRint(BaseRtypingTest):
-    
+
     def test_char_constant(self):
         def dummyfn(i):
             return chr(i)
diff --git a/pypy/rpython/test/test_rlist.py b/pypy/rpython/test/test_rlist.py
--- a/pypy/rpython/test/test_rlist.py
+++ b/pypy/rpython/test/test_rlist.py
@@ -1360,6 +1360,20 @@
             assert ('foldable' in func.func_name) == \
                    ("y[*]" in immutable_fields)
 
+    def test_hints(self):
+        from pypy.rlib.objectmodel import newlist
+        from pypy.rpython.annlowlevel import hlstr
+
+        strings = ['abc', 'def']
+        def f(i):
+            z = strings[i]
+            x = newlist(sizehint=13)
+            x += z
+            return ''.join(x)
+
+        res = self.interpret(f, [0])
+        assert self.ll_to_string(res) == 'abc'
+
 class TestLLtype(BaseTestRlist, LLRtypeMixin):
     type_system = 'lltype'
     rlist = ll_rlist
diff --git a/pypy/tool/logparser.py b/pypy/tool/logparser.py
--- a/pypy/tool/logparser.py
+++ b/pypy/tool/logparser.py
@@ -75,9 +75,9 @@
     if verbose:
         sys.stderr.write('loaded\n')
     if performance_log and time_decrase:
-        raise Exception("The time decreases!  The log file may have been"
-                        " produced on a multi-CPU machine and the process"
-                        " moved between CPUs.")
+        print ("The time decreases!  The log file may have been"
+               " produced on a multi-CPU machine and the process"
+               " moved between CPUs.")
     return log
 
 def extract_category(log, catprefix='', toplevel=False):
diff --git a/pypy/tool/release/package.py b/pypy/tool/release/package.py
--- a/pypy/tool/release/package.py
+++ b/pypy/tool/release/package.py
@@ -52,14 +52,15 @@
             pypy_c_dir = basedir.join('pypy', 'translator', 'goal')
         pypy_c = pypy_c_dir.join('pypy-c.exe')
         libpypy_c = pypy_c_dir.join('libpypy-c.dll')
-        libexpat = pypy_c_dir.join('libexpat.dll')
-        if not libexpat.check():
-            libexpat = py.path.local.sysfind('libexpat.dll')
-            assert libexpat, "libexpat.dll not found"
-            print "Picking %s" % libexpat
         binaries = [(pypy_c, pypy_c.basename),
-                    (libpypy_c, libpypy_c.basename),
-                    (libexpat, libexpat.basename)]
+                    (libpypy_c, libpypy_c.basename)]
+        for extra in ['libexpat.dll', 'sqlite3.dll']:
+            p = pypy_c_dir.join(extra)
+            if not p.check():
+                p = py.path.local.sysfind(extra)
+                assert p, "%s not found" % (extra,)
+            print "Picking %s" % p
+            binaries.append((p, p.basename))
     else:


More information about the pypy-commit mailing list