[pypy-svn] r20284 - in pypy/branch/somepbc-refactoring/pypy: annotation rpython rpython/lltypesystem rpython/test

arigo at codespeak.net arigo at codespeak.net
Sat Nov 26 17:43:31 CET 2005


Author: arigo
Date: Sat Nov 26 17:43:30 2005
New Revision: 20284

Modified:
   pypy/branch/somepbc-refactoring/pypy/annotation/builtin.py
   pypy/branch/somepbc-refactoring/pypy/annotation/model.py
   pypy/branch/somepbc-refactoring/pypy/rpython/lltypesystem/rbuiltin.py
   pypy/branch/somepbc-refactoring/pypy/rpython/lltypesystem/rclass.py
   pypy/branch/somepbc-refactoring/pypy/rpython/lltypesystem/rpbc.py
   pypy/branch/somepbc-refactoring/pypy/rpython/normalizecalls.py
   pypy/branch/somepbc-refactoring/pypy/rpython/rtyper.py
   pypy/branch/somepbc-refactoring/pypy/rpython/test/test_rclass.py
Log:
(pedronis, arigo)

Re-support calling a variable containing a class object.  This is even somehow
saner as before, but it still took serious time to fix :-/

Added a test.

Cleaned up some unused code.

The "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARGH bouh" comment is gone :-)


Modified: pypy/branch/somepbc-refactoring/pypy/annotation/builtin.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/annotation/builtin.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/annotation/builtin.py	Sat Nov 26 17:43:30 2005
@@ -240,6 +240,7 @@
     if len(s_clspbc.prebuiltinstances) > 1:
         for cls in s_clspbc.prebuiltinstances:
             getbookkeeper().needs_generic_instantiate[cls] = True
+            XXX_FixMe
     return SomeInstance(clsdef)
 
 def robjmodel_we_are_translated():

Modified: pypy/branch/somepbc-refactoring/pypy/annotation/model.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/annotation/model.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/annotation/model.py	Sat Nov 26 17:43:30 2005
@@ -277,7 +277,7 @@
 
     def __init__(self, classdef, can_be_None=False):
         self.classdef = classdef
-        self.knowntype = classdef
+        self.knowntype = classdef or object
         self.can_be_None = can_be_None
 
     def fmt_knowntype(self, kt):

Modified: pypy/branch/somepbc-refactoring/pypy/rpython/lltypesystem/rbuiltin.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/rpython/lltypesystem/rbuiltin.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/rpython/lltypesystem/rbuiltin.py	Sat Nov 26 17:43:30 2005
@@ -37,7 +37,7 @@
     else:
         return hop.gendirectcall(rclass.ll_isinstance, v_obj, v_cls)
 
-def ll_instantiate(typeptr):
+def ll_instantiate(typeptr):   # NB. used by rpbc.ClassesPBCRepr as well
     my_instantiate = typeptr.instantiate
     return my_instantiate()
 

Modified: pypy/branch/somepbc-refactoring/pypy/rpython/lltypesystem/rclass.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/rpython/lltypesystem/rclass.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/rpython/lltypesystem/rclass.py	Sat Nov 26 17:43:30 2005
@@ -178,9 +178,9 @@
             for i in range(len(name)):
                 vtable.name[i] = name[i]
             vtable.name[len(name)] = '\x00'
-            if hasattr(rsubcls.classdef, 'my_instantiate'):
-                fn = rsubcls.classdef.my_instantiate
-                vtable.instantiate = self.rtyper.getfunctionptr(fn)
+            if hasattr(rsubcls.classdef, 'my_instantiate_graph'):
+                graph = rsubcls.classdef.my_instantiate_graph
+                vtable.instantiate = self.rtyper.getcallable(graph)
             #else: the classdef was created recently, so no instantiate()
             #      could reach it
         else:

Modified: pypy/branch/somepbc-refactoring/pypy/rpython/lltypesystem/rpbc.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/rpython/lltypesystem/rpbc.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/rpython/lltypesystem/rpbc.py	Sat Nov 26 17:43:30 2005
@@ -239,24 +239,33 @@
         return self.redispatch_call(hop, call_args=True)
 
     def redispatch_call(self, hop, call_args):
-        if self.lowleveltype is not Void:
+        s_instance = hop.s_result
+        r_instance = hop.r_result
+
+        if self.lowleveltype is Void:
+            # instantiating a single class
+            assert isinstance(s_instance, annmodel.SomeInstance)
+            classdef = hop.s_result.classdef
+            v_instance = rclass.rtype_new_instance(hop.rtyper, classdef,
+                                                   hop.llops)
+            s_init = classdef.classdesc.s_read_attribute('__init__')
+            v_init = Constant("init-func-dummy")   # this value not really used
+        else:
             # instantiating a class from multiple possible classes
