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

antocuni at codespeak.net antocuni at codespeak.net
Tue Jun 6 13:08:42 CEST 2006


Author: antocuni
Date: Tue Jun  6 13:08:40 2006
New Revision: 28362

Modified:
   pypy/dist/pypy/rpython/llinterp.py
   pypy/dist/pypy/rpython/ootypesystem/ootype.py
   pypy/dist/pypy/rpython/ootypesystem/rdict.py
   pypy/dist/pypy/rpython/ootypesystem/test/test_oortype.py
   pypy/dist/pypy/rpython/test/test_objectmodel.py
Log:
Basic r_dict ootypesystem support.

ootype.py has been refactored a bit: the ability of handling generic
types has been moved from BuiltinADTType to the new SpecializableType;
StaticMethod is now a SpecializableType, too.



Modified: pypy/dist/pypy/rpython/llinterp.py
==============================================================================
--- pypy/dist/pypy/rpython/llinterp.py	(original)
+++ pypy/dist/pypy/rpython/llinterp.py	Tue Jun  6 13:08:40 2006
@@ -999,6 +999,33 @@
     def op_runtimenew(self, class_):
         return ootype.runtimenew(class_)
 
+    def _get_func_or_boundmethod(self, func, method_name):
+        if method_name is None:
+            # eq_func is a HalfConcreteWrapper wrapping a StaticMethod
+            self_arg = []
+            func_graph = func.concretize().value.graph
+        else:
+            # eq_func is an instance, we want to call 'method_name' on it
+            self_arg = [func]
+            func_graph = func._TYPE._methods[method_name].graph
+
+        def interp_func(*args):
+            graph_args = self_arg + list(args)
+            return self.llinterpreter.eval_graph(func_graph, args=graph_args)
+        return func_graph.name, interp_func
+
+    def op_oonewcustomdict(self, DICT, eq_func, eq_method_name, hash_func, hash_method_name):
+        eq_name, interp_eq = self._get_func_or_boundmethod(eq_func, eq_method_name)
+        EQ_FUNC = ootype.StaticMethod([DICT._KEYTYPE, DICT._KEYTYPE], ootype.Bool)
+        sm_eq = ootype.static_meth(EQ_FUNC, eq_name, _callable=interp_eq)        
+
+        hash_name, interp_hash = self._get_func_or_boundmethod(hash_func, hash_method_name)
+        HASH_FUNC = ootype.StaticMethod([DICT._KEYTYPE], ootype.Signed)
+        sm_hash = ootype.static_meth(HASH_FUNC, hash_name, _callable=interp_hash)
+
+        # XXX: is it fine to have StaticMethod type for bound methods, too?
+        return ootype.newcustomdict(DICT, sm_eq, sm_hash)
+
     def op_oosetfield(self, inst, name, value):
         assert checkinst(inst)
         assert isinstance(name, str)

Modified: pypy/dist/pypy/rpython/ootypesystem/ootype.py
==============================================================================
--- pypy/dist/pypy/rpython/ootypesystem/ootype.py	(original)
+++ pypy/dist/pypy/rpython/ootypesystem/ootype.py	Tue Jun  6 13:08:40 2006
@@ -2,6 +2,7 @@
 from pypy.rpython.lltypesystem.lltype import Bool, Void, UniChar, typeOf, \
         Primitive, isCompatibleType, enforce, saferecursive
 from pypy.rpython.lltypesystem.lltype import frozendict, isCompatibleType
+from pypy.rpython import objectmodel
 from pypy.tool.uid import uid
 
 STATICNESS = True
@@ -159,8 +160,19 @@
         all.update(self._fields)
         return all
 
+class SpecializableType(OOType):
+    def _specialize_type(self, TYPE, generic_types):
+        if isinstance(TYPE, SpecializableType):
+            res = TYPE._specialize(generic_types)
+        else:
+            res = generic_types.get(TYPE, TYPE)
+        assert res is not None
+        return res
+
+    def _specialize(self, generic_types):
+        raise NotImplementedError
 
-class StaticMethod(OOType):
+class StaticMethod(SpecializableType):
     __slots__ = ['_null']
 
     def __init__(self, args, result):
@@ -174,14 +186,24 @@
 
     def _defl(self):
         return null(self)
