[pypy-svn] r18724 - in pypy/dist/pypy/rpython: . lltypesystem ootypesystem ootypesystem/test

arigo at codespeak.net arigo at codespeak.net
Mon Oct 17 20:07:38 CEST 2005


Author: arigo
Date: Mon Oct 17 20:07:36 2005
New Revision: 18724

Modified:
   pypy/dist/pypy/rpython/lltypesystem/rclass.py
   pypy/dist/pypy/rpython/ootypesystem/rclass.py
   pypy/dist/pypy/rpython/ootypesystem/test/test_ooclean.py
   pypy/dist/pypy/rpython/rclass.py
   pypy/dist/pypy/rpython/rtyper.py
Log:
Support for general class attributes in the ootyper:

* moved the method/not-a-method distinction logic from lltypesystem/ up to
    rpython/.
* three dicts: self.allfields, self.allmethods, self.allclassattributes.
* generally attach a graph to the methods in addition to a _callable.
* build an accessor method for each non-constant class attribute (it's a method
    that just returns the value that corresponds to the class where the call
    arrived).
* annotating low-level helpers in ootypesystem seems to work out of the box in
    simple cases (after a minor fix in rtyper.py).



Modified: pypy/dist/pypy/rpython/lltypesystem/rclass.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/rclass.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/rclass.py	Mon Oct 17 20:07:36 2005
@@ -98,7 +98,10 @@
             for name, attrdef in attrs:
                 if attrdef.readonly:
                     s_value = attrdef.s_value
-                    s_value = self.prepare_method(name, s_value, allmethods)
+                    s_unboundmethod = self.prepare_method(s_value)
+                    if s_unboundmethod is not None:
+                        allmethods[name] = True
+                        s_value = s_unboundmethod
                     r = self.rtyper.getrepr(s_value)
                     mangled_name = 'cls_' + name
                     clsfields[name] = mangled_name, r
@@ -125,36 +128,6 @@
         self.allmethods = allmethods
         self.vtable = None
 
-    def prepare_method(self, name, s_value, allmethods):
-        # special-casing for methods:
-        #  - a class (read-only) attribute that would contain a PBC
-        #    with {func: classdef...} is probably meant to be used as a
-        #    method, but in corner cases it could be a constant object
-        #    of type MethodType that just sits here in the class.  But
-        #    as MethodType has a custom __get__ too and we don't support
-        #    it, it's a very bad idea anyway.
-        if isinstance(s_value, annmodel.SomePBC):
-            s_value = self.classdef.matching(s_value)
-            debound = {}
-            count = 0
-            for x, classdef in s_value.prebuiltinstances.items():
-                if isclassdef(classdef):
-                    #if classdef.commonbase(self.classdef) != self.classdef:
-                    #    raise TyperError("methods from PBC set %r don't belong "
-                    #                     "in %r" % (s_value.prebuiltinstances,
-                    #                                self.classdef.cls))
-                    count += 1
-                    classdef = True
-                debound[x] = classdef
-            if count > 0:
-                if count != len(s_value.prebuiltinstances):
-                    raise TyperError("mixing functions and methods "
-                                     "in PBC set %r" % (
-                        s_value.prebuiltinstances,))
-                s_value = annmodel.SomePBC(debound)
-                allmethods[name] = True
-        return s_value
-
     def convert_const(self, value):
         if not isinstance(value, (type, types.ClassType)):
             raise TyperError("not a class: %r" % (value,))

Modified: pypy/dist/pypy/rpython/ootypesystem/rclass.py
==============================================================================
--- pypy/dist/pypy/rpython/ootypesystem/rclass.py	(original)
+++ pypy/dist/pypy/rpython/ootypesystem/rclass.py	Mon Oct 17 20:07:36 2005
@@ -1,9 +1,10 @@
 from pypy.rpython.rmodel import inputconst
 from pypy.rpython.rclass import AbstractClassRepr, AbstractInstanceRepr, \
-                                getinstancerepr
+                                getinstancerepr, getclassrepr
 from pypy.rpython.rpbc import getsignature
 from pypy.rpython.ootypesystem import ootype
 from pypy.annotation.pairtype import pairtype
+from pypy.tool.sourcetools import func_with_new_name
 
 CLASSTYPE = ootype.Class
 
