Commits

Alexander Schremmer committed 9e700f1

Add HOp repr.

  • Participants
  • Parent commits 83116b2
  • Branches sepcomp

Comments (0)

Files changed (9)

File pypy/rpython/lltypesystem/lltype.py

 
     _is_compatible = __eq__
 
+    def __getstate__(self):
+        return self.__dict__
+
+    def __setstate__(self, val):
+        self.__dict__.update(val)
+
     def _enforce(self, value):
         if typeOf(value) != self:
             raise TypeError
             self.__cached_hash = result
         return result
 
-    # due to this dynamic hash value, we should forbid
-    # pickling, until we have an algorithm for that.
-    # but we just provide a tag for external help.
-    __hash_is_not_constant__ = True
-
     def __repr__(self):
         return '<%s>' % (self,)
 
 
 class _container(object):
     __slots__ = ()
+    _exported = False
     def _parentstructure(self, check=True):
         return None
     def _check(self):
 
     __slots__ = ('_TYPE',
                  '_parent_type', '_parent_index', '_keepparent',
-                 '_wrparent',
+                 '_wrparent',"_exported",
                  '__weakref__',
                  '_storage')
 
             return id(self)
 
     def __setattr__(self, attr, value):
-        raise AttributeError("cannot change the attributes of %r" % (self,))
+        if attr != "_exported":
+            raise AttributeError("cannot change the attributes of %r" % (self,))
+        _container.__setattr__(self, attr, value)
 
 class _opaque(_parentable):
     def __init__(self, TYPE, parent=None, parentindex=None, **attrs):
             return _parentable._normalizedcontainer(self)
 
 
+class _external_reference(_container):
+    def __init__(self, TYPE, name, component=None):
+        self._TYPE = TYPE
+        self.name = name
+        self.component = component
+        self.iddata = str(component) + name
+
+    def _getid(self):
+        return id(self.iddata)
+
+
 class _pyobject(Hashable, _container):
-    __slots__ = []   # or we get in trouble with pickling
 
     _TYPE = PyObject
 
     o = _pyobject(obj)
     return _ptr(Ptr(PyObject), o) 
 
+def externalptr(TYPE, name, component=None):
+    o = _external_reference(TYPE, name, component)
+    return _ptr(Ptr(TYPE), o, solid=True)
+
 def cast_ptr_to_int(ptr):
     return ptr._cast_to_int()
 

File pypy/rpython/lltypesystem/rclass.py

      cast_pointer, cast_ptr_to_int, castable, nullptr, \
      RuntimeTypeInfo, getRuntimeTypeInfo, typeOf, \
      Array, Char, Void, attachRuntimeTypeInfo, \