-    
+
+    def __repr__(self):
+        return "<StaticMethod(%s, %s)>" % (list(self.ARGS), self.RESULT)
+
+    def _specialize(self, generic_types):
+        ARGS = tuple([self._specialize_type(ARG, generic_types)
+                      for ARG in self.ARGS])
+        RESULT = self._specialize_type(self.RESULT, generic_types)
+        return self.__class__(ARGS, RESULT)
+
+
 class Meth(StaticMethod):
 
     def __init__(self, args, result):
         StaticMethod.__init__(self, args, result)
 
 
-class BuiltinType(OOType):
+class BuiltinType(SpecializableType):
 
     def _example(self):
         return new(self)
@@ -241,22 +263,11 @@
     def _setup_methods(self, generic_types):
         methods = {}
         for name, meth in self._GENERIC_METHODS.iteritems():
-            #args = [generic_types.get(arg, arg) for arg in meth.ARGS]
-            #result = generic_types.get(meth.RESULT, meth.RESULT)
             args = [self._specialize_type(arg, generic_types) for arg in meth.ARGS]
             result = self._specialize_type(meth.RESULT, generic_types)
             methods[name] = Meth(args, result)
         self._METHODS = frozendict(methods)
 
-    def _specialize_type(self, type_, generic_types):
-        if isinstance(type_, BuiltinADTType):
-            return type_._specialize(generic_types)
-        else:
-            return generic_types.get(type_, type_)
-
-    def _specialize(self, generic_types):
-        raise NotImplementedError
-
     def _lookup(self, meth_name):
         METH = self._METHODS.get(meth_name)
         meth = None
@@ -434,11 +445,11 @@
         return self._KEYTYPE is not None and self._VALUETYPE is not None
 
     def _init_methods(self):
-        generic_types = {
+        self._generic_types = frozendict({
             self.SELFTYPE_T: self,
             self.KEYTYPE_T: self._KEYTYPE,
             self.VALUETYPE_T: self._VALUETYPE
-            }
+            })
 
         self._GENERIC_METHODS = frozendict({
             "ll_length": Meth([], Signed),
@@ -450,7 +461,7 @@
             "ll_get_items_iterator": Meth([], DictItemsIterator(self.KEYTYPE_T, self.VALUETYPE_T)),
         })
 
-        self._setup_methods(generic_types)
+        self._setup_methods(self._generic_types)
 
     # NB: We are expecting Dicts of the same KEYTYPE, VALUETYPE to
     # compare/hash equal. We don't redefine __eq__/__hash__ since the
@@ -487,6 +498,27 @@
         self._KEYTYPE = KEYTYPE
         self._VALUETYPE = VALUETYPE
         self._init_methods()
+                                           
+
+class CustomDict(Dict):
+    def __init__(self, KEYTYPE=None, VALUETYPE=None):
+        Dict.__init__(self, KEYTYPE, VALUETYPE)
+        self._null = _null_custom_dict(self)
+
+        if self._is_initialized():
+            self._init_methods()
+
+    def _init_methods(self):
+        Dict._init_methods(self)
+        EQ_FUNC = StaticMethod([self.KEYTYPE_T, self.KEYTYPE_T], Bool)
+        HASH_FUNC = StaticMethod([self.KEYTYPE_T], Signed)
+        self._GENERIC_METHODS['ll_set_functions'] = Meth([EQ_FUNC, HASH_FUNC], Void)
+        self._GENERIC_METHODS['ll_copy'] = Meth([], self.SELFTYPE_T)
+        self._setup_methods(self._generic_types)
+
+    def _get_interp_class(self):
+        return _custom_dict
+
 
 class DictItemsIterator(BuiltinADTType):
     SELFTYPE_T = object()
@@ -1033,6 +1065,26 @@
     def __init__(self, DICT):
         self.__dict__["_TYPE"] = DICT
 
+class _custom_dict(_dict):
+    def __init__(self, DICT):
+        self._TYPE = DICT
+        self._dict = 'DICT_NOT_CREATED_YET' # it's created inside ll_set_functions
+
+    def ll_set_functions(self, sm_eq, sm_hash):
+        "NOT_RPYTHON"
+        key_eq = sm_eq._callable
+        key_hash = sm_hash._callable
+        self._dict = objectmodel.r_dict(key_eq, key_hash)
+
+    def ll_copy(self):
+        "NOT_RPYTHON"
+        res = self.__class__(self._TYPE)
+        res._dict = self._dict.copy()
+        return res
+
+class _null_custom_dict(_null_mixin(_custom_dict), _custom_dict):
+    def __init__(self, DICT):
+        self.__dict__["_TYPE"] = DICT
 
 class _dict_items_iterator(_builtin_type):
     def __init__(self, ITER):
