[pypy-commit] pypy rffi-parser-2: hg merge default

rlamy pypy.commits at gmail.com
Mon Jan 16 20:12:25 EST 2017


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: rffi-parser-2
Changeset: r89623:ebbd6a0d8773
Date: 2017-01-17 01:11 +0000
http://bitbucket.org/pypy/pypy/changeset/ebbd6a0d8773/

Log:	hg merge default

diff --git a/lib-python/2.7/sqlite3/test/regression.py b/lib-python/2.7/sqlite3/test/regression.py
--- a/lib-python/2.7/sqlite3/test/regression.py
+++ b/lib-python/2.7/sqlite3/test/regression.py
@@ -351,10 +351,7 @@
         self.assertRaises(ValueError, cur.execute, " \0select 2")
         self.assertRaises(ValueError, cur.execute, "select 2\0")
 
-    @test_support.impl_detail(pypy=False)
     def CheckCommitCursorReset(self):
-        # This test is for logic added in 2.7.13 which PyPy doesn't
-        # implement.  See http://bugs.python.org/issue29006
         """
         Connection.commit() did reset cursors, which made sqlite3
         to return rows multiple times when fetched from cursors
diff --git a/lib-python/2.7/test/test_capi.py b/lib-python/2.7/test/test_capi.py
--- a/lib-python/2.7/test/test_capi.py
+++ b/lib-python/2.7/test/test_capi.py
@@ -26,7 +26,6 @@
 skips = []
 if support.check_impl_detail(pypy=True):
     skips += [
-            'test_broken_memoryview',
             'test_buildvalue_N',
             'test_capsule',
             'test_lazy_hash_inheritance',
diff --git a/lib-python/2.7/test/test_multiprocessing.py b/lib-python/2.7/test/test_multiprocessing.py
--- a/lib-python/2.7/test/test_multiprocessing.py
+++ b/lib-python/2.7/test/test_multiprocessing.py
@@ -1969,9 +1969,10 @@
         if not gc.isenabled():
             gc.enable()
             self.addCleanup(gc.disable)
-        #thresholds = gc.get_threshold()
-        #self.addCleanup(gc.set_threshold, *thresholds)
-        #gc.set_threshold(10)
+        if test_support.check_impl_detail(cpython=True):
+            thresholds = gc.get_threshold()
+            self.addCleanup(gc.set_threshold, *thresholds)
+            gc.set_threshold(10)
 
         # perform numerous block allocations, with cyclic references to make
         # sure objects are collected asynchronously by the gc
diff --git a/lib_pypy/_sqlite3.py b/lib_pypy/_sqlite3.py
--- a/lib_pypy/_sqlite3.py
+++ b/lib_pypy/_sqlite3.py
@@ -220,6 +220,7 @@
         self.__statements_counter = 0
         self.__rawstatements = set()
         self._statement_cache = _StatementCache(self, cached_statements)
+        self.__statements_already_committed = []
 
         self.__func_cache = {}
         self.__aggregates = {}
@@ -363,6 +364,14 @@
                 if cursor is not None:
                     cursor._reset = True
 
+    def _reset_already_committed_statements(self):
+        lst = self.__statements_already_committed
+        self.__statements_already_committed = []
+        for weakref in lst:
+            statement = weakref()
+            if statement is not None:
+                statement._reset()
+
     @_check_thread_wrap
     @_check_closed_wrap
     def __call__(self, sql):
@@ -418,15 +427,18 @@
         if not self._in_transaction:
             return
 
-        # The following line is a KNOWN DIFFERENCE with CPython 2.7.13.
-        # More precisely, the corresponding line was removed in the
-        # version 2.7.13 of CPython, but this is causing troubles for
-        # PyPy (and potentially for CPython too):
-        #
-        #     http://bugs.python.org/issue29006
-        #
-        # So for now, we keep this line.
-        self.__do_all_statements(Statement._reset, False)
+        # PyPy fix for non-refcounting semantics: since 2.7.13 (and in
+        # <= 2.6.x), the statements are not automatically reset upon
+        # commit.  However, if this is followed by some specific SQL
+        # operations like "drop table", these open statements come in
+        # the way and cause the "drop table" to fail.  On CPython the
+        # problem is much less important because typically all the old
+        # statements are freed already by reference counting.  So here,
+        # we copy all the still-alive statements to another list which
+        # is usually ignored, except if we get SQLITE_LOCKED
+        # afterwards---at which point we reset all statements in this
+        # list.
+        self.__statements_already_committed = self.__statements[:]
 
         statement_star = _ffi.new('sqlite3_stmt **')
         ret = _lib.sqlite3_prepare_v2(self._db, b"COMMIT", -1,
@@ -827,8 +839,18 @@
                 self.__statement._set_params(params)
 
                 # Actually execute the SQL statement
+
                 ret = _lib.sqlite3_step(self.__statement._statement)
 
+                # PyPy: if we get SQLITE_LOCKED, it's probably because
+                # one of the cursors created previously is still alive
+                # and not reset and the operation we're trying to do
+                # makes Sqlite unhappy about that.  In that case, we
+                # automatically reset all old cursors and try again.
+                if ret == _lib.SQLITE_LOCKED:
+                    self.__connection._reset_already_committed_statements()
+                    ret = _lib.sqlite3_step(self.__statement._statement)
+
                 if ret == _lib.SQLITE_ROW:
                     if multiple:
                         raise ProgrammingError("executemany() can only execute DML statements.")
diff --git a/lib_pypy/_testcapimodule.c b/lib_pypy/_testcapimodule.c
--- a/lib_pypy/_testcapimodule.c
+++ b/lib_pypy/_testcapimodule.c
@@ -2780,6 +2780,9 @@
     m = Py_InitModule("_testcapi", TestMethods);
     if (m == NULL)
         return;
+    
+    if (PyType_Ready(&_MemoryViewTester_Type) < 0)
+        return;
 
     Py_TYPE(&_HashInheritanceTester_Type)=&PyType_Type;
 
diff --git a/lib_pypy/audioop.py b/lib_pypy/audioop.py
--- a/lib_pypy/audioop.py
+++ b/lib_pypy/audioop.py
@@ -374,7 +374,7 @@
 
     sample_count = _sample_count(cp, size)
 
-    rv = ffi.new("unsigned char[]", len(cp) * 2)
+    rv = ffi.new("char[]", len(cp) * 2)
     lib.tostereo(rv, cp, len(cp), size, fac1, fac2)
     return ffi.buffer(rv)[:]
 
@@ -385,7 +385,7 @@
     if len(cp1) != len(cp2):
         raise error("Lengths should be the same")
 
-    rv = ffi.new("unsigned char[]", len(cp1))
+    rv = ffi.new("char[]", len(cp1))
     lib.add(rv, cp1, cp2, len(cp1), size)
     return ffi.buffer(rv)[:]
 
@@ -488,7 +488,7 @@
     ceiling = (q + 1) * outrate
     nbytes = ceiling * bytes_per_frame
 
-    rv = ffi.new("unsigned char[]", nbytes)
+    rv = ffi.new("char[]", nbytes)
     trim_index = lib.ratecv(rv, cp, frame_count, size,
                             nchannels, inrate, outrate,
                             state_d, prev_i, cur_i,
diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst
--- a/pypy/doc/cpython_differences.rst
+++ b/pypy/doc/cpython_differences.rst
@@ -483,11 +483,6 @@
   the rest is kept.  If you return an unexpected string from
   ``__hex__()`` you get an exception (or a crash before CPython 2.7.13).
 
-* The ``sqlite`` module was updated on 2.7.13 to no longer reset all
-  cursors when there is a commit.  This causes troubles for PyPy (and
-  potentially for CPython too), and so for now we didn't port this change:
-  see http://bugs.python.org/issue29006.
-
 .. _`is ignored in PyPy`: http://bugs.python.org/issue14621
 .. _`little point`: http://events.ccc.de/congress/2012/Fahrplan/events/5152.en.html
 .. _`#2072`: https://bitbucket.org/pypy/pypy/issue/2072/
diff --git a/pypy/doc/faq.rst b/pypy/doc/faq.rst
--- a/pypy/doc/faq.rst
+++ b/pypy/doc/faq.rst
@@ -26,16 +26,16 @@
 
 Almost!
 
-The mostly likely stumbling block for any given project is support for
+The most likely stumbling block for any given project is support for
 :ref:`extension modules <extension-modules>`.  PyPy supports a continually growing
 number of extension modules, but so far mostly only those found in the
 standard library.
 
 The language features (including builtin types and functions) are very
-complete and well tested, so if your project does not use many
+refined and well tested, so if your project doesn't use many
 extension modules there is a good chance that it will work with PyPy.
 
-We list the differences we know about in :doc:`cpython differences <cpython_differences>`.
+We list the known differences in :doc:`cpython differences <cpython_differences>`.
 
 
 Module xyz does not work with PyPy: ImportError
@@ -76,10 +76,10 @@
 
 You cannot import *any* extension module in a `sandboxed PyPy`_,
 sorry.  Even the built-in modules available are very limited.
-Sandboxing in PyPy is a good proof of concept, really safe IMHO, but
-it is only a proof of concept.  It seriously requires someone working
-on it.  Before this occurs, it can only be used it for "pure Python"
-examples: programs that import mostly nothing (or only pure Python
+Sandboxing in PyPy is a good proof of concept, and is without a doubt
+safe IMHO, however it is only a proof of concept.  It currently requires 
+some work from a motivated developer. However, until then it can only be used for "pure Python"
+example: programs that import mostly nothing (or only pure Python
 modules, recursively).
 
 .. _`sandboxed PyPy`: sandbox.html
diff --git a/pypy/doc/getting-started-dev.rst b/pypy/doc/getting-started-dev.rst
--- a/pypy/doc/getting-started-dev.rst
+++ b/pypy/doc/getting-started-dev.rst
@@ -336,6 +336,6 @@
    that fixes some bugs and is translatable.
 
 *  :source:`pypy/objspace/std` contains the :ref:`Standard object space <standard-object-space>`.  The main file
-   is :source:`pypy/objspace/std/objspace.py`.  For each type, the files ``xxxtype.py`` and
-   ``xxxobject.py`` contain respectively the definition of the type and its
-   (default) implementation.
+   is :source:`pypy/objspace/std/objspace.py`.  For each type, the file
+   ``xxxobject.py`` contains the implementation for objects of type ``xxx``,
+   as a first approximation.  (Some types have multiple implementations.)
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -101,3 +101,21 @@
 
 Do not recreate the object in PyMemoryView_FromBuffer, rather pass it to
 the returned PyMemoryViewObject, to take ownership of it. Fixes a ref leak.
+
+.. branch: issue2464
+
+Give (almost?) all GetSetProperties a valid __objclass__.
+
+.. branch: TreeStain/fixed-typo-line-29-mostly-to-most-1484469416419
+.. branch: TreeStain/main-lines-changed-in-l77-l83-made-para-1484471558033
+
+.. branch: missing-tp_new
+
+Improve mixing app-level classes in c-extensions, especially if the app-level
+class has a ``tp_new`` or ``tp_dealloc``. The issue is that c-extensions expect
+all the method slots to be filled with a function pointer, where app-level will
+search up the mro for an appropriate function at runtime. With this branch we
+now fill many more slots in the c-extenion type objects.
+Also fix for c-extension type that calls ``tp_hash`` during initialization
+(str, unicode types), and fix instantiating c-extension types from built-in
+classes by enforcing an order of instaniation.
diff --git a/pypy/interpreter/test/test_typedef.py b/pypy/interpreter/test/test_typedef.py
--- a/pypy/interpreter/test/test_typedef.py
+++ b/pypy/interpreter/test/test_typedef.py
@@ -140,7 +140,11 @@
             pass
         def fget(self, space, w_self):
             assert self is prop
-        prop = typedef.GetSetProperty(fget, use_closure=True)
+        # NB. this GetSetProperty is not copied when creating the
+        # W_TypeObject because of 'cls'.  Without it, a duplicate of the
+        # GetSetProperty is taken and it is given the w_objclass that is
+        # the W_TypeObject
+        prop = typedef.GetSetProperty(fget, use_closure=True, cls=W_SomeType)
         W_SomeType.typedef = typedef.TypeDef(
             'some_type',
             x=prop)
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -276,12 +276,15 @@
         self.use_closure = use_closure
 
     def copy_for_type(self, w_objclass):
-        new = instantiate(GetSetProperty)
-        new._init(self.fget, self.fset, self.fdel, self.doc, self.reqcls,
-                  None, self.use_closure)
-        new.name = self.name
-        new.w_objclass = w_objclass
-        return new
+        if self.objclass_getter is None:
+            new = instantiate(GetSetProperty)
+            new._init(self.fget, self.fset, self.fdel, self.doc, self.reqcls,
+                      None, self.use_closure)
+            new.name = self.name
+            new.w_objclass = w_objclass
+            return new
+        else:
+            return self
 
     @unwrap_spec(w_cls = WrappedDefault(None))
     def descr_property_get(self, space, w_obj, w_cls=None):
@@ -483,7 +486,7 @@
     return lifeline.get_any_weakref(space)
 
 dict_descr = GetSetProperty(descr_get_dict, descr_set_dict, descr_del_dict,
-                            doc="dictionary for instance variables")
+                            doc="dictionary for instance variables (if defined)")
 dict_descr.name = '__dict__'
 
 
@@ -515,7 +518,7 @@
     return space.newtuple([w_docstring])
 
 weakref_descr = GetSetProperty(descr_get_weakref,
-                               doc="list of weak references to the object")
+                    doc="list of weak references to the object (if defined)")
 weakref_descr.name = '__weakref__'
 
 def make_weakref_descr(cls):
diff --git a/pypy/module/_ssl/test/test_ssl.py b/pypy/module/_ssl/test/test_ssl.py
--- a/pypy/module/_ssl/test/test_ssl.py
+++ b/pypy/module/_ssl/test/test_ssl.py
@@ -194,6 +194,17 @@
         from ..interp_ssl import SOCKET_STORAGE
         SOCKET_STORAGE._dict.clear()
 
+    def test_warmup_connection(self):
+        # not sure it is gmail.com's fault, but on some machines the
+        # very first connection attempt fails.  So we make one here and
+        # ignore the result.  The first real test is test_connect().
+        import socket, ssl
+        try:
+            ss = socket.ssl(self.s)
+            self.s.close()
+        except ssl.SSLError:
+            pass
+
     def test_connect(self):
         import socket, gc
         ss = socket.ssl(self.s)
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -40,6 +40,7 @@
 from rpython.rlib import rawrefcount
 from rpython.rlib import rthread
 from rpython.rlib.debug import fatalerror_notb
+from pypy.objspace.std.typeobject import W_TypeObject, find_best_base
 from pypy.module.cpyext.cparser import CTypeSpace
 
 DEBUG_WRAPPER = True
@@ -906,6 +907,7 @@
         retval = fatal_value
         boxed_args = ()
         tb = None
+        state = space.fromcache(State)
         try:
             if not we_are_translated() and DEBUG_WRAPPER:
                 print >>sys.stderr, callable,
@@ -924,7 +926,6 @@
                 boxed_args += (arg_conv, )
             if pygilstate_ensure:
                 boxed_args += (args[-1], )
-            state = space.fromcache(State)
             try:
                 result = callable(space, *boxed_args)
                 if not we_are_translated() and DEBUG_WRAPPER:
@@ -970,6 +971,7 @@
         except Exception as e:
             unexpected_exception(pname, e, tb)
             _restore_gil_state(pygilstate_release, gilstate, gil_release, _gil_auto, tid)
+            state.check_and_raise_exception(always=True)
             return fatal_value
 
         assert lltype.typeOf(retval) == restype
@@ -1130,6 +1132,34 @@
     setup_init_functions(eci, prefix)
     return modulename.new(ext='')
 
+def attach_recursively(space, static_pyobjs, static_objs_w, attached_objs, i):
+    # Start at i but make sure all the base classes are already attached
+    from pypy.module.cpyext.pyobject import get_typedescr, make_ref
+    if i in attached_objs:
+        return
+    py_obj = static_pyobjs[i]
+    w_obj = static_objs_w[i]
+    w_base = None
+    # w_obj can be NotImplemented, which is not a W_TypeObject
+    if isinstance(w_obj, W_TypeObject):
+        bases_w = w_obj.bases_w
+        if bases_w:
+            w_base = find_best_base(bases_w)
+        if w_base:
+            try:
+                j = static_objs_w.index(w_base)
+            except ValueError:
+                j = -1
+            if j >=0 and j not in attached_objs:
+                attach_recursively(space, static_pyobjs, static_objs_w,
+                                                 attached_objs, j)
+    w_type = space.type(w_obj)
+    typedescr = get_typedescr(w_type.layout.typedef)
+    py_obj.c_ob_type = rffi.cast(PyTypeObjectPtr,
+                                 make_ref(space, w_type))
+    typedescr.attach(space, py_obj, w_obj)
+    attached_objs.append(i)
+
 
 class StaticObjectBuilder(object):
     def __init__(self):
@@ -1150,7 +1180,6 @@
 
     def attach_all(self, space):
         # this is RPython, called once in pypy-c when it imports cpyext
-        from pypy.module.cpyext.pyobject import get_typedescr, make_ref
         from pypy.module.cpyext.typeobject import finish_type_1, finish_type_2
         from pypy.module.cpyext.pyobject import track_reference
         #
@@ -1160,14 +1189,9 @@
             track_reference(space, static_pyobjs[i], static_objs_w[i])
         #
         self.cpyext_type_init = []
+        attached_objs = []
         for i in range(len(static_objs_w)):
-            py_obj = static_pyobjs[i]
-            w_obj = static_objs_w[i]
-            w_type = space.type(w_obj)
-            typedescr = get_typedescr(w_type.layout.typedef)
-            py_obj.c_ob_type = rffi.cast(PyTypeObjectPtr,
-                                         make_ref(space, w_type))
-            typedescr.attach(space, py_obj, w_obj)
+            attach_recursively(space, static_pyobjs, static_objs_w, attached_objs, i)
         cpyext_type_init = self.cpyext_type_init
         self.cpyext_type_init = None
         for pto, w_type in cpyext_type_init:
diff --git a/pypy/module/cpyext/bytesobject.py b/pypy/module/cpyext/bytesobject.py
--- a/pypy/module/cpyext/bytesobject.py
+++ b/pypy/module/cpyext/bytesobject.py
@@ -80,14 +80,18 @@
     """
     py_str = rffi.cast(PyBytesObject, py_obj)
     s = space.str_w(w_obj)
