[pypy-svn] r6382 - in pypy/trunk/src/pypy/translator: . test

arigo at codespeak.net arigo at codespeak.net
Fri Sep 10 16:44:46 CEST 2004


Author: arigo
Date: Fri Sep 10 16:44:45 2004
New Revision: 6382

Modified:
   pypy/trunk/src/pypy/translator/classtyper.py
   pypy/trunk/src/pypy/translator/genc.h
   pypy/trunk/src/pypy/translator/genc.py
   pypy/trunk/src/pypy/translator/genc_op.py
   pypy/trunk/src/pypy/translator/genc_typeset.py
   pypy/trunk/src/pypy/translator/test/snippet.py
   pypy/trunk/src/pypy/translator/test/test_ctrans.py
Log:
Class attributes.  (Methods are the next step.)  This is done by storing the
attributes in an extension of the PyTypeObjects, as described in
genc.py.  There is a bit of code duplication between class and instance
attributes, that should be cleaned up sometime.

 --This line, and those below, will be ignored--

M    translator/test/test_ctrans.py
M    translator/test/snippet.py
M    translator/classtyper.py
M    translator/genc_typeset.py
M    translator/genc_op.py
M    translator/genc.h
M    translator/genc.py


Modified: pypy/trunk/src/pypy/translator/classtyper.py
==============================================================================
--- pypy/trunk/src/pypy/translator/classtyper.py	(original)
+++ pypy/trunk/src/pypy/translator/classtyper.py	Fri Sep 10 16:44:45 2004
@@ -97,6 +97,7 @@
     def get_management_functions(self):
         "Generate LLFunctions that operate on this class' structure."
         yield self.make_fn_new()
+        yield self.make_fn_typenew()
 
     def build_llfunc(self, graph):
         return LLFunction(self.typeset, graph.name, graph)
@@ -128,3 +129,21 @@
         self.bindings[graph.getreturnvar()] = self.bindings[v1]
         b.closeblock(Link([v1], graph.returnblock))
         return self.build_llfunc(graph)
+
+    def make_fn_typenew(self):
+        # generate the flow graph of the xxx_typenew() function that
+        # initializes the class attributes of the type object
+        b = Block([])
+        op = self.put_op(b)
+        cls = self.cdef.cls
+        v1 = Constant(cls)
+        # initialize class attributes
+        for fld in self.class_fields:
+            value = getattr(cls, fld.name)
+            op('initclassattr', v1, Constant(fld.name), Constant(value),
+               s_result = annmodel.SomeImpossibleValue())
+        # finally, return None
+        graph = FunctionGraph('%s_typenew' % self.name, b)
+        self.bindings[graph.getreturnvar()] = annmodel.immutablevalue(None)
+        b.closeblock(Link([Constant(None)], graph.returnblock))
+        return self.build_llfunc(graph)

Modified: pypy/trunk/src/pypy/translator/genc.h
==============================================================================
--- pypy/trunk/src/pypy/translator/genc.h	(original)
+++ pypy/trunk/src/pypy/translator/genc.h	Fri Sep 10 16:44:45 2004
@@ -97,12 +97,18 @@
 
 #define INSTANTIATE(cls, r, err)    if (!(r=cls##_new())) goto err;
 #define ALLOC_INSTANCE(cls, r, err)                             \