@@ -1108,6 +1160,11 @@
     elif isinstance(TYPE, BuiltinType):
         return TYPE._get_interp_class()(TYPE)
 
+def oonewcustomdict(DICT, ll_eq, ll_hash):
+    d = new(DICT)
+    d.ll_set_functions(ll_eq, ll_hash)
+    return d
+
 def runtimenew(class_):
     assert isinstance(class_, _class)
     assert class_ is not nullruntimeclass
@@ -1207,6 +1264,9 @@
 def setDictTypes(DICT, KEYTYPE, VALUETYPE):
     return DICT._set_types(KEYTYPE, VALUETYPE)
 
+def setDictFunctions(DICT, ll_eq, ll_hash):
+    return DICT._set_functions(ll_eq, ll_hash)
+
 def hasDictTypes(DICT):
     return DICT._is_initialized()
 

Modified: pypy/dist/pypy/rpython/ootypesystem/rdict.py
==============================================================================
--- pypy/dist/pypy/rpython/ootypesystem/rdict.py	(original)
+++ pypy/dist/pypy/rpython/ootypesystem/rdict.py	Tue Jun  6 13:08:40 2006
@@ -32,10 +32,15 @@
         else:
             self.external_value_repr, self.value_repr = self.pickrepr(value_repr)
 
+        if self.custom_eq_hash:
+            Dict = ootype.CustomDict
+        else:
+            Dict = ootype.Dict
+
         if already_computed:
-            self.DICT = ootype.Dict(key_repr.lowleveltype, value_repr.lowleveltype)
+            self.DICT = Dict(key_repr.lowleveltype, value_repr.lowleveltype)
         else:
-            self.DICT = ootype.Dict()
+            self.DICT = Dict()
         self.lowleveltype = self.DICT
 
         self.dictkey = dictkey
@@ -58,6 +63,9 @@
             ootype.setDictTypes(self.DICT, self.key_repr.lowleveltype,
                     self.value_repr.lowleveltype)
 
+        if self.custom_eq_hash:
+            self.r_rdict_eqfn, self.r_rdict_hashfn = self._custom_eq_hash_repr()
+
     def send_message(self, hop, message, can_raise=False, v_args=None):
         if v_args is None:
             v_args = hop.inputargs(self, *hop.args_r[1:])
@@ -97,7 +105,11 @@
         v_dict, = hop.inputargs(self)
         cDICT = hop.inputconst(ootype.Void, self.lowleveltype)
         hop.exception_cannot_occur()
-        return hop.gendirectcall(ll_dict_copy, cDICT, v_dict)
+        if self.custom_eq_hash:
+            c_copy = hop.inputconst(ootype.Void, 'll_copy')
+            return hop.genop('oosend', [c_copy, v_dict], resulttype=hop.r_result.lowleveltype)
+        else:
+            return hop.gendirectcall(ll_dict_copy, cDICT, v_dict)
 
     def rtype_method_update(self, hop):
         v_dict1, v_dict2 = hop.inputargs(self, self)
@@ -193,7 +205,39 @@
 
 
 def rtype_r_dict(hop):
-    pass # TODO
+    r_dict = hop.r_result
+    if not r_dict.custom_eq_hash:
+        raise TyperError("r_dict() call does not return an r_dict instance")
+    v_eqfn, v_hashfn = hop.inputargs(r_dict.r_rdict_eqfn,
+                                     r_dict.r_rdict_hashfn)
+    cDICT = hop.inputconst(ootype.Void, r_dict.DICT)
+    hop.exception_cannot_occur()
+
+    if r_dict.r_rdict_eqfn.lowleveltype == ootype.Void:
+        c_eq_method_name = hop.inputconst(ootype.Void, None)
+    else:
+        # simulate a call to the method to get the exact variant name we need
+        s_pbc_eq = hop.args_s[0] # the instance which we take key_eq from
+        s_key = r_dict.dictkey.s_value
+        methodname = r_dict.r_rdict_eqfn._get_method_name("simple_call", s_pbc_eq, [s_key, s_key])
+        c_eq_method_name = hop.inputconst(ootype.Void, methodname)
+
+    if r_dict.r_rdict_hashfn.lowleveltype == ootype.Void:
+        c_hash_method_name = hop.inputconst(ootype.Void, None)
+    else:
+        # same as above
+        s_pbc_hash = hop.args_s[1] # the instance which we take key_hash from
+        s_key = r_dict.dictkey.s_value
+        methodname = r_dict.r_rdict_hashfn._get_method_name("simple_call", s_pbc_hash, [s_key])
+        c_hash_method_name = hop.inputconst(ootype.Void, methodname)
+
+    # the signature of oonewcustomdict is a bit complicated: v_eqfn
+    # can be either a function (with lowleveltype StaticMethod) or a
+    # bound method (with lowleveltype Instance). In the first case
+    # c_eq_method_name is None, in the latter it's a constant Void
+    # string containing the name of the method we want to call.
+    return hop.genop("oonewcustomdict", [cDICT, v_eqfn, c_eq_method_name, v_hashfn, c_hash_method_name],
+                     resulttype=hop.r_result.lowleveltype)
 
 def ll_newdict(DICT):
     return ootype.new(DICT)

