[pypy-commit] pypy default: For a "func-in-small-set" attribute, initialize the class vtable to

arigo pypy.commits at gmail.com
Tue Mar 29 11:32:47 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r83421:1f344b91d8ab
Date: 2016-03-29 17:26 +0200
http://bitbucket.org/pypy/pypy/changeset/1f344b91d8ab/

Log:	For a "func-in-small-set" attribute, initialize the class vtable to
	'\xff' if there is no corresponding function, instead of the default
	of 0 which will map to a random function.

diff --git a/rpython/rtyper/rclass.py b/rpython/rtyper/rclass.py
--- a/rpython/rtyper/rclass.py
+++ b/rpython/rtyper/rclass.py
@@ -310,10 +310,15 @@
         # setup class attributes: for each attribute name at the level
         # of 'r_parentcls', look up its value in the class
         def assign(mangled_name, value):
-            if (isinstance(value, Constant) and
-                    isinstance(value.value, staticmethod)):
-                value = Constant(value.value.__get__(42))   # staticmethod => bare function
-            llvalue = r.convert_desc_or_const(value)
+            if value is None:
+                llvalue = r.special_uninitialized_value()
+                if llvalue is None:
+                    return
+            else:
+                if (isinstance(value, Constant) and
+                        isinstance(value.value, staticmethod)):
+                    value = Constant(value.value.__get__(42))   # staticmethod => bare function
+                llvalue = r.convert_desc_or_const(value)
             setattr(vtable, mangled_name, llvalue)
 
         for fldname in r_parentcls.clsfields:
@@ -321,8 +326,7 @@
             if r.lowleveltype is Void:
                 continue
             value = self.classdef.classdesc.read_attribute(fldname, None)
-            if value is not None:
-                assign(mangled_name, value)
+            assign(mangled_name, value)
         # extra PBC attributes
         for (access_set, attr), (mangled_name, r) in r_parentcls.pbcfields.items():
             if self.classdef.classdesc not in access_set.descs:
@@ -330,8 +334,7 @@
             if r.lowleveltype is Void:
                 continue
             attrvalue = self.classdef.classdesc.read_attribute(attr, None)
-            if attrvalue is not None:
-                assign(mangled_name, attrvalue)
+            assign(mangled_name, attrvalue)
 
     def fill_vtable_root(self, vtable):
         """Initialize the head of the vtable."""
diff --git a/rpython/rtyper/rmodel.py b/rpython/rtyper/rmodel.py
--- a/rpython/rtyper/rmodel.py
+++ b/rpython/rtyper/rmodel.py
@@ -125,6 +125,9 @@
                 self, value))
         return value
 
+    def special_uninitialized_value(self):
+        return None
+
     def get_ll_eq_function(self):
         """Return an eq(x,y) function to use to compare two low-level
         values of this Repr.
diff --git a/rpython/rtyper/rpbc.py b/rpython/rtyper/rpbc.py
--- a/rpython/rtyper/rpbc.py
+++ b/rpython/rtyper/rpbc.py
@@ -431,10 +431,14 @@
         if isinstance(value, types.MethodType) and value.im_self is None:
             value = value.im_func   # unbound method -> bare function
         if value is None:
+            assert self.descriptions[0] is None
             return chr(0)
         funcdesc = self.rtyper.annotator.bookkeeper.getdesc(value)
         return self.convert_desc(funcdesc)
 
+    def special_uninitialized_value(self):
+        return chr(0xFF)
+
     def dispatcher(self, shape, index, argtypes, resulttype):
         key = shape, index, tuple(argtypes), resulttype
         if key in self._dispatch_cache:
diff --git a/rpython/rtyper/test/test_rpbc.py b/rpython/rtyper/test/test_rpbc.py
--- a/rpython/rtyper/test/test_rpbc.py
+++ b/rpython/rtyper/test/test_rpbc.py
@@ -1947,6 +1947,30 @@
         kwds['config'] = self.config
         return TestRPBC.interpret(fn, args, **kwds)
 
+    def test_class_missing_base_method_should_crash(self):
+        class Base(object):
+            pass   # no method 'm' here
+        class A(Base):
+            def m(self):
+                return 42
+        class B(Base):
+            def m(self):
+                return 63
+        def g(n):
+            if n == 1:
+                return A()
+            elif n == 2:
+                return B()
+            else:
+                return Base()
+        def f(n):
+            return g(n).m()
+
+        assert self.interpret(f, [1]) == 42
+        assert self.interpret(f, [2]) == 63
+        e = py.test.raises(ValueError, self.interpret, f, [3])
+        assert str(e.value).startswith(r"exit case '\xff' not found")
+
 def test_smallfuncsets_basic():
     from rpython.translator.translator import TranslationContext, graphof
     from rpython.config.translationoption import get_combined_translation_config


More information about the pypy-commit mailing list