-		if (!(r=PyType_GenericAlloc(&cls##_Type, 0))) goto err;
+		if (!(r=PyType_GenericAlloc(&cls##_Type.type, 0))) goto err;
+#define SETUP_TYPE(cls)                         \
+		PyType_Ready(&cls##_Type.type); \
+		cls##_typenew();
 
 #define GET_ATTR_py(fld, r)   r=fld; Py_INCREF(r);
 #define SET_ATTR_py(fld, v)   { PyObject* o=fld; fld=v;         \
                                 Py_INCREF(v); Py_XDECREF(o); }
 
+#define GET_ATTR_cls(fld, r)  r=fld; Py_INCREF(r);
+#define SET_ATTR_cls(fld, v)  fld=v; Py_INCREF(v);  /* initialization only */
+
 /* a few built-in functions */
 
 #define CALL_len_oi(o,r,err)  if ((r=PyObject_Size(o))<0) goto err;

Modified: pypy/trunk/src/pypy/translator/genc.py
==============================================================================
--- pypy/trunk/src/pypy/translator/genc.py	(original)
+++ pypy/trunk/src/pypy/translator/genc.py	Fri Sep 10 16:44:45 2004
@@ -251,7 +251,7 @@
             'base': '0',
             }
         if llclass.llparent is not None:
-            info['base'] = '&%s_Type' % llclass.llparent.name
+            info['base'] = '&%s_Type.type' % llclass.llparent.name
 
         # print the C struct declaration
         print >> f, self.C_STRUCT_HEADER % info
@@ -260,6 +260,14 @@
                 print >> f, '\t%s %s;' % (llvar.type, llvar.name)
         print >> f, self.C_STRUCT_FOOTER % info
 
+        # print the struct Xxx_TypeObject, which is an extension of
+        # PyTypeObject with the class attributes of this class
+        print >> f, self.C_TYPESTRUCT_HEADER % info
+        for fld in llclass.class_fields:
+            for llvar in fld.llvars:
+                print >> f, '\t%s %s;' % (llvar.type, llvar.name)
+        print >> f, self.C_TYPESTRUCT_FOOTER % info
+
         # generate the deallocator function -- must special-case it;
         # other functions are generated by LLClass.get_management_functions()
         print >> f, self.C_DEALLOC_HEADER % info
@@ -293,7 +301,7 @@
         # declare and initialize the static PyTypeObject
         print >> f, self.C_TYPEOBJECT % info
 
-        self.initializationcode += (self.C_INITTYPE % info).split('\n')
+        self.initializationcode.append('SETUP_TYPE(%s)' % llclass.name)
 
 # ____________________________________________________________
 
@@ -332,7 +340,7 @@
     C_DEALLOC_HEADER = '''static void %(name)s_dealloc(%(name)s_Object* op)
 {'''
 
-    C_DEALLOC_FOOTER = '''	op->ob_type->tp_free((PyObject*) op);
+    C_DEALLOC_FOOTER = '''	PyObject_Del((PyObject*) op);
 }
 '''
 
@@ -342,7 +350,24 @@
 };
 '''
 
-    C_TYPEOBJECT = '''static PyTypeObject %(name)s_Type = {
+    # NB: our types don't have Py_TPFLAGS_BASETYPE because we do not want
+    #     the complications of dynamically created subclasses.  This doesn't
+    #     prevent the various types from inheriting from each other via
+    #     tp_base.  This is ok because we expect all RPython classes to exist
+    #     and be analyzed in advance.  This allows class attributes to be stored
+    #     as an extensison of the PyTypeObject structure, which are then
+    #     accessed with ((PyXxxTypeObject*)op->ob_type)->classattrname.
+    #     This doesn't work if op->ob_type can point to a heap-allocated
+    #     type object over which we have no control.
+
+    C_TYPESTRUCT_HEADER = '''typedef struct {
+	PyTypeObject type;
+	/* class attributes follow */'''
+
+    C_TYPESTRUCT_FOOTER = '''} %(name)s_TypeObject;
+'''
+
+    C_TYPEOBJECT = '''static %(name)s_TypeObject %(name)s_Type = {
 	PyObject_HEAD_INIT(&PyType_Type)
 	0,
 	"%(name)s",
@@ -363,7 +388,7 @@
 	PyObject_GenericGetAttr,		/* tp_getattro */
 	PyObject_GenericSetAttr,		/* tp_setattro */
 	0,					/* tp_as_buffer */
-	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,/* tp_flags */
+	Py_TPFLAGS_DEFAULT,			/* tp_flags */
  	0,					/* tp_doc */
  	0,	/* XXX need GC */		/* tp_traverse */
  	0,					/* tp_clear */
@@ -386,6 +411,4 @@
 };
 '''
 
-    C_INITTYPE = '''PyType_Ready(&%(name)s_Type);'''
-
 # ____________________________________________________________

Modified: pypy/trunk/src/pypy/translator/genc_op.py
==============================================================================
--- pypy/trunk/src/pypy/translator/genc_op.py	(original)
+++ pypy/trunk/src/pypy/translator/genc_op.py	Fri Sep 10 16:44:45 2004
@@ -158,25 +158,34 @@
         assert len(result) == len(self.fld.llvars)
         ls = []
         llclass = self.fld.llclass
-        for src, dstname in zip(self.fld.llvars, result):
-            fldexpr = '((%s_Object*) %s)->%s' % (llclass.name, inst,
-                                                 src.name)
-            if src.type == 'PyObject*':
-                ls.append('GET_ATTR_py(%s, %s)' % (fldexpr, dstname))
-            else:
-                ls.append('%s = %s;' % (dstname, fldexpr))
+        if self.fld.is_class_attr:
+            for src, dstname in zip(self.fld.llvars, result):
+                fldexpr = '((%s_TypeObject*)(%s->ob_type))->%s' % (
+                    llclass.name, inst, src.name)
+                if src.type == 'PyObject*':
+                    ls.append('GET_ATTR_cls(%s, %s)' % (fldexpr, dstname))
+                else:
+                    ls.append('%s = %s;' % (dstname, fldexpr))
+        else:
+            for src, dstname in zip(self.fld.llvars, result):
+                fldexpr = '((%s_Object*) %s)->%s' % (llclass.name, inst,
+                                                     src.name)
+                if src.type == 'PyObject*':
+                    ls.append('GET_ATTR_py(%s, %s)' % (fldexpr, dstname))
+                else:
+                    ls.append('%s = %s;' % (dstname, fldexpr))
         return '\n'.join(ls)
 
 class LoSetAttr(LoC):
-    cost = 1
-    fld  = PARAMETER
+    cost    = 1
+    llclass = PARAMETER   # the class involved in the operation
+    fld     = PARAMETER   # the field, which might come from a parent class
 
     def writestr(self, inst, *value):
         assert len(value) == len(self.fld.llvars)
         ls = []
-        llclass = self.fld.llclass
         for srcname, dst in zip(value, self.fld.llvars):
-            fldexpr = '((%s_Object*) %s)->%s' % (llclass.name, inst,
+            fldexpr = '((%s_Object*) %s)->%s' % (self.llclass.name, inst,
                                                  dst.name)
             if dst.type == 'PyObject*':
                 ls.append('SET_ATTR_py(%s, %s)' % (fldexpr, srcname))
@@ -184,6 +193,23 @@
                 ls.append('%s = %s;' % (fldexpr, srcname))
         return '\n'.join(ls)
 
+class LoInitClassAttr(LoC):
+    cost    = 1
+    llclass = PARAMETER   # the class involved in the operation
+    fld     = PARAMETER   # the field, which might come from a parent class
+
+    def writestr(self, *value):
+        assert len(value) == len(self.fld.llvars)
+        ls = []
+        # setting class attributes is only used for initialization
+        for srcname, dst in zip(value, self.fld.llvars):
+            fldexpr = '%s_Type.%s' % (self.llclass.name, dst.name)
+            if dst.type == 'PyObject*':
+                ls.append('SET_ATTR_cls(%s, %s)' % (fldexpr, srcname))
+            else:
+                ls.append('%s = %s;' % (fldexpr, srcname))
+        return '\n'.join(ls)
+
 class LoConvertChain(LoOptimized):
     r_from   = PARAMETER
     r_middle = PARAMETER

Modified: pypy/trunk/src/pypy/translator/genc_typeset.py
==============================================================================
--- pypy/trunk/src/pypy/translator/genc_typeset.py	(original)
+++ pypy/trunk/src/pypy/translator/genc_typeset.py	Fri Sep 10 16:44:45 2004
@@ -180,11 +180,14 @@
         if len(hltypes) != 3:
             return
         r_obj, r_attr, r_result = hltypes
-        if isinstance(r_obj, CInstance) and isinstance(r_attr, CConstant):
+        if not isinstance(r_attr, CConstant):
+            return
+        if isinstance(r_obj, CInstance):
             # record the OP_GETATTR operation for this field
-            fld = r_obj.llclass.get_instance_field(r_attr.value)
+            fld = (r_obj.llclass.get_instance_field(r_attr.value) or
+                   r_obj.llclass.get_class_field(r_attr.value))
             if fld is not None:
-                sig = (r_obj, constant_representation(fld.name), fld.hltype)
+                sig = (r_obj, r_attr, fld.hltype)
                 yield sig, genc_op.LoGetAttr.With(
                     fld     = fld,
                     )
@@ -193,16 +196,35 @@
         if len(hltypes) != 4:
             return
         r_obj, r_attr, r_value, r_voidresult = hltypes
+        if not isinstance(r_attr, CConstant):
+            return
         if isinstance(r_obj, CInstance):
             # record the OP_SETATTR operation for this field
             fld = r_obj.llclass.get_instance_field(r_attr.value)
             if fld is not None:
-                sig = (r_obj, constant_representation(fld.name), fld.hltype,
-                       R_VOID)
+                sig = (r_obj, r_attr, fld.hltype, R_VOID)
                 yield sig, genc_op.LoSetAttr.With(
                     fld     = fld,
+                    llclass = r_obj.llclass,
                     )
 
+    def extend_OP_INITCLASSATTR(self, hltypes):
+        # only to initialize class attributes
+        if len(hltypes) != 4:
+            return
+        r_obj, r_attr, r_value, r_voidresult = hltypes
+        if isinstance(r_attr, CConstant) and isinstance(r_obj, CConstant):
+            cls = r_obj.value
+            if cls in self.genc.llclasses:
+                llclass = self.genc.llclasses[cls]
+                fld = llclass.get_class_field(r_attr.value)
+                if fld is not None:
+                    sig = (r_obj, r_attr, fld.hltype, R_VOID)
+                    yield sig, genc_op.LoInitClassAttr.With(
+                        fld     = fld,
+                        llclass = llclass,
+                        )
+
     # ____________________________________________________________
 
     def parse_operation_templates(self):

Modified: pypy/trunk/src/pypy/translator/test/snippet.py
==============================================================================
--- pypy/trunk/src/pypy/translator/test/snippet.py	(original)
+++ pypy/trunk/src/pypy/translator/test/snippet.py	Fri Sep 10 16:44:45 2004
@@ -319,6 +319,30 @@
 def prime(n=int):
     return len([i for i in range(1,n+1) if n%i==0]) == 2
 
+class A1:
+    clsattr = 123
+class A2(A1):
+    clsattr = 456
+class A3(A2):
+    clsattr = 789
+class A4(A3):
+    pass
+class A5(A1):
+    clsattr = 101112
+
+def classattribute(flag=int):
+    if flag == 1:
+        x = A1()
+    elif flag == 2:
+        x = A2()
+    elif flag == 3:
+        x = A3()
+    elif flag == 4:
+        x = A4()
+    else:
+        x = A5()
+    return x.clsattr
+
 
 class Z:
     def my_method(self):

Modified: pypy/trunk/src/pypy/translator/test/test_ctrans.py
==============================================================================
--- pypy/trunk/src/pypy/translator/test/test_ctrans.py	(original)
+++ pypy/trunk/src/pypy/translator/test/test_ctrans.py	Fri Sep 10 16:44:45 2004
@@ -151,5 +151,13 @@
         fn = self.getcompiled(tuple_repr)
         self.assertEquals(fn(6,'a'), (6,'a'))
 
+    def test_classattribute(self):
+        fn = self.getcompiled(snippet.classattribute)
+        self.assertEquals(fn(1), 123)
+        self.assertEquals(fn(2), 456)
+        self.assertEquals(fn(3), 789)
+        self.assertEquals(fn(4), 789)
+        self.assertEquals(fn(5), 101112)
+
 if __name__ == '__main__':
     testit.main()



More information about the Pypy-commit mailing list