-            vcls = hop.inputarg(self, arg=0)
+            from pypy.rpython.lltypesystem.rbuiltin import ll_instantiate
+            vtypeptr = hop.inputarg(self, arg=0)
             access_set = self.get_access_set()
-            vnewfn = self.get_class_repr().getpbcfield(vcls, access_set,
-                                                       '__new__', hop.llops)
-            hop2 = hop.copy()
-            hop2.r_s_popfirstarg()   # discard the class pointer argument
-            hop2.v_s_insertfirstarg(vnewfn, access_set.attrs['__new__'])
-            # now hop2 looks like simple_call(klass__new__, args...)
-            return hop2.dispatch()
+            r_class = self.get_class_repr()
+            if '__init__' in access_set.attrs:
+                s_init = access_set.attrs['__init__']
+                v_init = r_class.getpbcfield(vtypeptr, access_set, '__init__',
+                                             hop.llops)
+            else:
+                s_init = annmodel.s_ImpossibleValue
+            v_inst1 = hop.gendirectcall(ll_instantiate, vtypeptr)
+            v_instance = hop.genop('cast_pointer', [v_inst1],
+                                   resulttype = r_instance)
 
-        # instantiating a single class
-        s_instance = hop.s_result
-        assert isinstance(s_instance, annmodel.SomeInstance)
-        classdef = hop.s_result.classdef
-        v_instance = rclass.rtype_new_instance(hop.rtyper, classdef, hop.llops)
-        s_init = classdef.classdesc.s_read_attribute('__init__')
         if isinstance(s_init, annmodel.SomeImpossibleValue):
             assert hop.nb_args == 1, ("arguments passed to __init__, "
                                       "but no __init__!")
@@ -269,8 +278,7 @@
                 adjust_shape(hop2, s_shape)
             else:
                 hop2.v_s_insertfirstarg(v_instance, s_instance)  # add 'instance'
-            c = Constant("init-func-dummy")   # this value not really used
-            hop2.v_s_insertfirstarg(c, s_init)   # add 'initfunc'
+            hop2.v_s_insertfirstarg(v_init, s_init)   # add 'initfunc'
             hop2.s_result = annmodel.s_None
             hop2.r_result = self.rtyper.getrepr(hop2.s_result)
             # now hop2 looks like simple_call(initfunc, instance, args...)
@@ -297,14 +305,14 @@
 
 # ____________________________________________________________
 
-def rtype_call_memo(hop): 
-    memo_table = hop.args_v[0].value
-    if memo_table.s_result.is_constant():
-        return hop.inputconst(hop.r_result, memo_table.s_result.const)
-    fieldname = memo_table.fieldname 
-    assert hop.nb_args == 2, "XXX"  
-
-    r_pbc = hop.args_r[1]
-    assert isinstance(r_pbc, (MultipleFrozenPBCRepr, ClassesPBCRepr))
-    v_table, v_pbc = hop.inputargs(Void, r_pbc)
-    return r_pbc.getfield(v_pbc, fieldname, hop.llops)
+##def rtype_call_memo(hop): 
+##    memo_table = hop.args_v[0].value
+##    if memo_table.s_result.is_constant():
+##        return hop.inputconst(hop.r_result, memo_table.s_result.const)
+##    fieldname = memo_table.fieldname 
+##    assert hop.nb_args == 2, "XXX"  
+
+##    r_pbc = hop.args_r[1]
+##    assert isinstance(r_pbc, (MultipleFrozenPBCRepr, ClassesPBCRepr))
+##    v_table, v_pbc = hop.inputargs(Void, r_pbc)
+##    return r_pbc.getfield(v_pbc, fieldname, hop.llops)

Modified: pypy/branch/somepbc-refactoring/pypy/rpython/normalizecalls.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/rpython/normalizecalls.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/rpython/normalizecalls.py	Sat Nov 26 17:43:30 2005
@@ -2,7 +2,7 @@
 import types
 import inspect
 from pypy.objspace.flow.model import Variable, Constant, Block, Link
-from pypy.objspace.flow.model import checkgraph
+from pypy.objspace.flow.model import checkgraph, FunctionGraph, SpaceOperation
 from pypy.annotation import model as annmodel
 from pypy.annotation import description
 from pypy.tool.sourcetools import has_varargs, valid_identifier