-     FuncType, Bool, Signed, functionptr, FuncType, PyObject
+     FuncType, Bool, Signed, functionptr, FuncType, PyObject, \
+     normalizeptr
 from pypy.rpython.lltypesystem import lltype
 from pypy.rpython.robject import PyObjRepr, pyobj_repr
 from pypy.rpython.extregistry import ExtRegistryEntry
 OBJECTPTR = Ptr(OBJECT)
 OBJECT_VTABLE.become(Struct('object_vtable',
                             #('parenttypeptr', CLASSTYPE),
-                            ('subclassrange_min', Signed),
-                            ('subclassrange_max', Signed),
+                            ('level', Signed),
+                            ('classrow', Ptr(Array(CLASSTYPE))),
                             ('rtti', Ptr(RuntimeTypeInfo)),
                             ('name', Ptr(Array(Char))),
                             ('instantiate', Ptr(FuncType([], OBJECTPTR))),
     return vtable
 
 
+
+def weave_llfields(llfields, classinfo, types_only=False):
+    llfields_iter = iter(llfields)
+    result = []
+    for mangled_name, name, value in classinfo:
+        if name is not None:
+            curfield = llfields_iter.next()
+            while curfield is not None and curfield[0] != mangled_name: # XXX does this make sense?
+                result.append((mangled_name, value.get_func()))
+                try:
+                    curfield = llfields_iter.next()
+                except StopIteration:
+                    curfield = None
+            if curfield is not None:
+                result.append(curfield)
+        else:
+            if not types_only:
+                value = typeOf(value)
+            result.append((mangled_name, value))
+    try:
+        llfields_iter.next()
+    except StopIteration:
+        pass
+    else:
+        raise TyperError("Incorrect set of llfields")
+    return result
+
+def extend_exportinfo(exportinfo, mangled_name, name, r, classdesc=None):
+    T = r.lowleveltype
+    if classdesc is None:
+        llvalue = T
+        var = None
+    else:
+        var = classdesc.read_attribute(name, None)
+        llvalue = r.convert_desc_or_const(var)
+    #import pdb; pdb.set_trace()
+    if var is None or not getattr(var.value, '_export_', None):
+        name = None # hide
+        val = llvalue
+    else:
+        val = var.value
+    exportinfo.append((mangled_name, name, val))
+
+
 class ClassRepr(AbstractClassRepr):
     def __init__(self, rtyper, classdef):
         AbstractClassRepr.__init__(self, rtyper, classdef)
         self.lowleveltype = Ptr(self.vtable_type)
 
     def _setup_repr(self):
+        exportinfo = None
         # NOTE: don't store mutable objects like the dicts below on 'self'
         #       before they are fully built, to avoid strange bugs in case
         #       of recursion where other code would uses these
         pbcfields = {}
         allmethods = {}
         if self.classdef is not None:
+            if self.classdef.is_exported():
+                exportinfo = []
+            importinfo = self.classdef.get_import_data()
             # class attributes
             llfields = []
             attrs = self.classdef.attrs.items()
                         allmethods[name] = True
                         s_value = s_unboundmethod
                     r = self.rtyper.getrepr(s_value)
+                    #if "Multi" in repr(r) or ("Void" in repr(r.lowleveltype) and name in ("foo", "bar") and "foovoid" not in repr(self)):
+                    #    import pdb; pdb.set_trace()
                     mangled_name = 'cls_' + name
                     clsfields[name] = mangled_name, r
                     llfields.append((mangled_name, r.lowleveltype))
+                    if exportinfo is not None and r.lowleveltype is not Void:
+                        extend_exportinfo(exportinfo, mangled_name, name, r, self.classdef.classdesc)
             # attributes showing up in getattrs done on the class as a PBC
             extra_access_sets = self.rtyper.class_pbc_attributes.get(
                 self.classdef, {})
                 mangled_name = mangle('pbc%d' % counter, attr)
                 pbcfields[access_set, attr] = mangled_name, r
                 llfields.append((mangled_name, r.lowleveltype))
+                if exportinfo is not None:
+                    assert 0, "not yet supported"
             #
+            if importinfo:
+                #import pdb; pdb.set_trace()
+                llfields = weave_llfields(llfields, importinfo.cls)
             self.rbase = getclassrepr(self.rtyper, self.classdef.basedef)
             self.rbase.setup()
             kwds = {'hints': {'immutable': True}}
                                  ('super', self.rbase.vtable_type),
                                  *llfields, **kwds)
             self.vtable_type.become(vtable_type)
+            if exportinfo is not None:
+                self.classdef.exportinfo_cls = exportinfo
             allmethods.update(self.rbase.allmethods)
         self.clsfields = clsfields
         self.pbcfields = pbcfields
     def getvtable(self, cast_to_typeptr=True):
         """Return a ptr to the vtable of this type."""
         if self.vtable is None:
-            self.vtable = malloc(self.vtable_type, immortal=True)
-            self.setup_vtable(self.vtable, self)
+            if self.classdef and self.classdef.get_import_data():
+                #from pypy.rpython.lltypesystem.rffi import CConstant
+                handle = self.classdef.get_import_data().vtablename
+                component = None
+                if isinstance(handle, tuple):
+                    component, handle = handle
+                #T = lltype.Ptr(lltype.OpaqueType(handle, hints=dict(external_void=True)))
+                #val = CConstant("&" + handle, T)
+                self.vtable = lltype.externalptr(OBJECT_VTABLE, handle + "_vtable", component)
+                #self.vtable = val
+                cast_to_typeptr = False
+            else:
+                self.vtable = malloc(self.vtable_type, immortal=True)
+                self.setup_vtable(self.vtable, self)
         #
         vtable = self.vtable
         if cast_to_typeptr:
             # initialize the 'subclassrange_*' and 'name' fields
             if rsubcls.classdef is not None:
                 #vtable.parenttypeptr = rsubcls.rbase.getvtable()
-                vtable.subclassrange_min = rsubcls.classdef.minid
-                vtable.subclassrange_max = rsubcls.classdef.maxid
+                vtable.level = rsubcls.classdef.level
+                vtable.classrow = malloc(Array(CLASSTYPE), rsubcls.classdef.level + 1, immortal=True)
+                cur_repr = rsubcls
+                for i in xrange(rsubcls.classdef.level, -1, -1):
+                    vtable.classrow[i] = cur_repr.getvtable()
+                    cur_repr = cur_repr.rbase
+
+                if rsubcls.classdef.is_exported():
+                    rsubcls.classdef.exportinfo_vtableval = normalizeptr(vtable)
             else: #for the root class
-                vtable.subclassrange_min = 0
-                vtable.subclassrange_max = sys.maxint
+                vtable.level = -1
+                vtable.classrow = nullptr(Array(CLASSTYPE))
             rinstance = getinstancerepr(self.rtyper, rsubcls.classdef)
             rinstance.setup()
             if rinstance.gcflavor == 'gc':
             #else: the classdef was created recently, so no instantiate()
             #      could reach it
         else:
+            importinfo = self.classdef.get_import_data()
             # setup class attributes: for each attribute name at the level
             # of 'self', look up its value in the subclass rsubcls
             def assign(mangled_name, value):
                 llvalue = r.convert_desc_or_const(value)
                 setattr(vtable, mangled_name, llvalue)
 
-            mro = list(rsubcls.classdef.getmro())
+            mro = list(rsubcls.classdef.getmro()) # XXX not needed?
             for fldname in self.clsfields:
                 mangled_name, r = self.clsfields[fldname]
                 if r.lowleveltype is Void:
                 attrvalue = rsubcls.classdef.classdesc.read_attribute(attr, None)
                 if attrvalue is not None:
                     assign(mangled_name, attrvalue)
+            if importinfo:
+                for mangled_name, name, val in importinfo.cls:
+                    if not name:
+                        setattr(vtable, mangled_name, val)
 
             # then initialize the 'super' portion of the vtable
             self.rbase.setup_vtable(vtable.super, rsubcls)
 ##                # a class with no subclass
 ##                return hop.genop('ptr_eq', [v_cls1, v_cls2], resulttype=Bool)
 ##            else:
-            minid = hop.inputconst(Signed, cls2.subclassrange_min)
-            maxid = hop.inputconst(Signed, cls2.subclassrange_max)
-            return hop.gendirectcall(ll_issubclass_const, v_cls1, minid,
-                                     maxid)
+            level = hop.inputconst(Signed, cls2.level)
+            return hop.gendirectcall(ll_issubclass_const, v_cls1, v_cls2, level)
         else:
             v_cls1, v_cls2 = hop.inputargs(class_repr, class_repr)
             return hop.gendirectcall(ll_issubclass, v_cls1, v_cls2)
         self.gcflavor = gcflavor
 
     def _setup_repr(self, llfields=None, hints=None, adtmeths=None):
+        exportinfo = None
         # NOTE: don't store mutable objects like the dicts below on 'self'
         #       before they are fully built, to avoid strange bugs in case
         #       of recursion where other code would uses these
         if self.classdef is None:
             fields['__class__'] = 'typeptr', get_type_repr(self.rtyper)
         else:
+            if self.classdef.is_exported():
+                exportinfo = []
             # instance attributes
             if llfields is None:
                 llfields = []
                     mangled_name = 'inst_' + name
                     fields[name] = mangled_name, r
                     llfields.append((mangled_name, r.lowleveltype))
+                    if exportinfo is not None:
+                        extend_exportinfo(exportinfo, mangled_name, name, r, None)
+
             #
             # hash() support
             if self.rtyper.needs_hash_support(self.classdef):
                 from pypy.rpython import rint
                 fields['_hash_cache_'] = 'hash_cache', rint.signed_repr
                 llfields.append(('hash_cache', Signed))
+            importinfo = self.classdef.get_import_data()
+            if importinfo:
+                llfields = weave_llfields(llfields, importinfo.inst, True)
 
             self.rbase = getinstancerepr(self.rtyper, self.classdef.basedef,
                                          self.gcflavor)
             if '_immutable_' in self.classdef.classdesc.classdict:
                 hints = hints.copy()
                 hints['immutable'] = True
+            if self.classdef.is_exported():
+                hints = hints.copy()
+                hints['_exported'] = True
+            if exportinfo is not None:
+                self.classdef.exportinfo_inst = exportinfo
             object_type = MkStruct(self.classdef.name,
                                    ('super', self.rbase.object_type),
                                    hints=hints,
         instance_repr = self.common_repr()
 
         v_obj, v_cls = hop.inputargs(instance_repr, class_repr)
-        if isinstance(v_cls, Constant):
+        if False and isinstance(v_cls, Constant): # XXX
             cls = v_cls.value
             # XXX re-implement the following optimization
             #if cls.subclassrange_max == cls.subclassrange_min:
             #    # a class with no subclass
             #    return hop.gendirectcall(rclass.ll_isinstance_exact, v_obj, v_cls)
             #else:
-            minid = hop.inputconst(Signed, cls.subclassrange_min)
-            maxid = hop.inputconst(Signed, cls.subclassrange_max)
-            return hop.gendirectcall(ll_isinstance_const, v_obj, minid, maxid)
+            level = hop.inputconst(Signed, cls.level)
+            return hop.gendirectcall(ll_isinstance_const, v_obj, v_cls, level)
         else:
             return hop.gendirectcall(ll_isinstance, v_obj, v_cls)
 
     return cast_pointer(OBJECTPTR, obj).typeptr
 
 def ll_issubclass(subcls, cls):
-    return cls.subclassrange_min <= subcls.subclassrange_min <= cls.subclassrange_max
+    return subcls.level >= cls.level and subcls.classrow[cls.level] == cls
 
-def ll_issubclass_const(subcls, minid, maxid):
-    return minid <= subcls.subclassrange_min <= maxid
-
+def ll_issubclass_const(subcls, cls, level):
+    return subcls.level >= level and subcls.classrow[level] == cls
 
 def ll_isinstance(obj, cls): # obj should be cast to OBJECT or NONGCOBJECT
     if not obj:
     obj_cls = obj.typeptr
     return ll_issubclass(obj_cls, cls)
 
-def ll_isinstance_const(obj, minid, maxid):
+def ll_isinstance_const(obj, cls, level):
     if not obj:
         return False
-    return ll_issubclass_const(obj.typeptr, minid, maxid)
+    return ll_issubclass_const(obj.typeptr, cls, level)
 
-def ll_isinstance_exact(obj, cls):
+def ll_isinstance_exact(obj, cls): #unused
     if not obj:
         return False
     obj_cls = obj.typeptr

File pypy/rpython/lltypesystem/rtagged.py

         cls = v_cls.value
         answer = self.unboxedclassdef.issubclass(classdef)
         c_answer_if_unboxed = hop.inputconst(lltype.Bool, answer)
-        minid = hop.inputconst(lltype.Signed, cls.subclassrange_min)
-        maxid = hop.inputconst(lltype.Signed, cls.subclassrange_max)
-        return hop.gendirectcall(ll_unboxed_isinstance_const, v_obj,
-                                 minid, maxid, c_answer_if_unboxed)
+        level = hop.inputconst(lltype.Signed, cls.level)
+        return hop.gendirectcall(ll_unboxed_isinstance_const, v_obj, v_cls,
+                                 level, c_answer_if_unboxed)
 
 
 def ll_int_to_unboxed(PTRTYPE, value):
     else:
         return instance.typeptr
 
-def ll_unboxed_isinstance_const(obj, minid, maxid, answer_if_unboxed):
+def ll_unboxed_isinstance_const(obj, cls, level, answer_if_unboxed):
     if not obj:
         return False
     if lltype.cast_ptr_to_int(obj) & 1:
         return answer_if_unboxed
     else:
-        return ll_issubclass_const(obj.typeptr, minid, maxid)
+        return ll_issubclass_const(obj.typeptr, cls, level)

File pypy/rpython/lltypesystem/test/test_lltype.py

     assert S == S1
     assert hash(S1) == hash(S)
 
+
+def test_pickle():
+    S = ForwardReference()
+    S.become(Struct('S', ('p', Ptr(S))))
+    assert S == S
+    hash(S)
+    from pickle import dumps, loads
+    S_pickle = dumps(S)
+    S_loaded = loads(S_pickle)
+    assert S_loaded == S
+    assert hash(S_loaded) == hash(S)
+
+
 def test_array_with_non_container_elements():
     As = GcArray(Signed)
     a = malloc(As, 3)

File pypy/rpython/normalizecalls.py

             classdef.minid = TotalOrderSymbolic(witness, lst)
             classdef.maxid = TotalOrderSymbolic(witness + [MAX], lst)
 
+
+def assign_inheritance_levels(annotator):
+    bk = annotator.bookkeeper
+    for classdef in bk.classdefs:
+        if not hasattr(classdef, 'level'):
+            classdef.level = len(tuple(classdef.getmro())) - 1
+
+
 MAX = 1E100
 _cdef_id_counter = 0
 def get_unique_cdef_id(cdef):
     try:
         normalize_call_familes(rtyper.annotator)
         merge_classpbc_getattr_into_classdef(rtyper)
-        assign_inheritance_ids(rtyper.annotator)
+        assign_inheritance_levels(rtyper.annotator)
     finally:
         rtyper.annotator.frozen -= 1
     create_instantiate_functions(rtyper.annotator)

File pypy/rpython/rclass.py

             if not s_value.isNone() and s_value.getKind() == description.MethodDesc:
                 s_value = self.classdef.lookup_filter(s_value)
                 funcdescs = [mdesc.funcdesc for mdesc in s_value.descriptions]
-                return annmodel.SomePBC(funcdescs)
+                return annmodel.SomePBC(funcdescs, force_virtual_access=any(mdesc.force_virtual_access for mdesc in s_value.descriptions))
         return None   # not a method
 
     def get_ll_eq_function(self):

File pypy/rpython/rpbc.py

 
 class AbstractFunctionsPBCRepr(CanBeNull, Repr):
     """Representation selected for a PBC of function(s)."""
-
+    force_virtual = False
     def __init__(self, rtyper, s_pbc):
         self.rtyper = rtyper
         self.s_pbc = s_pbc
         self.callfamily = s_pbc.descriptions.iterkeys().next().getcallfamily()
-        if len(s_pbc.descriptions) == 1 and not s_pbc.can_be_None:
+
+        if len(s_pbc.descriptions) == 1 and not s_pbc.can_be_None and not (
+                s_pbc.force_virtual_access):
             # a single function
             self.lowleveltype = Void
         else:

File pypy/rpython/rtyper.py

         self.r_result = rtyper.getrepr(self.s_result)
         rtyper.call_all_setups()  # compute ForwardReferences now
 
+    def __repr__(self):
+        return '<HOp %r #llops=%i>' % (self.spaceop, len(self.llops))
+
     def copy(self):
         result = HighLevelOp(self.rtyper, self.spaceop,
                              self.exceptionlinks, self.llops)

File pypy/rpython/test/test_rpbc.py

         self.interpret(f, [int])
 
 class TestLLtype(BaseTestRPBC, LLRtypeMixin):
-    pass
+    def test_devoid_exported_func(self):
+        from pypy.translator.translator import graphof
+        from pypy.objspace.flow.model import summary
+        class c:
+            _force_virtual_ = True
+            def foo(self):
+                return 42
+            foo._force_virtual_ = True
+        def f():
+            inst = c()
+            inst.foo()
+            return inst
+        def testfunc(x):
+            return x.foo()
+        def h():
+            return testfunc(c())
+
+        t, r, g = self.gengraph(f)
+        bk = t.annotator.bookkeeper
+        getcdef = bk.getuniqueclassdef
+        classdef = getcdef(c)
+        vt = t.rtyper.class_reprs[classdef].getvtable()
+        assert isinstance(typeOf(t.rtyper.class_reprs[classdef].getvtable(False).cls_foo).TO, FuncType)
+        t, r, g2 = self.gengraph(h)
+        assert not summary(g).get('direct_call', 0)
+        assert not summary(graphof(t, testfunc)).get('direct_call', 0)
+
 
 class TestOOtype(BaseTestRPBC, OORtypeMixin):
     pass