-    if py_str.c_ob_size  < len(s):
+    len_s = len(s)
+    if py_str.c_ob_size  < len_s:
         raise oefmt(space.w_ValueError,
             "bytes_attach called on object with ob_size %d but trying to store %d",
-            py_str.c_ob_size, len(s))
+            py_str.c_ob_size, len_s)
     with rffi.scoped_nonmovingbuffer(s) as s_ptr:
-        rffi.c_memcpy(py_str.c_ob_sval, s_ptr, len(s))
-    py_str.c_ob_sval[len(s)] = '\0'
-    py_str.c_ob_shash = space.hash_w(w_obj)
+        rffi.c_memcpy(py_str.c_ob_sval, s_ptr, len_s)
+    py_str.c_ob_sval[len_s] = '\0'
+    # if py_obj has a tp_hash, this will try to call it, but the objects are
+    # not fully linked yet
+    #py_str.c_ob_shash = space.hash_w(w_obj)
+    py_str.c_ob_shash = space.hash_w(space.newbytes(s))
     py_str.c_ob_sstate = rffi.cast(rffi.INT, 1) # SSTATE_INTERNED_MORTAL
 
 def bytes_realize(space, py_obj):
@@ -100,7 +104,9 @@
     w_type = from_ref(space, rffi.cast(PyObject, py_obj.c_ob_type))
     w_obj = space.allocate_instance(W_BytesObject, w_type)
     w_obj.__init__(s)