@@ -15,65 +15,6 @@
     for callfamily in annotator.bookkeeper.pbc_maximal_call_families.infos():
        normalize_calltable(annotator, callfamily)
 
-##def normalize_function_signatures(annotator):
-##    """Make sure that all functions called in a group have exactly
-##    the same signature, by hacking their flow graphs if needed.
-##    """
-##    call_families = annotator.getpbccallfamilies()
-
-##    # for methods, we create or complete a corresponding function-only
-##    # family with call patterns that have the extra 'self' argument
-##    for family in call_families.infos():
-##        prevkey = None
-##        for classdef, func in family.objects:
-##            if classdef is not None:
-##                # add (None, func) to the func_family
-##                if prevkey is None:
-##                    prevkey = (None, func)
-##                else:
-##                    call_families.union((None, func), prevkey)
-##        if prevkey is not None:
-##            # copy the patterns from family to func_family with the
-##            # extra self argument
-##            _, _, func_family = call_families.find(prevkey)
-##            for pattern in family.patterns:
-##                argcount = pattern[0]
-##                pattern = (argcount+1,) + pattern[1:]
-##                func_family.patterns[pattern] = True
-
-##    # for bound method objects, make sure the im_func shows up too.
-##    for family in call_families.infos():
-##        first = family.objects.keys()[0][1]
-##        for _, callable in family.objects:
-##            if isinstance(callable, types.MethodType):
-##                # for bound methods families for now just accept and check that
-##                # they all refer to the same function
-##                if not isinstance(first, types.MethodType):
-##                    raise TyperError("call family with both bound methods and "
-##                                     "%r" % (first,))
-##                if first.im_func is not callable.im_func:
-##                    raise TyperError("call family of bound methods: should all "
-##                                     "refer to the same function")
-##        # create a family for the common im_func
-##        if isinstance(first, types.MethodType):
-##            _, _, func_family = call_families.find((None, first.im_func))
-##            for pattern in family.patterns:
-##                argcount = pattern[0]
-##                pattern = (argcount+1,) + pattern[1:]
-##                func_family.patterns[pattern] = True
-
-##    # find the most general signature of each family
-##    for family in call_families.infos():
-##        # collect functions in this family, ignoring:
-##        #  - methods: taken care of above
-##        #  - bound methods: their im_func will also show up
-##        #  - classes: already handled by create_class_constructors()
-##        functions = [func for classdef, func in family.objects
-##                          if classdef is None and
-##                not isinstance(func, (type, types.ClassType, types.MethodType))]
-##        if len(functions) > 1:  # otherwise, nothing to do
-
-
 def normalize_calltable(annotator, callfamily):
     """Try to normalize all rows of a table."""
     nshapes = len(callfamily.calltables)
@@ -92,7 +33,6 @@
         if not progress:
             return   # done
 
-
 def normalize_calltable_row_signature(annotator, shape, row):
     graphs = row.values()
     assert graphs, "no graph??"
@@ -242,6 +182,7 @@
 
     return conversion
 
+# ____________________________________________________________
 
 def merge_classpbc_getattr_into_classdef(rtyper):
     # code like 'some_class.attr' will record an attribute access in the
@@ -253,16 +194,8 @@
         descs = access_set.descs
         if len(descs) <= 1:
             continue
-        count = 0
-        for desc in descs:
-            if isinstance(desc, description.ClassDesc):
-                count += 1
-        if count == 0:
+        if not isinstance(descs.iterkeys().next(), description.ClassDesc):
             continue
-        if count != len(descs):
-            raise TyperError("reading attributes %r: mixing instantiated "
-                             "classes with something else in %r" % (
-                access_set.attrs.keys(), descs.keys()))
         classdefs = [desc.getuniqueclassdef() for desc in descs]
         commonbase = classdefs[0]
         for cdef in classdefs[1:]:
@@ -276,118 +209,63 @@
                                                                    {})
         extra_access_sets[access_set] = len(extra_access_sets)
 
-def create_class_constructors(rtyper):
-    # for classes that appear in families, make a __new__ PBC attribute.
-    call_families = rtyper.annotator.getpbccallfamilies()
-    access_sets = rtyper.annotator.getpbcaccesssets()
+# ____________________________________________________________
+
+def create_class_constructors(annotator):
+    bk = annotator.bookkeeper
+    call_families = bk.pbc_maximal_call_families
 
     for family in call_families.infos():
