[pypy-commit] pypy default: Merge the first part of the cpyext-fast-typecheck branch.

antocuni pypy.commits at gmail.com
Sat Mar 24 06:06:13 EDT 2018


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: 
Changeset: r94126:66208269e66e
Date: 2018-03-24 10:47 +0100
http://bitbucket.org/pypy/pypy/changeset/66208269e66e/

Log:	Merge the first part of the cpyext-fast-typecheck branch.

	This branch introduce a way to implement Py*_Check efficiently as C
	macros which simply checks a flag on the C typeobject, instead of
	doing a full roundtrip to RPython land to do a space.isinstance_w().

	For now, implement PyFloat_Check, PyBool_Check and PySlice_Check
	using the new technique. Eventually, we should do the same for all
	type checkers and possibly kill build_type_checkers().

	This commits merges only part of the branch because later commits
	have diverged from the original scope and implements a different
	feature (bad me); they will be merged in a separate commit, to keep
	the diff clear.

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
@@ -133,6 +133,11 @@
              'TYPE', 'STRING'): # 'STRING' -> 'BYTES' in py3
     constant_names.append('Py_TPFLAGS_%s_SUBCLASS' % name)
 
+# PyPy-specific flags
+for name in ('FLOAT',):
+    constant_names.append('Py_TPPYPYFLAGS_%s_SUBCLASS' % name)
+
+
 for name in constant_names:
     setattr(CConfig_constants, name, rffi_platform.ConstantInteger(name))
 globals().update(rffi_platform.configure(CConfig_constants))
diff --git a/pypy/module/cpyext/boolobject.py b/pypy/module/cpyext/boolobject.py
--- a/pypy/module/cpyext/boolobject.py
+++ b/pypy/module/cpyext/boolobject.py
@@ -1,9 +1,5 @@
-from rpython.rtyper.lltypesystem import rffi, lltype
-from pypy.module.cpyext.api import (cpython_api, PyObject, CANNOT_FAIL,
-                                    build_type_checkers)
-
-# Inheriting from bool isn't actually possible.
-PyBool_Check = build_type_checkers("Bool")[1]
+from rpython.rtyper.lltypesystem import rffi
+from pypy.module.cpyext.api import cpython_api, PyObject
 
 @cpython_api([rffi.LONG], PyObject)
 def PyBool_FromLong(space, value):
diff --git a/pypy/module/cpyext/floatobject.py b/pypy/module/cpyext/floatobject.py
--- a/pypy/module/cpyext/floatobject.py
+++ b/pypy/module/cpyext/floatobject.py
@@ -1,7 +1,7 @@
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import (PyObjectFields, bootstrap_function,
     cpython_struct,
-    CANNOT_FAIL, cpython_api, PyObject, build_type_checkers, CONST_STRING)
+    CANNOT_FAIL, cpython_api, PyObject, CONST_STRING)
 from pypy.module.cpyext.pyobject import (
     make_typedescr, track_reference, from_ref)
 from pypy.interpreter.error import OperationError
@@ -38,8 +38,6 @@
     track_reference(space, obj, w_obj)
     return w_obj
 
-PyFloat_Check, PyFloat_CheckExact = build_type_checkers("Float")
-
 @cpython_api([lltype.Float], PyObject)
 def PyFloat_FromDouble(space, value):
     return space.newfloat(value)
diff --git a/pypy/module/cpyext/include/boolobject.h b/pypy/module/cpyext/include/boolobject.h
--- a/pypy/module/cpyext/include/boolobject.h
+++ b/pypy/module/cpyext/include/boolobject.h
@@ -16,6 +16,8 @@
 #define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
 #define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
 
+#define PyBool_Check(op) ((op)->ob_type == &PyBool_Type)
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/pypy/module/cpyext/include/floatobject.h b/pypy/module/cpyext/include/floatobject.h
--- a/pypy/module/cpyext/include/floatobject.h
+++ b/pypy/module/cpyext/include/floatobject.h
@@ -32,6 +32,11 @@
                 return PyFloat_FromDouble(-Py_HUGE_VAL);        \
         } while(0)
 