-    py_str.c_ob_shash = space.hash_w(w_obj)
+    # if py_obj has a tp_hash, this will try to call it but the object is
+    # not realized yet
+    py_str.c_ob_shash = space.hash_w(space.newbytes(s))
     py_str.c_ob_sstate = rffi.cast(rffi.INT, 1) # SSTATE_INTERNED_MORTAL
     track_reference(space, py_obj, w_obj)
     return w_obj
diff --git a/pypy/module/cpyext/listobject.py b/pypy/module/cpyext/listobject.py
--- a/pypy/module/cpyext/listobject.py
+++ b/pypy/module/cpyext/listobject.py
@@ -90,11 +90,10 @@
     return 0
 
 @cpython_api([rffi.VOIDP], Py_ssize_t, error=CANNOT_FAIL)
-def PyList_GET_SIZE(space, w_list):
+def PyList_GET_SIZE(space, w_obj):
     """Macro form of PyList_Size() without error checking.
     """
-    assert isinstance(w_list, W_ListObject)
-    return w_list.length()
+    return space.len_w(w_obj)
 
 
 @cpython_api([PyObject], Py_ssize_t, error=-1)
diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py
--- a/pypy/module/cpyext/slotdefs.py
+++ b/pypy/module/cpyext/slotdefs.py
@@ -18,6 +18,7 @@
 from pypy.module.cpyext.pyerrors import PyErr_Occurred
 from pypy.module.cpyext.memoryobject import fill_Py_buffer
 from pypy.module.cpyext.state import State
+from pypy.module.cpyext import userslot
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.argument import Arguments
 from rpython.rlib.buffer import Buffer
@@ -512,6 +513,10 @@
 
 @specialize.memo()
 def get_slot_tp_function(space, typedef, name):
+    """Return a description of the slot C function to use for the built-in
+    type for 'typedef'.  The 'name' is the slot name.  This is a memo
+    function that, after translation, returns one of a built-in finite set.
+    """
     key = (typedef, name)
     try:
         return SLOTS[key]
@@ -551,6 +556,19 @@
                 return space.call_function(slot_fn, w_self)
             handled = True
 