-        if len(family.objects) <= 1:
+        if len(family.descs) <= 1:
             continue
-        count = 0
-        for _, klass in family.objects:
-            if isinstance(klass, (type, types.ClassType)):
-                count += 1
-        if count == 0:
+        descs = family.descs.keys()
+        if not isinstance(descs[0], description.ClassDesc):
             continue
-        if count != len(family.objects):
-            raise TyperError("calls to mixed class/non-class objects in the "
-                             "family %r" % family.objects.keys())
-
-        patterns = family.patterns.copy()
-
-        klasses = [klass for (_, klass) in family.objects.keys()]
-        functions = {}
-        function_values = {}
-        for klass in klasses:
-            try:
-                initfunc = klass.__init__.im_func
-            except AttributeError:
-                initfunc = None
-            # XXX AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARGH
-            #     bouh.
-            if initfunc:
-                args, varargs, varkw, defaults = inspect.getargspec(initfunc)
-            else:
-                args, varargs, varkw, defaults = ('self',), None, None, ()
-            args = list(args)
-            args2 = args[:]
-            if defaults:
-                for i in range(-len(defaults), 0):
-                    args[i] += '=None'
-            if varargs:
-                args.append('*%s' % varargs)
-                args2.append('*%s' % varargs)
-            if varkw:
-                args.append('**%s' % varkw)
-                args2.append('**%s' % varkw)
-            args.pop(0)   # 'self'
-            args2.pop(0)   # 'self'
-            funcsig = ', '.join(args)
-            callsig = ', '.join(args2)
-            klass_name = valid_identifier(klass.__name__)
-            source = py.code.Source('''
-                def %s__new__(%s):
-                    return ____class(%s)
-            ''' % (
-                klass_name, funcsig, callsig))
-            miniglobals = {
-                '____class': klass,
-                }
-            exec source.compile() in miniglobals
-            klass__new__ = miniglobals['%s__new__' % klass_name]
-            if initfunc:
-                klass__new__.func_defaults = initfunc.func_defaults
-                graph = rtyper.annotator.translator.getflowgraph(initfunc)
-                args_s = [rtyper.annotator.binding(v) for v in graph.getargs()]
-                args_s.pop(0)   # 'self'
-            else:
-                args_s = []
-            rtyper.annotator.build_types(klass__new__, args_s)
-            functions[klass__new__] = True
-            function_values[klass, '__new__'] = klass__new__
-
-        _, _, access_set = access_sets.find(klasses[0])
-        for klass in klasses[1:]:
-            _, _, access_set = access_sets.union(klasses[0], klass)
-        if '__new__' in access_set.attrs:
-            raise TyperError("PBC access set for classes %r already contains "
-                             "a __new__" % (klasses,))
-        access_set.attrs['__new__'] = annmodel.SomePBC(functions)
-        access_set.values.update(function_values)
-
-        # make a call family for 'functions', copying the call family for
-        # 'klasses'
-        functionslist = functions.keys()
-        key0 = None, functionslist[0]
-        _, _, new_call_family = call_families.find(key0)
-        for klass__new__ in functionslist[1:]:
-            _, _, new_call_family = call_families.union(key0,
-                                                        (None, klass__new__))
-        new_call_family.patterns = patterns
+        # Note that a callfamily of classes must really be in the same
+        # attrfamily as well; This property is relied upon on various
+        # places in the rtyper
+        descs[0].mergeattrfamilies(*descs[1:])
+        attrfamily = descs[0].getattrfamily()
+        # Put __init__ into the attr family, for ClassesPBCRepr.call()
+        s_value = attrfamily.attrs.get('__init__', annmodel.s_ImpossibleValue)
+        inits_s = [desc.s_read_attribute('__init__') for desc in descs]
+        s_value = annmodel.unionof(s_value, *inits_s)
+        attrfamily.attrs['__init__'] = s_value
+        # ClassesPBCRepr.call() will also need instantiate() support
+        for desc in descs:
+            bk.needs_generic_instantiate[desc.getuniqueclassdef()] = True
 
+# ____________________________________________________________
 
 def create_instantiate_functions(annotator):
     # build the 'instantiate() -> instance of C' functions for the vtables
 
     needs_generic_instantiate = annotator.bookkeeper.needs_generic_instantiate
     