@@ -36,10 +37,13 @@
 
     def _setup_repr(self):
         if self.baserepr is not None:
-            self.allfields = self.baserepr.allfields.copy()
+            allfields = self.baserepr.allfields.copy()
+            allmethods = self.baserepr.allmethods.copy()
+            allclassattributes = self.baserepr.allclassattributes.copy()
         else:
-            self.allfields = {}
-        self.allmethods = {}
+            allfields = {}
+            allmethods = {}
+            allclassattributes = {}
 
         fields = {}
         attrs = self.classdef.attrs.items()
@@ -47,44 +51,89 @@
         for name, attrdef in attrs:
             if not attrdef.readonly:
                 repr = self.rtyper.getrepr(attrdef.s_value)
-                self.allfields[name] = repr
+                allfields[name] = repr
                 oot = repr.lowleveltype
                 fields[name] = oot
 
         ootype.addFields(self.lowleveltype, fields)
 
         methods = {}
+        classattributes = {}
         baseInstance = self.lowleveltype._superclass
+        classrepr = getclassrepr(self.rtyper, self.classdef)
 
         for classdef in self.classdef.getmro():
             attrs = classdef.attrs.items()
             for name, attrdef in attrs:
-                if attrdef.readonly:
-                    try:
-                        impl = self.classdef.cls.__dict__[name]
-                    except KeyError:
-                        pass
-                    else:
-                        f, inputs, ret = getsignature(self.rtyper, impl)
-                        M = ootype.Meth([r.lowleveltype for r in inputs[1:]], ret.lowleveltype)
-                        m = ootype.meth(M, _name=name, _callable=impl)
-                        methods[name] = m
+                if not attrdef.readonly:
+                    continue
+                try:
+                    impl = self.classdef.cls.__dict__[name]
+                except KeyError:
+                    continue
+                if classrepr.prepare_method(attrdef.s_value) is not None:
+                    # a regular method
+                    f, inputs, ret = getsignature(self.rtyper, impl)
+                    M = ootype.Meth([r.lowleveltype for r in inputs[1:]], ret.lowleveltype)
+                    m = ootype.meth(M, _name=name, _callable=impl,
+                                    graph=f.graph)
+                    methods[name] = m
+                    allmethods[name] = True
+                else:
+                    # a non-method class attribute
+                    allclassattributes[name] = True
+                    if not attrdef.s_value.is_constant():
+                        classattributes[name] = attrdef.s_value, impl
         
         ootype.addMethods(self.lowleveltype, methods)
-            
+        self.allfields = allfields
+        self.allmethods = allmethods
+        self.allclassattributes = allclassattributes
+
+        # step 2: provide accessor methods for class attributes that are
+        # really overridden in subclasses (this is done after the rest of
+        # the initialization because convert_const can require 'self' to
+        # be fully initialized)
+        for name, (s_value, impl) in classattributes.items():
+            r = self.rtyper.getrepr(s_value)
+            oovalue = r.convert_const(impl)
+            m = self.attach_class_attr_accessor(name, oovalue, r.lowleveltype)
+
+    def attach_class_attr_accessor(self, name, oovalue, oovaluetype):
+        def ll_getclassattr(self):
+            return oovalue
+        ll_getclassattr = func_with_new_name(ll_getclassattr, 'll_get_' + name)
+        sm = self.rtyper.annotate_helper(ll_getclassattr, [self.lowleveltype])
+        M = ootype.Meth([], oovaluetype)
+        m = ootype.meth(M, _name=name, _callable=ll_getclassattr,
+                        graph=sm.graph)
+        ootype.addMethods(self.lowleveltype, {name: m})
+
     def rtype_getattr(self, hop):
         vlist = hop.inputargs(self, ootype.Void)
         attr = hop.args_s[1].const
         s_inst = hop.args_s[0]
-        meth = self.lowleveltype._lookup(attr)
-        if meth is not None:
+        if attr in self.allfields:
+            # regular instance attributes
+            self.lowleveltype._check_field(attr)
+            return hop.genop("oogetfield", vlist,
+                             resulttype = hop.r_result.lowleveltype)
+        elif attr in self.allmethods:
             # special case for methods: represented as their 'self' only
             # (see MethodsPBCRepr)
             return hop.r_result.get_method_from_instance(self, vlist[0],
                                                          hop.llops)