+    for tp_name, attr in [('tp_hash', '__hash__'),
+                         ]:
+        if name == tp_name:
+            slot_fn = w_type.getdictvalue(space, attr)
+            if slot_fn is None:
+                return
+            @slot_function([PyObject], lltype.Signed, error=-1)
+            @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name))
+            def slot_func(space, w_obj):
+                return space.int_w(space.call_function(slot_fn, w_obj))
+            handled = True
+
+
     # binary functions
     for tp_name, attr in [('tp_as_number.c_nb_add', '__add__'),
                           ('tp_as_number.c_nb_subtract', '__sub__'),
@@ -755,7 +773,7 @@
         else:
             assert False
 
-    function = globals().get(FUNCTION, None)
+    function = getattr(userslot, FUNCTION or '!missing', None)
     assert FLAGS == 0 or FLAGS == PyWrapperFlag_KEYWORDS
     if FLAGS:
         if wrapper is Ellipsis:
@@ -818,7 +836,7 @@
 static slotdef slotdefs[] = {
         SQSLOT("__len__", sq_length, slot_sq_length, wrap_lenfunc,
                "x.__len__() <==> len(x)"),
-        SQSLOT("__add__", sq_concat, NULL, wrap_binaryfunc,
+        SQSLOT("__add__", sq_concat, slot_sq_concat, wrap_binaryfunc,
           "x.__add__(y) <==> x+y"),
         SQSLOT("__mul__", sq_repeat, NULL, wrap_indexargfunc,
           "x.__mul__(n) <==> x*n"),
@@ -966,9 +984,7 @@
                "x.__call__(...) <==> x(...)", PyWrapperFlag_KEYWORDS),
         TPSLOT("__getattribute__", tp_getattro, slot_tp_getattr_hook,
                wrap_binaryfunc, "x.__getattribute__('name') <==> x.name"),
-        TPSLOT("__getattribute__", tp_getattr, NULL, NULL, ""),
-        TPSLOT("__getattr__", tp_getattro, slot_tp_getattr_hook, NULL, ""),
-        TPSLOT("__getattr__", tp_getattr, NULL, NULL, ""),
+        TPSLOT("__getattr__", tp_getattro, slot_tp_getattr, NULL, ""),
         TPSLOT("__setattr__", tp_setattro, slot_tp_setattro, wrap_setattr,
                "x.__setattr__('name', value) <==> x.name = value"),
         TPSLOT("__setattr__", tp_setattr, NULL, NULL, ""),
diff --git a/pypy/module/cpyext/test/foo3.c b/pypy/module/cpyext/test/foo3.c
--- a/pypy/module/cpyext/test/foo3.c
+++ b/pypy/module/cpyext/test/foo3.c
@@ -8,6 +8,24 @@
     return newType;
 }
 
+PyObject * typ = NULL;
+PyObject* datetimetype_tp_new(PyTypeObject* metatype, PyObject* args, PyObject* kwds)
+{
+    PyObject* newType;
+    if (typ == NULL)
+    {
+        PyErr_Format(PyExc_TypeError, "could not import datetime.datetime");
+        return NULL;
+    }
+    newType = ((PyTypeObject*)typ)->tp_new(metatype, args, kwds);
+    return newType;
+}
+
+void datetimetype_tp_dealloc(PyObject* self)
+{
+    return ((PyTypeObject*)typ)->tp_dealloc(self);
+}
+
 #define BASEFLAGS Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES
 
 PyTypeObject footype = {
@@ -58,6 +76,54 @@
     /*tp_weaklist*/         0
 };
 
+PyTypeObject datetimetype = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    /*tp_name*/             "foo3.datetimetype",
+    /*tp_basicsize*/        sizeof(PyTypeObject),
+    /*tp_itemsize*/         0,
+    /*tp_dealloc*/          datetimetype_tp_dealloc,
+    /*tp_print*/            0,
+    /*tp_getattr*/          0,
+    /*tp_setattr*/          0,
+    /*tp_compare*/          0,
+    /*tp_repr*/             0,
+    /*tp_as_number*/        0,
+    /*tp_as_sequence*/      0,
+    /*tp_as_mapping*/       0,
+    /*tp_hash*/             0,
+    /*tp_call*/             0,
+    /*tp_str*/              0,
+    /*tp_getattro*/         0,
+    /*tp_setattro*/         0,
+    /*tp_as_buffer*/        0,
+    /*tp_flags*/            BASEFLAGS,
+    /*tp_doc*/              0,
+    /*tp_traverse*/         0,
+    /*tp_clear*/            0,
+    /*tp_richcompare*/      0,
+    /*tp_weaklistoffset*/   0,
+    /*tp_iter*/             0,
+    /*tp_iternext*/         0,
+    /*tp_methods*/          0,
+    /*tp_members*/          0,
+    /*tp_getset*/           0,
+    /*tp_base*/             0,  //  set to &PyType_Type in module init function (why can it not be done here?)
+    /*tp_dict*/             0,
+    /*tp_descr_get*/        0,
+    /*tp_descr_set*/        0,
+    /*tp_dictoffset*/       0,
+    /*tp_init*/             0,
+    /*tp_alloc*/            0,
+    /*tp_new*/              datetimetype_tp_new,
+    /*tp_free*/             0,
+    /*tp_is_gc*/            0,
+    /*tp_bases*/            0,
+    /*tp_mro*/              0,
+    /*tp_cache*/            0,
+    /*tp_subclasses*/       0,
+    /*tp_weaklist*/         0
+};
+
 static PyMethodDef sbkMethods[] = {{NULL, NULL, 0, NULL}};
 
 /* Initialize this module. */
@@ -69,6 +135,16 @@
 initfoo3(void)
 {
     PyObject *mod, *d;
+    PyObject *module = NULL;
+    module = PyImport_ImportModule("datetime");
+    typ = PyObject_GetAttr(module, PyString_FromString("datetime"));
+    Py_DECREF(module);
+    if (!PyType_Check(typ)) {
+        PyErr_Format(PyExc_TypeError, "datetime.datetime is not a type object");
+        return;
+    }
+    datetimetype.tp_base = (PyTypeObject*)typ;
+    PyType_Ready(&datetimetype);
     footype.tp_base = &PyType_Type;
     PyType_Ready(&footype);
     mod = Py_InitModule("foo3", sbkMethods);
@@ -79,5 +155,7 @@
         return;
     if (PyDict_SetItemString(d, "footype", (PyObject *)&footype) < 0)
         return;
+    if (PyDict_SetItemString(d, "datetimetype", (PyObject *)&datetimetype) < 0)
+        return;
     Py_INCREF(&footype);
 }
diff --git a/pypy/module/cpyext/test/test_bytesobject.py b/pypy/module/cpyext/test/test_bytesobject.py
--- a/pypy/module/cpyext/test/test_bytesobject.py
+++ b/pypy/module/cpyext/test/test_bytesobject.py
@@ -358,11 +358,15 @@
 
                 data = PyString_AS_STRING(args);
                 len = PyString_GET_SIZE(args);