+#define PyFloat_Check(op) \
+		 _PyPy_Type_FastSubclass((op)->ob_type, Py_TPPYPYFLAGS_FLOAT_SUBCLASS)
+#define PyFloat_CheckExact(op) ((op)->ob_type == &PyFloat_Type)
+
+
 #ifdef __cplusplus
 }
 #endif
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
@@ -228,6 +228,11 @@
 #define Py_TPFLAGS_BASE_EXC_SUBCLASS	(1L<<30)
 #define Py_TPFLAGS_TYPE_SUBCLASS	(1L<<31)
 
+/* These are conceptually the same as the flags above, but they are
+   PyPy-specific and are stored inside tp_pypy_flags */
+#define Py_TPPYPYFLAGS_FLOAT_SUBCLASS (1L<<0)
+
+    
 #define Py_TPFLAGS_DEFAULT_EXTERNAL ( \
                              Py_TPFLAGS_HAVE_GETCHARBUFFER | \
                              Py_TPFLAGS_HAVE_SEQUENCE_IN | \
@@ -247,6 +252,8 @@
 #define PyType_HasFeature(t,f)  (((t)->tp_flags & (f)) != 0)
 #define PyType_FastSubclass(t,f)  PyType_HasFeature(t,f)
 
+#define _PyPy_Type_FastSubclass(t,f) (((t)->tp_pypy_flags & (f)) != 0)
+    
 #define PyType_Check(op) \
     PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_TYPE_SUBCLASS)
 #define PyType_CheckExact(op) (Py_TYPE(op) == &PyType_Type)
diff --git a/pypy/module/cpyext/include/sliceobject.h b/pypy/module/cpyext/include/sliceobject.h
--- a/pypy/module/cpyext/include/sliceobject.h
+++ b/pypy/module/cpyext/include/sliceobject.h
@@ -17,6 +17,8 @@
     PyObject *step;
 } PySliceObject;
 
+#define PySlice_Check(op) ((op)->ob_type == &PySlice_Type)
+    
 #ifdef __cplusplus
 }
 #endif
diff --git a/pypy/module/cpyext/parse/cpyext_object.h b/pypy/module/cpyext/parse/cpyext_object.h
--- a/pypy/module/cpyext/parse/cpyext_object.h
+++ b/pypy/module/cpyext/parse/cpyext_object.h
@@ -311,6 +311,10 @@
 	/* Type attribute cache version tag. Added in version 2.6 */
 	unsigned int tp_version_tag;
 