Modified: pypy/dist/pypy/rpython/ootypesystem/test/test_oortype.py
==============================================================================
--- pypy/dist/pypy/rpython/ootypesystem/test/test_oortype.py	(original)
+++ pypy/dist/pypy/rpython/ootypesystem/test/test_oortype.py	Tue Jun  6 13:08:40 2006
@@ -7,6 +7,7 @@
 from pypy.objspace.flow import FlowObjSpace
 from pypy.translator.translator import TranslationContext, graphof
 from pypy.rpython.test.test_llinterp import interpret
+from pypy.rpython.objectmodel import r_dict
 from pypy.rpython.ootypesystem import ooregistry # side effects
 
 def gengraph(f, args=[], viewBefore=False, viewAfter=False):
@@ -245,3 +246,28 @@
 
     assert interpret(oof, [True], type_system='ootype') == 2
     assert interpret(oof, [False], type_system='ootype') == 1
+
+def test_r_dict():
+    def strange_key_eq(key1, key2):
+        return key1[0] == key2[0]   # only the 1st character is relevant
+    def strange_key_hash(key):
+        return ord(key[0])
+    def oof():
+        d = r_dict(strange_key_eq, strange_key_hash)
+        d['x'] = 42
+        return d['x']
+    assert interpret(oof, [], type_system='ootype') == 42
+
+def test_r_dict_bm():
+    class Strange:
+        def key_eq(strange, key1, key2):
+            return key1[0] == key2[0]   # only the 1st character is relevant
+        def key_hash(strange, key):
+            return ord(key[0])
+
+    def oof():
+        strange = Strange()
+        d = r_dict(strange.key_eq, strange.key_hash)
+        d['x'] = 42
+        return d['x']
+    assert interpret(oof, [], type_system='ootype') == 42

Modified: pypy/dist/pypy/rpython/test/test_objectmodel.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_objectmodel.py	(original)
+++ pypy/dist/pypy/rpython/test/test_objectmodel.py	Tue Jun  6 13:08:40 2006
@@ -2,6 +2,7 @@
 from pypy.rpython.objectmodel import *
 from pypy.translator.translator import TranslationContext, graphof
 from pypy.rpython.test.test_llinterp import interpret
+from pypy.rpython.test.tool import BaseRtypingTest, LLRtypeMixin, OORtypeMixin
 
 def test_we_are_translated():
     assert we_are_translated() == False
@@ -145,13 +146,15 @@
     assert a.binding(graph.getargs()[0]).knowntype == Strange_def
     assert a.binding(graph.getargs()[1]).knowntype == str
 
-def test_rtype_r_dict():
-    res = interpret(test_r_dict, [])
-    assert res is True
-
-def test_rtype_r_dict_bm():
-    res = interpret(test_r_dict_bm, [])
-    assert res is True
+class BaseTestObjectModel(BaseRtypingTest):
+    
+    def test_rtype_r_dict(self):
+        res = self.interpret(test_r_dict, [])
+        assert res is True
+
+    def test_rtype_r_dict_bm(self):
+        res = self.interpret(test_r_dict_bm, [])
+        assert res is True
 
 def test_rtype_constant_r_dicts():
     d1 = r_dict(strange_key_eq, strange_key_hash)
@@ -288,3 +291,10 @@
     s2 = Symbolic()
     py.test.raises(TypeError, "s1 < s2")
     py.test.raises(TypeError, "hash(s1)")
+
+
+class TestLLtype(BaseTestObjectModel, LLRtypeMixin):
+    pass
+
+class TestOOtype(BaseTestObjectModel, OORtypeMixin):
+    pass



More information about the Pypy-commit mailing list