-    for cls, classdef in annotator.getuserclasses().items():
-        if cls in needs_generic_instantiate:
-            assert needsgc(classdef) # only gc-case            
-            create_instantiate_function(annotator, cls, classdef)
-
-def create_instantiate_function(annotator, cls, classdef):
-    def my_instantiate():
-        return instantiate(cls)
-    my_instantiate = func_with_new_name(my_instantiate,
-                                valid_identifier('instantiate_'+cls.__name__))
-    annotator.build_types(my_instantiate, [])
+    for classdef in needs_generic_instantiate:
+        assert needsgc(classdef) # only gc-case
+        create_instantiate_function(annotator, classdef)
+
+def create_instantiate_function(annotator, classdef):
+    # build the graph of a function that looks like
+    # 
+    # def my_instantiate():
+    #     return instantiate(cls)
+    #
+    v = Variable()
+    block = Block([])
+    block.operations.append(SpaceOperation('instantiate1', [], v))
+    name = valid_identifier('instantiate_'+classdef.name)
+    graph = FunctionGraph(name, block)
+    block.closeblock(Link([v], graph.returnblock))
+    annotator.setbinding(v, annmodel.SomeInstance(classdef))
+    annotator.annotated[block] = graph
     # force the result to be converted to a generic OBJECTPTR
     generalizedresult = annmodel.SomeInstance(classdef=None)
-    graph = annotator.translator.getflowgraph(my_instantiate)
     annotator.setbinding(graph.getreturnvar(), generalizedresult)
-    classdef.my_instantiate = my_instantiate
+    classdef.my_instantiate_graph = graph
+
+# ____________________________________________________________
 
 def assign_inheritance_ids(annotator):
     def assign_id(classdef, nextid):
@@ -401,16 +279,16 @@
     for classdef in annotator.bookkeeper.classdefs:
         if classdef.basedef is None:
             id_ = assign_id(classdef, id_)
-        
+
+# ____________________________________________________________
+
 def perform_normalizations(rtyper):
-    #XXX later: create_class_constructors(rtyper)
+    create_class_constructors(rtyper.annotator)
     rtyper.annotator.frozen += 1
     try:
         normalize_call_familes(rtyper.annotator)
-        #XXX later: normalize_function_signatures(rtyper.annotator)
-        #XXX later: specialize_pbcs_by_memotables(rtyper.annotator) 
         merge_classpbc_getattr_into_classdef(rtyper)
         assign_inheritance_ids(rtyper.annotator)
     finally:
         rtyper.annotator.frozen -= 1
-    #XXX later: create_instantiate_functions(rtyper.annotator)
+    create_instantiate_functions(rtyper.annotator)

Modified: pypy/branch/somepbc-refactoring/pypy/rpython/rtyper.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/rpython/rtyper.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/rpython/rtyper.py	Sat Nov 26 17:43:30 2005
@@ -516,11 +516,12 @@
     def translate_op_newslice(self, hop):
         return rslice.rtype_newslice(hop)
 
-    def translate_op_call_memo(self, hop):
-        return self.type_system.rpbc.rtype_call_memo(hop)
-
-    def translate_op_call_specialcase(self, hop):
-        return rspecialcase.rtype_call_specialcase(hop)
+    def translate_op_instantiate1(self, hop):
+        from pypy.rpython.lltypesystem import rclass
+        if not isinstance(hop.s_result, annmodel.SomeInstance):
+            raise TyperError("instantiate1 got s_result=%r" % (hop.s_result,))
+        classdef = hop.s_result.classdef
+        return rclass.rtype_new_instance(self, classdef, hop.llops)
 
     def missing_operation(self, hop):
         raise TyperError("unimplemented operation: '%s'" % hop.spaceop.opname)

Modified: pypy/branch/somepbc-refactoring/pypy/rpython/test/test_rclass.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/rpython/test/test_rclass.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/rpython/test/test_rclass.py	Sat Nov 26 17:43:30 2005
@@ -331,3 +331,27 @@
         return e.attr()
     res = interpret(f, [])
     assert res == 42
+
+def test_getattr_on_classes():
+    class A:
+        def meth(self):
+            return self.value + 42
+    class B(A):
+        def meth(self):
+            shouldnt**be**seen
+    class C(B):
+        def meth(self):
+            return self.value - 1
+    def f(i):
+        if i > 0:
+            cls = A
+        else:
+            cls = C
+        meth = cls.meth
+        x = C()
+        x.value = 12
+        return meth(x)   # calls A.meth or C.meth, completely ignores B.meth
+    res = interpret(f, [1])
+    assert res == 54
+    res = interpret(f, [0])
+    assert res == 11



More information about the Pypy-commit mailing list