+    /* PyPy specific extra fields: make sure that they are ALWAYS at the end,
+       for compatibility with CPython */
+    long tp_pypy_flags;
+
 } PyTypeObject;
 
 typedef struct _heaptypeobject {
diff --git a/pypy/module/cpyext/sliceobject.py b/pypy/module/cpyext/sliceobject.py
--- a/pypy/module/cpyext/sliceobject.py
+++ b/pypy/module/cpyext/sliceobject.py
@@ -47,7 +47,6 @@
     from pypy.module.cpyext.object import _dealloc
     _dealloc(space, py_obj)
 
-PySlice_Check, PySlice_CheckExact = build_type_checkers("Slice")
 
 @cpython_api([PyObject, PyObject, PyObject], PyObject)
 def PySlice_New(space, w_start, w_stop, w_step):
@@ -75,9 +74,8 @@
     normal slices.
 
     Returns 0 on success and -1 on error with exception set."""
-    if not PySlice_Check(space, w_slice):
+    if not isinstance(w_slice, W_SliceObject):
         PyErr_BadInternalCall(space)
-    assert isinstance(w_slice, W_SliceObject)
     start_p[0], stop_p[0], step_p[0], slicelength_p[0] = \
             w_slice.indices4(space, length)
     return 0
@@ -97,9 +95,8 @@
     objects in versions of Python prior to 2.3, you would probably do well to
     incorporate the source of PySlice_GetIndicesEx(), suitably renamed,
     in the source of your extension."""
-    if not PySlice_Check(space, w_slice):
+    if not isinstance(w_slice, W_SliceObject):
         PyErr_BadInternalCall(space)
-    assert isinstance(w_slice, W_SliceObject)
     start_p[0], stop_p[0], step_p[0] = \
             w_slice.indices3(space, length)
     return 0
diff --git a/pypy/module/cpyext/test/test_boolobject.py b/pypy/module/cpyext/test/test_boolobject.py
--- a/pypy/module/cpyext/test/test_boolobject.py
+++ b/pypy/module/cpyext/test/test_boolobject.py
@@ -1,7 +1,6 @@
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
 from pypy.module.cpyext.test.test_api import BaseApiTest
-from pypy.module.cpyext.boolobject import PyBool_Check, PyBool_FromLong
-from pypy.module.cpyext.floatobject import PyFloat_FromDouble
+from pypy.module.cpyext.boolobject import PyBool_FromLong
 
 class TestBoolObject(BaseApiTest):
     def test_fromlong(self, space):
@@ -12,12 +11,6 @@
             else:
                 assert obj is space.w_False
 
-    def test_check(self, space):
-        assert PyBool_Check(space, space.w_True)
-        assert PyBool_Check(space, space.w_False)
-        assert not PyBool_Check(space, space.w_None)
-        assert not PyBool_Check(space, PyFloat_FromDouble(space, 1.0))
-
 class AppTestBoolMacros(AppTestCpythonExtensionBase):
     def test_macros(self):
         module = self.import_extension('foo', [
@@ -42,4 +35,14 @@
         assert module.to_int(False) == 0
         assert module.to_int(True) == 1
 
-            
+    def test_check(self):
+        module = self.import_extension('foo', [
+            ("type_check", "METH_O",
+             '''
+                return PyLong_FromLong(PyBool_Check(args));
+             ''')])
+        assert module.type_check(True)
+        assert module.type_check(False)
+        assert not module.type_check(None)
+        assert not module.type_check(1.0)
+             
diff --git a/pypy/module/cpyext/test/test_floatobject.py b/pypy/module/cpyext/test/test_floatobject.py
--- a/pypy/module/cpyext/test/test_floatobject.py
+++ b/pypy/module/cpyext/test/test_floatobject.py
@@ -102,9 +102,11 @@
              """
              PyObject* pyobj = PyFloat_FromDouble(1.0);
              PyFloatObject* pfo = (PyFloatObject*)pyobj;
-             int res = PyFloat_Check(pyobj) && PyFloat_CheckExact(pyobj) &&
-                PyFloat_Check(pfo) && PyFloat_CheckExact(pfo);
+             int res = (PyFloat_Check(pyobj) +
+                        PyFloat_CheckExact(pyobj) * 10 +
+                        PyFloat_Check(pfo) * 100 +
+                        PyFloat_CheckExact(pfo) * 1000);
              Py_DecRef(pyobj);
              return PyLong_FromLong(res);"""),
             ])
-        assert module.test() == 1
+        assert module.test() == 1111
diff --git a/pypy/module/cpyext/test/test_number.py b/pypy/module/cpyext/test/test_number.py
--- a/pypy/module/cpyext/test/test_number.py
+++ b/pypy/module/cpyext/test/test_number.py
@@ -11,7 +11,6 @@
     PyNumber_Index, PyNumber_Coerce, PyNumber_CoerceEx, PyNumber_Add,
     PyNumber_Multiply, PyNumber_InPlaceMultiply, PyNumber_Absolute,
     PyNumber_Power, PyNumber_InPlacePower)
-from pypy.module.cpyext.floatobject import PyFloat_Check
 from pypy.module.cpyext.intobject import PyInt_CheckExact
 from pypy.module.cpyext.longobject import PyLong_CheckExact
 from pypy.module.cpyext.object import PyObject_Size
@@ -86,7 +85,7 @@
 
         w_res = from_ref(space, ppl[0])
 
-        assert PyFloat_Check(space, w_res)
+        assert space.isinstance_w(w_res, space.w_float)
         assert space.unwrap(w_res) == 123.
         decref(space, pl)
         decref(space, pf)