-        self.lowleveltype._check_field(attr)
-        return hop.genop("oogetfield", vlist,
-                         resulttype = hop.r_result.lowleveltype)
+        elif attr in self.allclassattributes:
+            # class attributes
+            if hop.s_result.is_constant():
+                oovalue = hop.r_result.convert_const(hop.s_result.const)
+                return hop.inputconst(hop.r_result, oovalue)
+            else:
+                cname = hop.inputconst(ootype.Void, attr)
+                return hop.genop("oosend", [cname, vlist[0]],
+                                 resulttype = hop.r_result.lowleveltype)
+        else:
+            raise TyperError("no attribute %r on %r" % (attr, self))
 
     def rtype_setattr(self, hop):
         attr = hop.args_s[1].const

Modified: pypy/dist/pypy/rpython/ootypesystem/test/test_ooclean.py
==============================================================================
--- pypy/dist/pypy/rpython/ootypesystem/test/test_ooclean.py	(original)
+++ pypy/dist/pypy/rpython/ootypesystem/test/test_ooclean.py	Mon Oct 17 20:07:36 2005
@@ -129,3 +129,30 @@
         return inst.f()
     result = interpret(dummyfn, [], type_system='ootype')
     assert result == 3
+
+class HasClassAttr(object):
+    a = 3
+    def f(self, n):
+        return n + self.a
+
+class OverridesClassAttr(HasClassAttr):
+    a = 42
+
+def test_single_class_attr():
+    def dummyfn():
+        inst = HasClassAttr()
+        return inst.f(100)
+    result = interpret(dummyfn, [], type_system='ootype')
+    assert result == 103
+
+def test_class_attr():
+    def dummyfn(flag):
+        if flag:
+            inst = HasClassAttr()
+        else:
+            inst = OverridesClassAttr()
+        return inst.f(100)
+    result = interpret(dummyfn, [True], type_system='ootype')
+    assert result == 103
+    result = interpret(dummyfn, [False], type_system='ootype')
+    assert result == 142

Modified: pypy/dist/pypy/rpython/rclass.py
==============================================================================
--- pypy/dist/pypy/rpython/rclass.py	(original)
+++ pypy/dist/pypy/rpython/rclass.py	Mon Oct 17 20:07:36 2005
@@ -80,6 +80,35 @@
         #
         return getclassrepr(self.rtyper, subclassdef).getruntime()
 
+    def prepare_method(self, s_value):
+        # special-casing for methods:
+        #  - a class (read-only) attribute that would contain a PBC
+        #    with {func: classdef...} is probably meant to be used as a
+        #    method, but in corner cases it could be a constant object
+        #    of type MethodType that just sits here in the class.  But
+        #    as MethodType has a custom __get__ too and we don't support
+        #    it, it's a very bad idea anyway.
+        if isinstance(s_value, annmodel.SomePBC):
+            s_value = self.classdef.matching(s_value)
+            debound = {}
+            count = 0
+            for x, classdef in s_value.prebuiltinstances.items():
+                if isclassdef(classdef):
+                    #if classdef.commonbase(self.classdef) != self.classdef:
+                    #    raise TyperError("methods from PBC set %r don't belong "
+                    #                     "in %r" % (s_value.prebuiltinstances,
+                    #                                self.classdef.cls))
+                    count += 1
+                    classdef = True
+                debound[x] = classdef
+            if count > 0:
+                if count != len(s_value.prebuiltinstances):
+                    raise TyperError("mixing functions and methods "
+                                     "in PBC set %r" % (
+                        s_value.prebuiltinstances,))
+                return annmodel.SomePBC(debound)
+        return None   # not a method
+
     def get_ll_eq_function(self):
         return None
 

Modified: pypy/dist/pypy/rpython/rtyper.py
==============================================================================
--- pypy/dist/pypy/rpython/rtyper.py	(original)
+++ pypy/dist/pypy/rpython/rtyper.py	Mon Oct 17 20:07:36 2005
@@ -53,7 +53,7 @@
             def getconcretetype(v):
                 return self.bindingrepr(v).lowleveltype
 
-            return LowLevelTypeSystem.instance.getcallable(
+            return self.type_system.getcallable(
                         self.annotator.translator, graphfunc, getconcretetype)
         
         self.getfunctionptr = getfunctionptr



More information about the Pypy-commit mailing list