-                if (data == NULL || len < 1)
+                if (data == NULL)
                     Py_RETURN_NONE;
                 obj = PyArray_Scalar(data, len);
                 return obj;
              """),
+            ("get_len", "METH_O",
+             """
+                return PyLong_FromLong(PyObject_Size(args));
+             """),
             ], prologue="""
                 #include <Python.h>
                 PyTypeObject PyStringArrType_Type = {
@@ -438,12 +442,16 @@
                 PyStringArrType_Type.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE;
                 PyStringArrType_Type.tp_itemsize = sizeof(char);
                 PyStringArrType_Type.tp_base = &PyString_Type;
+                PyStringArrType_Type.tp_hash = PyString_Type.tp_hash;
                 if (PyType_Ready(&PyStringArrType_Type) < 0) INITERROR;
             ''')
 
         a = module.newsubstr('abc')
         assert type(a).__name__ == 'string_'
         assert a == 'abc'
+        assert 3 == module.get_len(a)
+        b = module.newsubstr('')
+        assert 0 == module.get_len(b)
 
 class TestBytes(BaseApiTest):
     def test_bytes_resize(self, space):
diff --git a/pypy/module/cpyext/test/test_memoryobject.py b/pypy/module/cpyext/test/test_memoryobject.py
--- a/pypy/module/cpyext/test/test_memoryobject.py
+++ b/pypy/module/cpyext/test/test_memoryobject.py
@@ -30,13 +30,14 @@
         assert view.c_len == 5
         o = rffi.charp2str(view.c_buf)
         assert o == 'hello'
-        w_mv = from_ref(space, api.PyMemoryView_FromBuffer(view))
+        ref = api.PyMemoryView_FromBuffer(view)
+        w_mv = from_ref(space, ref)
         for f in ('format', 'itemsize', 'ndim', 'readonly', 
                   'shape', 'strides', 'suboffsets'):
             w_f = space.wrap(f)
             assert space.eq_w(space.getattr(w_mv, w_f), 
                               space.getattr(w_memoryview, w_f))
-
+        api.Py_DecRef(ref)
 
 class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase):
     def test_fillWithObject(self):
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
@@ -2,6 +2,7 @@
 from rpython.rtyper.lltypesystem import rffi
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
 from pypy.module.cpyext.test.test_api import BaseApiTest
+from pypy.module.cpyext.api import generic_cpy_call
 from pypy.module.cpyext.pyobject import make_ref, from_ref
 from pypy.module.cpyext.typeobject import PyTypeObjectPtr
 
@@ -663,30 +664,6 @@
         assert module.tp_init(list, x, ("hi",)) is None
         assert x == ["h", "i"]
 
-    def test_tp_str(self):
-        module = self.import_extension('foo', [
-           ("tp_str", "METH_VARARGS",
-            '''
-                 PyTypeObject *type = (PyTypeObject *)PyTuple_GET_ITEM(args, 0);
-                 PyObject *obj = PyTuple_GET_ITEM(args, 1);
-                 if (!type->tp_str)
-                 {
-                     PyErr_SetNone(PyExc_ValueError);
-                     return NULL;
-                 }
-                 return type->tp_str(obj);
-             '''
-             )
-            ])
-        class C:
-            def __str__(self):
-                return "text"
-        assert module.tp_str(type(C()), C()) == "text"
-        class D(int):
-            def __str__(self):
-                return "more text"
-        assert module.tp_str(int, D(42)) == "42"
-
     def test_mp_ass_subscript(self):
         module = self.import_extension('foo', [
            ("new_obj", "METH_NOARGS",
@@ -978,9 +955,12 @@
         assert (d + a) == 5
         assert pow(d,b) == 16
 
-    def test_tp_new_in_subclass_of_type(self):
+    def test_tp_new_in_subclass(self):
+        import datetime
         module = self.import_module(name='foo3')
         module.footype("X", (object,), {})
+        a = module.datetimetype(1, 1, 1)
+        assert isinstance(a, module.datetimetype)
 
     def test_app_subclass_of_c_type(self):
         import sys
@@ -1165,7 +1145,6 @@
             self._check_type_object(FooType)
         class X(object):
             __metaclass__ = FooType
-        print repr(X)
         X()
 
     def test_multiple_inheritance3(self):
diff --git a/pypy/module/cpyext/test/test_userslots.py b/pypy/module/cpyext/test/test_userslots.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/test/test_userslots.py
@@ -0,0 +1,208 @@
+from rpython.rtyper.lltypesystem import rffi
+from pypy.module.cpyext.pyobject import make_ref, from_ref
+from pypy.module.cpyext.api import generic_cpy_call
+from pypy.module.cpyext.typeobject import PyTypeObjectPtr
+from pypy.module.cpyext.test.test_api import BaseApiTest
+from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
+
+
+class TestAppLevelObject(BaseApiTest):
+    def test_nb_add_from_python(self, space, api):
+        w_date = space.appexec([], """():
+            class DateType(object):
+                def __add__(self, other):
+                    return 'sum!'
+            return DateType()
+            """)
+        w_datetype = space.type(w_date)
+        py_date = make_ref(space, w_date)
+        py_datetype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_datetype))
+        assert py_datetype.c_tp_as_number
+        assert py_datetype.c_tp_as_number.c_nb_add
+        w_obj = generic_cpy_call(space, py_datetype.c_tp_as_number.c_nb_add,
+                                 py_date, py_date)
+        assert space.str_w(w_obj) == 'sum!'
+
+    def test_tp_new_from_python(self, space, api):
+        w_date = space.appexec([], """():
+            class Date(object):
+                def __new__(cls, year, month, day):
+                    self = object.__new__(cls)
+                    self.year = year
+                    self.month = month
+                    self.day = day
+                    return self
+            return Date
+            """)
+        py_datetype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_date))
+        one = space.newint(1)
+        arg = space.newtuple([one, one, one])
+        # call w_date.__new__
+        w_obj = space.call_function(w_date, one, one, one)
+        w_year = space.getattr(w_obj, space.newbytes('year'))
+        assert space.int_w(w_year) == 1
+
+        w_obj = generic_cpy_call(space, py_datetype.c_tp_new, py_datetype, 
+                                 arg, space.newdict({}))
+        w_year = space.getattr(w_obj, space.newbytes('year'))
+        assert space.int_w(w_year) == 1
+
+class AppTestUserSlots(AppTestCpythonExtensionBase):
+    def test_tp_hash_from_python(self):
+        # to see that the functions are being used,
+        # run pytest with -s
+        module = self.import_extension('foo', [
+           ("use_hash", "METH_O",
+            '''
+                long hash = args->ob_type->tp_hash(args);
+                return PyLong_FromLong(hash);
+            ''')])
+        class C(object):
+            def __hash__(self):
+                return -23
+        c = C()
+        # uses the userslot slot_tp_hash
+        ret = module.use_hash(C())
+        assert hash(c) == ret
+        # uses the slotdef renamed cpyext_tp_hash_int
+        ret = module.use_hash(3)
+        assert hash(3) == ret
+
+    def test_tp_str(self):
+        module = self.import_extension('foo', [
+           ("tp_str", "METH_VARARGS",
+            '''
+                 PyTypeObject *type = (PyTypeObject *)PyTuple_GET_ITEM(args, 0);
+                 PyObject *obj = PyTuple_GET_ITEM(args, 1);
+                 if (!type->tp_str)
+                 {
+                     PyErr_SetString(PyExc_ValueError, "no tp_str");
+                     return NULL;
+                 }
+                 return type->tp_str(obj);
+             '''
+             )
+            ])
+        class C:
+            def __str__(self):
+                return "text"
+        assert module.tp_str(type(C()), C()) == "text"
+        class D(int):
+            def __str__(self):
+                return "more text"
+        assert module.tp_str(int, D(42)) == "42"
+        class A(object):
+            pass
+        s = module.tp_str(type(A()), A())
+        assert 'A object' in s
+
+    def test_tp_deallocate(self):
+        module = self.import_extension('foo', [
+            ("get_cnt", "METH_NOARGS",
+            '''
+                return PyLong_FromLong(foocnt);
+            '''),
+            ("get__timestamp", "METH_NOARGS",
+            '''
+                PyObject * one = PyLong_FromLong(1);
+                PyObject * a = PyTuple_Pack(3, one, one, one);
+                PyObject * k = NULL;
+                obj = _Timestamp.tp_new(&_Timestamp, a, k);
+                Py_DECREF(one);
+                return obj;
+             '''),
+            ("get_timestamp", "METH_NOARGS",
+            '''
+                PyObject * one = PyLong_FromLong(1);
+                PyObject * a = PyTuple_Pack(3, one, one, one);
+                PyObject * k = NULL;
+                obj = Timestamp.tp_new(&Timestamp, a, k);
+                Py_DECREF(one);
+                return obj;
+             '''),
+            ], prologue='''
+                static int foocnt = 0;
+                static PyTypeObject* datetime_cls = NULL;
+                static PyObject * obj = NULL;
+                static PyObject*
+                _timestamp_new(PyTypeObject* t, PyObject* a, PyObject* k)
+                {
+                    foocnt ++;
+                    return datetime_cls->tp_new(t, a, k);
+                }
+
+                static PyObject*
+                timestamp_new(PyTypeObject* t, PyObject* a, PyObject* k)
+                {
+                    return datetime_cls->tp_new(t, a, k);
+                }
+
+                static void 
+                _timestamp_dealloc(PyObject *op)
+                {
+                    foocnt --;
+                    datetime_cls->tp_dealloc(op);
+                }
+                 
+
+                static PyTypeObject _Timestamp = {
+                    PyObject_HEAD_INIT(NULL)
+                    0,                            /* ob_size */
+                    "foo._Timestamp",   /* tp_name*/
+                    0,                  /* tp_basicsize*/
+                    0,                  /* tp_itemsize */
+                    _timestamp_dealloc  /* tp_dealloc  */
+                };
+                static PyTypeObject Timestamp = {
+                    PyObject_HEAD_INIT(NULL)
+                    0,                            /* ob_size */
+                    "foo.Timestamp",   /* tp_name*/
+                    0,                  /* tp_basicsize*/
+                    0                  /* tp_itemsize */
+                };
+            ''', more_init='''
+                PyObject * mod = PyImport_ImportModule("datetime");
+                if (mod == NULL) INITERROR;
+                PyObject * dt = PyString_FromString("datetime");
+                datetime_cls = (PyTypeObject*)PyObject_GetAttr(mod, dt); 
+                if (datetime_cls == NULL) INITERROR;
+                _Timestamp.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
+                _Timestamp.tp_base = datetime_cls;
+                _Timestamp.tp_new = _timestamp_new;
+                Py_DECREF(mod);
+                Py_DECREF(dt);
+                if (PyType_Ready(&_Timestamp) < 0) INITERROR;
+
+                Timestamp.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
+                Timestamp.tp_base = &_Timestamp;
+                Timestamp.tp_new = timestamp_new;
+                Timestamp.tp_dealloc = datetime_cls->tp_dealloc;
+                if (PyType_Ready(&Timestamp) < 0) INITERROR;
+            ''')
+        # _Timestamp has __new__, __del__ and 
+        #      inherits from datetime.datetime
+        # Timestamp has __new__, default __del__ (subtype_dealloc) and
+        #      inherits from _Timestamp
+        import gc, sys
+        cnt = module.get_cnt()
+        assert cnt == 0
+        obj = module.get__timestamp() #_Timestamp
+        cnt = module.get_cnt()
+        assert cnt == 1
+        assert obj.year == 1
+        del obj
+        self.debug_collect()
+        cnt = module.get_cnt()
+        assert cnt == 0
+
+        obj = module.get_timestamp() #Timestamp
+        cnt = module.get_cnt()
+        assert cnt == 0
+        assert obj.year == 1
+        # XXX calling Timestamp.tp_dealloc which is subtype_dealloc
+        #     causes infinite recursion
+        del obj
+        self.debug_collect()
+        cnt = module.get_cnt()
+        assert cnt == 0
+
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -1,7 +1,7 @@
 import os
 
 from rpython.rlib import jit
-from rpython.rlib.objectmodel import specialize
+from rpython.rlib.objectmodel import specialize, we_are_translated
 from rpython.rtyper.lltypesystem import rffi, lltype
 
 from pypy.interpreter.baseobjspace import W_Root, DescrMismatch
@@ -32,12 +32,12 @@
 from pypy.module.cpyext.state import State
 from pypy.module.cpyext.structmember import PyMember_GetOne, PyMember_SetOne
 from pypy.module.cpyext.typeobjectdefs import (
-    PyGetSetDef, PyMemberDef,
+    PyGetSetDef, PyMemberDef, PyMappingMethods,
     PyNumberMethods, PySequenceMethods, PyBufferProcs)
 from pypy.objspace.std.typeobject import W_TypeObject, find_best_base
 
 
-WARN_ABOUT_MISSING_SLOT_FUNCTIONS = False
+#WARN_ABOUT_MISSING_SLOT_FUNCTIONS = False
 
 PyType_Check, PyType_CheckExact = build_type_checkers("Type", "w_type")
 
@@ -250,6 +250,7 @@
             dict_w[name] = w_descr
             i += 1
 
+missing_slots={}
 def update_all_slots(space, w_type, pto):
     # fill slots in pto
     # Not very sure about it, but according to
@@ -257,22 +258,46 @@
     # overwrite slots that are already set: these ones are probably
     # coming from a parent C type.
 
-    typedef = w_type.layout.typedef
+    if w_type.is_heaptype():
+        typedef = None
+        search_dict_w = w_type.dict_w
+    else:
+        typedef = w_type.layout.typedef
+        search_dict_w = None
+
     for method_name, slot_name, slot_names, slot_apifunc in slotdefs_for_tp_slots:
-        w_descr = w_type.lookup(method_name)
-        if w_descr is None:
-            # XXX special case iternext
-            continue
+        slot_func_helper = None
+        if search_dict_w is None:
+            # built-in types: expose as many slots as possible, even
+            # if it happens to come from some parent class
+            slot_apifunc = None # use get_slot_tp_function
+        else:
+            # For heaptypes, w_type.layout.typedef will be object's typedef, and
+            # get_slot_tp_function will fail
+            w_descr = search_dict_w.get(method_name, None)
+            if w_descr:
+                # use the slot_apifunc (userslots) to lookup at runtime
+                pass
+            elif len(slot_names) ==1:
+                # 'inherit' from tp_base
+                slot_func_helper = getattr(pto.c_tp_base, slot_names[0])
+            else:
+                struct = getattr(pto.c_tp_base, slot_names[0])
+                if struct:
+                    slot_func_helper = getattr(struct, slot_names[1])
 
-        if slot_apifunc is None and typedef is not None:
-            slot_apifunc = get_slot_tp_function(space, typedef, slot_name)
-        if not slot_apifunc:
-            if WARN_ABOUT_MISSING_SLOT_FUNCTIONS:
-                os.write(2,
-                    "%s defined by %s but no slot function defined!\n" % (
-                        method_name, w_type.getname(space)))
-            continue
-        slot_func_helper = slot_apifunc.get_llhelper(space)
+        if not slot_func_helper:
+            if typedef is not None:
+                if slot_apifunc is None:
+                    slot_apifunc = get_slot_tp_function(space, typedef, slot_name)
+            if not slot_apifunc:
+                if not we_are_translated():
+                    if slot_name not in missing_slots:
+                        missing_slots[slot_name] = w_type.getname(space)
+                        print "missing slot %r/%r, discovered on %r" % (
+                            method_name, slot_name, w_type.getname(space))
+                continue
+            slot_func_helper = slot_apifunc.get_llhelper(space)
 
         # XXX special case wrapper-functions and use a "specific" slot func
 
@@ -300,6 +325,8 @@
                     STRUCT_TYPE = PySequenceMethods
                 elif slot_names[0] == 'c_tp_as_buffer':
                     STRUCT_TYPE = PyBufferProcs
+                elif slot_names[0] == 'c_tp_as_mapping':
+                    STRUCT_TYPE = PyMappingMethods
                 else:
                     raise AssertionError(
                         "Structure not allocated: %s" % (slot_names[0],))
@@ -396,13 +423,6 @@
         pto.c_tp_itemsize = base_pto.c_tp_itemsize
     pto.c_tp_flags |= base_pto.c_tp_flags & Py_TPFLAGS_CHECKTYPES
     pto.c_tp_flags |= base_pto.c_tp_flags & Py_TPFLAGS_HAVE_INPLACEOPS
-    flags = rffi.cast(lltype.Signed, pto.c_tp_flags)
-    base_object_pyo = make_ref(space, space.w_object)
-    base_object_pto = rffi.cast(PyTypeObjectPtr, base_object_pyo)
-    if base_pto != base_object_pto or flags & Py_TPFLAGS_HEAPTYPE:
-        if not pto.c_tp_new:
-            pto.c_tp_new = base_pto.c_tp_new
-    Py_DecRef(space, base_object_pyo)
 
 def check_descr(space, w_self, w_type):
     if not space.isinstance_w(w_self, w_type):
@@ -502,9 +522,25 @@
     pto = obj.c_ob_type
     base = pto
     this_func_ptr = llslot(space, subtype_dealloc)
+    w_obj = from_ref(space, rffi.cast(PyObject, base))
+    # This wrapper is created on a specific type, call it w_A.
+    # We wish to call the dealloc function from one of the base classes of w_A,
+    # the first of which is not this function itself.
+    # w_obj is an instance of w_A or one of its subclasses. So climb up the
+    # inheritance chain until base.c_tp_dealloc is exactly this_func, and then
+    # continue on up until they differ.
+    #print 'subtype_dealloc, start from', rffi.charp2str(base.c_tp_name)
+    while base.c_tp_dealloc != this_func_ptr:
+        base = base.c_tp_base
+        assert base
+        #print '                 ne move to', rffi.charp2str(base.c_tp_name)
+        w_obj = from_ref(space, rffi.cast(PyObject, base))
     while base.c_tp_dealloc == this_func_ptr:
         base = base.c_tp_base
         assert base
+        #print '                 eq move to', rffi.charp2str(base.c_tp_name)
+        w_obj = from_ref(space, rffi.cast(PyObject, base))
+    #print '                   end with', rffi.charp2str(base.c_tp_name)
     dealloc = base.c_tp_dealloc
     # XXX call tp_del if necessary
     generic_cpy_call(space, dealloc, obj)
@@ -676,13 +712,6 @@
 
     typedescr = get_typedescr(w_type.layout.typedef)
 
-    # dealloc
-    if space.gettypeobject(w_type.layout.typedef) is w_type:
-        # only for the exact type, like 'space.w_tuple' or 'space.w_list'
-        pto.c_tp_dealloc = typedescr.get_dealloc().get_llhelper(space)
-    else:
-        # for all subtypes, use subtype_dealloc()
-        pto.c_tp_dealloc = llslot(space, subtype_dealloc)
     if space.is_w(w_type, space.w_str):
         pto.c_tp_itemsize = 1
     elif space.is_w(w_type, space.w_tuple):
@@ -713,6 +742,17 @@
     w_base = best_base(space, w_type.bases_w)
     pto.c_tp_base = rffi.cast(PyTypeObjectPtr, make_ref(space, w_base))
 
+    # dealloc
+    if space.gettypeobject(w_type.layout.typedef) is w_type:
+        # only for the exact type, like 'space.w_tuple' or 'space.w_list'
+        pto.c_tp_dealloc = typedescr.get_dealloc().get_llhelper(space)
+    else:
+        # for all subtypes, use base's dealloc (requires sorting in attach_all)
+        pto.c_tp_dealloc = pto.c_tp_base.c_tp_dealloc
+        if not pto.c_tp_dealloc:
+            # strange, but happens (ABCMeta)
+            pto.c_tp_dealloc = llslot(space, subtype_dealloc)
+
     if builder.cpyext_type_init is not None:
         builder.cpyext_type_init.append((pto, w_type))
     else:
@@ -726,11 +766,18 @@
         if pto.c_tp_itemsize < pto.c_tp_base.c_tp_itemsize:
             pto.c_tp_itemsize = pto.c_tp_base.c_tp_itemsize
 
-    # will be filled later on with the correct value
-    # may not be 0
     if space.is_w(w_type, space.w_object):
+        # will be filled later on with the correct value
+        # may not be 0
         pto.c_tp_new = cts.cast('newfunc', 1)
     update_all_slots(space, w_type, pto)
+    if not pto.c_tp_new:
+        base_object_pyo = make_ref(space, space.w_object)
+        base_object_pto = rffi.cast(PyTypeObjectPtr, base_object_pyo)
+        flags = rffi.cast(lltype.Signed, pto.c_tp_flags)
+        if pto.c_tp_base != base_object_pto or flags & Py_TPFLAGS_HEAPTYPE:
+                pto.c_tp_new = pto.c_tp_base.c_tp_new
+        Py_DecRef(space, base_object_pyo)
     pto.c_tp_flags |= Py_TPFLAGS_READY
     return pto
 
diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py
--- a/pypy/module/cpyext/unicodeobject.py
+++ b/pypy/module/cpyext/unicodeobject.py
@@ -65,9 +65,10 @@
 def unicode_attach(space, py_obj, w_obj, w_userdata=None):
     "Fills a newly allocated PyUnicodeObject with a unicode string"
     py_unicode = rffi.cast(PyUnicodeObject, py_obj)
-    py_unicode.c_length = len(space.unicode_w(w_obj))
+    s = space.unicode_w(w_obj)
+    py_unicode.c_length = len(s)
     py_unicode.c_str = lltype.nullptr(rffi.CWCHARP.TO)
-    py_unicode.c_hash = space.hash_w(w_obj)
+    py_unicode.c_hash = space.hash_w(space.newunicode(s))
     py_unicode.c_defenc = lltype.nullptr(PyObject.TO)
 
 def unicode_realize(space, py_obj):
@@ -80,7 +81,7 @@
     w_type = from_ref(space, rffi.cast(PyObject, py_obj.c_ob_type))
     w_obj = space.allocate_instance(unicodeobject.W_UnicodeObject, w_type)
     w_obj.__init__(s)
-    py_uni.c_hash = space.hash_w(w_obj)
+    py_uni.c_hash = space.hash_w(space.newunicode(s))
     track_reference(space, py_obj, w_obj)
     return w_obj
 
@@ -205,9 +206,8 @@
 
 @cpython_api([rffi.VOIDP], Py_ssize_t, error=CANNOT_FAIL)
 def PyUnicode_GET_SIZE(space, w_obj):
-    """Return the size of the object.  o has to be a PyUnicodeObject (not
+    """Return the size of the object.  obj is a PyUnicodeObject (not
     checked)."""
-    assert isinstance(w_obj, unicodeobject.W_UnicodeObject)
     return space.len_w(w_obj)
 
 @cpython_api([rffi.VOIDP], rffi.CWCHARP, error=CANNOT_FAIL)
diff --git a/pypy/module/cpyext/userslot.py b/pypy/module/cpyext/userslot.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/userslot.py
@@ -0,0 +1,112 @@
+"""
+These are the default implementation for type slots that we put
+in user-defined app-level Python classes, if the class implements
+the corresponding '__xxx__' special method.  It should mostly just
+call back the general version of the space operation.
+
+This is only approximately correct.  One problem is that some
+details are likely subtly wrong.  Another problem is that we don't
+track changes to an app-level Python class (addition or removal of
+'__xxx__' special methods) after initalization of the PyTypeObject.
+"""
+
+from pypy.interpreter.error import oefmt
+from pypy.interpreter.argument import Arguments
+from pypy.module.cpyext.api import slot_function, PyObject, Py_ssize_t
+from pypy.module.cpyext.api import PyTypeObjectPtr
+from rpython.rtyper.lltypesystem import rffi, lltype
+
+ at slot_function([PyObject], Py_ssize_t, error=-1)
+def slot_sq_length(space, w_obj):
+    return space.int_w(space.len(w_obj))
+
+ at slot_function([PyObject], lltype.Signed, error=-1)
+def slot_tp_hash(space, w_obj):
+    return space.int_w(space.hash(w_obj))
+
+ at slot_function([PyObject, Py_ssize_t], PyObject)
+def slot_sq_item(space, w_obj, index):
+    return space.getitem(w_obj, space.wrap(index))
+
+ at slot_function([PyTypeObjectPtr, PyObject, PyObject], PyObject)
+def slot_tp_new(space, w_type, w_args, w_kwds):
+    # XXX problem - we need to find the actual __new__ function to call.
+    #     but we have no 'self' argument. Theoretically, self will be
+    #     w_type, but if w_type is a subclass of self, and w_type has a
+    #     __new__ function that calls super().__new__, and that call ends
+    #     up here, we will get infinite recursion. Prevent the recursion
+    #     in the simple case (from cython) where w_type is a cpytype, but
+    #     we know (since we are in this function) that self is not a cpytype
+    from pypy.module.cpyext.typeobject import W_PyCTypeObject
+    w_type0 = w_type
+    mro_w = space.listview(space.getattr(w_type0, space.wrap('__mro__')))
+    for w_m in mro_w[1:]:
+        if not w_type0.is_cpytype():
+            break
+        w_type0 = w_m
+    w_impl = space.getattr(w_type0, space.wrap('__new__'))
+    args = Arguments(space, [w_type],
+                     w_stararg=w_args, w_starstararg=w_kwds)
+    return space.call_args(w_impl, args)
+
+# unary functions
+
+ at slot_function([PyObject], PyObject)
+def slot_tp_str(space, w_obj):
+    return space.str(w_obj)
+
+ at slot_function([PyObject], PyObject)
+def slot_tp_repr(space, w_obj):
+    return space.repr(w_obj)
+
+#binary functions
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_nb_add(space, w_obj1, w_obj2):
+    return space.add(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_nb_subtract(space, w_obj1, w_obj2):
+    return space.sub(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_nb_multiply(space, w_obj1, w_obj2):
+    return space.mul(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_nb_divide(space, w_obj1, w_obj2):
+    return space.div(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_nb_inplace_add(space, w_obj1, w_obj2):
+    return space.add(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_nb_inplace_subtract(space, w_obj1, w_obj2):
+    return space.sub(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_nb_inplace_multiply(space, w_obj1, w_obj2):
+    return space.mul(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_nb_inplace_divide(space, w_obj1, w_obj2):
+    return space.div(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_sq_concat(space, w_obj1, w_obj2):
+    return space.add(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_sq_inplace_concat(space, w_obj1, w_obj2):
+    return space.add(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_mp_subscript(space, w_obj1, w_obj2):
+    return space.getitem(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_tp_getattr(space, w_obj1, w_obj2):
+    return space.getattr(w_obj1, w_obj2)
+
+
diff --git a/pypy/objspace/std/bytesobject.py b/pypy/objspace/std/bytesobject.py
--- a/pypy/objspace/std/bytesobject.py
+++ b/pypy/objspace/std/bytesobject.py
@@ -1,5 +1,6 @@
 """The builtin str implementation"""
 
+from rpython.rlib import jit
 from rpython.rlib.jit import we_are_jitted
 from rpython.rlib.objectmodel import (
     compute_hash, compute_unique_id, import_from_mixin)
@@ -950,6 +951,7 @@
 W_BytesObject.typedef.flag_sequence_bug_compat = True
 
 
+ at jit.elidable
 def string_escape_encode(s, quote):
     buf = StringBuilder(len(s) + 2)
 
diff --git a/pypy/objspace/std/test/test_typeobject.py b/pypy/objspace/std/test/test_typeobject.py
--- a/pypy/objspace/std/test/test_typeobject.py
+++ b/pypy/objspace/std/test/test_typeobject.py
@@ -1329,3 +1329,8 @@
             pass
         assert X.__dict__['__dict__'].__objclass__ is X
         assert X.__dict__['__weakref__'].__objclass__ is X
+        assert object.__dict__['__class__'].__objclass__ is object
+        assert int.__dict__['imag'].__objclass__ is int
+        assert file.closed.__objclass__ is file
+        assert type.__dict__['__name__'].__objclass__ is type
+        assert type.__dict__['__doc__'].__objclass__ is type
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
@@ -958,7 +958,7 @@
     __base__ = GetSetProperty(descr__base),
     __mro__ = GetSetProperty(descr_get__mro__),
     __dict__=GetSetProperty(type_get_dict),
-    __doc__ = GetSetProperty(descr__doc),
+    __doc__ = GetSetProperty(descr__doc, cls=W_TypeObject),
     mro = gateway.interp2app(descr_mro),
     __flags__ = GetSetProperty(descr__flags),
     __module__ = GetSetProperty(descr_get__module, descr_set__module),
@@ -1312,10 +1312,13 @@
     def build(self, typedef):
         "NOT_RPYTHON: initialization-time only."
         from pypy.objspace.std.objectobject import W_ObjectObject
+        from pypy.interpreter.typedef import GetSetProperty
+        from rpython.rlib.objectmodel import instantiate
 
         space = self.space
         rawdict = typedef.rawdict
         lazyloaders = {}
+        w_type = instantiate(W_TypeObject)
 
         # compute the bases
         if typedef is W_ObjectObject.typedef:
@@ -1327,13 +1330,16 @@
         # wrap everything
         dict_w = {}
         for descrname, descrvalue in rawdict.items():
+            # special case for GetSetProperties' __objclass__:
+            if isinstance(descrvalue, GetSetProperty):
+                descrvalue = descrvalue.copy_for_type(w_type)
             dict_w[descrname] = space.wrap(descrvalue)
 
         if typedef.applevel_subclasses_base is not None:
             overridetypedef = typedef.applevel_subclasses_base.typedef
         else:
             overridetypedef = typedef
-        w_type = W_TypeObject(space, typedef.name, bases_w, dict_w,
+        w_type.__init__(space, typedef.name, bases_w, dict_w,
                               overridetypedef=overridetypedef,
                               is_heaptype=overridetypedef.heaptype)
         if typedef is not overridetypedef:
diff --git a/rpython/jit/metainterp/optimizeopt/unroll.py b/rpython/jit/metainterp/optimizeopt/unroll.py
--- a/rpython/jit/metainterp/optimizeopt/unroll.py
+++ b/rpython/jit/metainterp/optimizeopt/unroll.py
@@ -405,7 +405,8 @@
                 i += 1
                 self.optimizer.send_extra_operation(op)
             # force all of them except the virtuals
-            for arg in args_no_virtuals + short_jump_args:
+            for arg in (args_no_virtuals +
+                        self._map_args(mapping, short_jump_args)):
                 self.optimizer.force_box(self.get_box_replacement(arg))
             self.optimizer.flush()
             # done unless "short" has grown again
diff --git a/rpython/jit/metainterp/test/test_ajit.py b/rpython/jit/metainterp/test/test_ajit.py
--- a/rpython/jit/metainterp/test/test_ajit.py
+++ b/rpython/jit/metainterp/test/test_ajit.py
@@ -4620,3 +4620,19 @@
             time.clock()
             return 0
         self.interp_operations(g, [])
+
+    def test_issue2465(self):
+        driver = JitDriver(greens=[], reds=['i', 'a', 'b'])
+        class F(object):
+            def __init__(self, floatval):
+                self.floatval = floatval
+        def f(i):
+            a = F(0.0)
+            b = None
+            while i > 0:
+                driver.jit_merge_point(i=i, a=a, b=b)
+                b = F(a.floatval / 1.)
+                i -= 1
+            return i
+
+        self.meta_interp(f, [10])
diff --git a/rpython/memory/gctransform/framework.py b/rpython/memory/gctransform/framework.py
--- a/rpython/memory/gctransform/framework.py
+++ b/rpython/memory/gctransform/framework.py
@@ -1601,6 +1601,8 @@
                                       resulttype=llmemory.Address)
             hop.genop("cast_adr_to_ptr", [v_ret],
                       resultvar = hop.spaceop.result)
+        else:
+            hop.rename("same_as")
 
 
 
diff --git a/rpython/rlib/buffer.py b/rpython/rlib/buffer.py
--- a/rpython/rlib/buffer.py
+++ b/rpython/rlib/buffer.py
@@ -112,6 +112,7 @@
 
     def get_raw_address(self):
         from rpython.rtyper.lltypesystem import rffi
+        # may still raise ValueError on some GCs
         return rffi.get_raw_address_of_string(self.value)
 
 class SubBuffer(Buffer):
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -542,8 +542,13 @@
         become identical to the one returned.
 
         NOTE: Only use for immutable objects!
+
+        NOTE: Might fail on some GCs!  You have to check again
+        can_move() afterwards.  It should always work with the default
+        GC.  With Boehm, can_move() is always False so
+        move_out_of_nursery() should never be called in the first place.
     """
-    pass
+    return obj
 
 class MoveOutOfNurseryEntry(ExtRegistryEntry):
     _about_ = move_out_of_nursery
diff --git a/rpython/rlib/runicode.py b/rpython/rlib/runicode.py
--- a/rpython/rlib/runicode.py
+++ b/rpython/rlib/runicode.py
@@ -1378,7 +1378,12 @@
         CHR = chr
 
     def unicode_escape(s, size, errors, errorhandler=None):
-        # errorhandler is not used: this function cannot cause Unicode errors
+        # errors and errorhandler are not used: this function cannot cause
+        # Unicode errors
+        return _unicode_escape(s, size)
+
+    @jit.elidable
+    def _unicode_escape(s, size):
         result = STRING_BUILDER(size)
 
         if quotes:
diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py
--- a/rpython/rtyper/lltypesystem/rffi.py
+++ b/rpython/rtyper/lltypesystem/rffi.py
@@ -1336,6 +1336,8 @@
     as key is alive. If key goes out of scope, the buffer will eventually
     be freed. `string` cannot go out of scope until the RawBytes object
     referencing it goes out of scope.
+
+    NOTE: may raise ValueError on some GCs, but not the default one.
     """
     assert isinstance(string, str)
     from rpython.rtyper.annlowlevel import llstr
@@ -1346,6 +1348,8 @@
     if we_are_translated():
         if rgc.can_move(string):
             string = rgc.move_out_of_nursery(string)
+            if rgc.can_move(string):
+                raise ValueError("cannot make string immovable")
 
         # string cannot move now! return the address
         lldata = llstr(string)
diff --git a/rpython/rtyper/lltypesystem/test/test_ztranslated.py b/rpython/rtyper/lltypesystem/test/test_ztranslated.py
--- a/rpython/rtyper/lltypesystem/test/test_ztranslated.py
+++ b/rpython/rtyper/lltypesystem/test/test_ztranslated.py
@@ -37,7 +37,10 @@
     return ptr
 
 def main(argv=[]):
-    use_str()
+    try:
+        use_str()
+    except ValueError:
+        return 42
     gc.collect()
     mystr = b"12341234aa"*4096*10
     #debug_assert(not rgc.can_move(mystr), "long string can move... why?")
@@ -56,4 +59,15 @@
 
 def test_compiled_incminimark():
     fn = compile(main, [], gcpolicy="incminimark")
-    fn()
+    res = fn()
+    assert res == 0
+
+def test_compiled_semispace():
+    fn = compile(main, [], gcpolicy="semispace")
+    res = fn()
+    assert res == 42    # get_raw_address_of_string() raises ValueError
+
+def test_compiled_boehm():
+    fn = compile(main, [], gcpolicy="boehm")
+    res = fn()
+    assert res == 0


More information about the pypy-commit mailing list