diff --git a/pypy/module/cpyext/test/test_sliceobject.py b/pypy/module/cpyext/test/test_sliceobject.py
--- a/pypy/module/cpyext/test/test_sliceobject.py
+++ b/pypy/module/cpyext/test/test_sliceobject.py
@@ -2,14 +2,8 @@
 from pypy.module.cpyext.test.test_api import BaseApiTest
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
 from pypy.module.cpyext.api import Py_ssize_t, Py_ssize_tP
-from pypy.module.cpyext.sliceobject import PySlice_Check
 
 class TestSliceObject(BaseApiTest):
-    def test_slice(self, space):
-        w_i = space.wrap(10)
-        w_slice = space.newslice(w_i, w_i, w_i)
-        assert PySlice_Check(space, w_slice)
-        assert not PySlice_Check(space, w_i)
 
     def test_GetIndicesEx(self, space, api):
         w = space.wrap
@@ -79,3 +73,14 @@
              """),
             ])
         assert module.get_ellipsis() is Ellipsis
+
+    def test_typecheck(self):
+        module = self.import_extension('foo', [
+            ("check", "METH_O",
+             """
+                 PySliceObject *slice = (PySliceObject *)args;
+                 return PyLong_FromLong(PySlice_Check(slice));
+             """),
+            ])
+        s = slice(10, 20, 30)
+        assert module.check(s)
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
@@ -1537,4 +1537,29 @@
             pass
         assert module.test_flags(MyList, Py_TPFLAGS_LIST_SUBCLASS) == 0
 
+    def test_has_pypy_subclass_flag(self):
+        module = self.import_extension('foo', [
+           ("test_pypy_flags", "METH_VARARGS",
+            '''
+                long long in_flag, my_flag;
+                PyObject * obj;
+                if (!PyArg_ParseTuple(args, "OL", &obj, &in_flag))
+                    return NULL;
+                if (!PyType_Check(obj))
+                {
+                    PyErr_SetString(PyExc_ValueError, "input must be type");
+                    return NULL;
+                }
+                my_flag = ((PyTypeObject*)obj)->tp_pypy_flags;
+                if ((my_flag & in_flag) != in_flag)
+                    return PyLong_FromLong(-1);
+                return PyLong_FromLong(0);
+            '''),])
+        # copied from object.h
+        Py_TPPYPYFLAGS_FLOAT_SUBCLASS = (1L<<0)
 
+        class MyFloat(float):
+            pass
+        assert module.test_pypy_flags(float, Py_TPPYPYFLAGS_FLOAT_SUBCLASS) == 0
+        assert module.test_pypy_flags(MyFloat, Py_TPPYPYFLAGS_FLOAT_SUBCLASS) == 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
@@ -22,6 +22,7 @@
     Py_TPFLAGS_DICT_SUBCLASS, Py_TPFLAGS_BASE_EXC_SUBCLASS,
     Py_TPFLAGS_TYPE_SUBCLASS,
     Py_TPFLAGS_INT_SUBCLASS, Py_TPFLAGS_STRING_SUBCLASS, # change on py3
+    Py_TPPYPYFLAGS_FLOAT_SUBCLASS,
     )
 from pypy.module.cpyext.methodobject import (W_PyCClassMethodObject,
     W_PyCWrapperObject, PyCFunction_NewEx, PyCFunction, PyMethodDef,
@@ -426,6 +427,9 @@
         pto.c_tp_flags |= Py_TPFLAGS_LIST_SUBCLASS
     elif space.issubtype_w(w_obj, space.w_dict):
         pto.c_tp_flags |= Py_TPFLAGS_DICT_SUBCLASS
+    # the following types are a pypy-specific extensions, using tp_pypy_flags
+    elif space.issubtype_w(w_obj, space.w_float):
+        pto.c_tp_pypy_flags |= Py_TPPYPYFLAGS_FLOAT_SUBCLASS
 
 def check_descr(space, w_self, w_type):
     if not space.isinstance_w(w_self, w_type):


More information about the pypy-commit mailing list