[pypy-svn] r24836 - in pypy/branch/pypy-rdict-refactoring: . annotation rpython rpython/lltypesystem rpython/test
arigo at codespeak.net
arigo at codespeak.net
Thu Mar 23 00:35:10 CET 2006
Author: arigo
Date: Thu Mar 23 00:35:06 2006
New Revision: 24836
Added:
pypy/branch/pypy-rdict-refactoring/
- copied from r24803, pypy/dist/pypy/
pypy/branch/pypy-rdict-refactoring/rpython/annlowlevel.py
- copied unchanged from r24831, pypy/dist/pypy/rpython/annlowlevel.py
Modified:
pypy/branch/pypy-rdict-refactoring/annotation/model.py
pypy/branch/pypy-rdict-refactoring/rpython/llinterp.py
pypy/branch/pypy-rdict-refactoring/rpython/lltypesystem/lltype.py
pypy/branch/pypy-rdict-refactoring/rpython/lltypesystem/rclass.py
pypy/branch/pypy-rdict-refactoring/rpython/raddress.py
pypy/branch/pypy-rdict-refactoring/rpython/rclass.py
pypy/branch/pypy-rdict-refactoring/rpython/rdict.py
pypy/branch/pypy-rdict-refactoring/rpython/rfloat.py
pypy/branch/pypy-rdict-refactoring/rpython/rint.py
pypy/branch/pypy-rdict-refactoring/rpython/rmodel.py
pypy/branch/pypy-rdict-refactoring/rpython/rstr.py
pypy/branch/pypy-rdict-refactoring/rpython/rtuple.py
pypy/branch/pypy-rdict-refactoring/rpython/rtyper.py
pypy/branch/pypy-rdict-refactoring/rpython/test/test_rdict.py
Log:
Branch check-in: this rdict refactoring is mostly done, but
I got a test_stress failure. (I promize a more detailed log
message in the trunk merge.)
Modified: pypy/branch/pypy-rdict-refactoring/annotation/model.py
==============================================================================
--- pypy/dist/pypy/annotation/model.py (original)
+++ pypy/branch/pypy-rdict-refactoring/annotation/model.py Thu Mar 23 00:35:06 2006
@@ -28,7 +28,7 @@
#
-from types import BuiltinFunctionType, MethodType
+from types import BuiltinFunctionType, MethodType, FunctionType
import pypy
from pypy.annotation.pairtype import pair, extendabletype
from pypy.tool.tls import tlsobject
@@ -582,6 +582,11 @@
ll_ptrtype = lltype.typeOf(v.im_self)
assert isinstance(ll_ptrtype, lltype.Ptr)
return SomeLLADTMeth(ll_ptrtype, v.im_func)
+ if isinstance(v, FunctionType):
+ # this case should only be for staticmethod instances used in
+ # adtmeths: the getattr() result is then a plain FunctionType object.
+ from pypy.annotation.bookkeeper import getbookkeeper
+ return getbookkeeper().immutablevalue(v)
return lltype_to_annotation(lltype.typeOf(v))
# ____________________________________________________________
Modified: pypy/branch/pypy-rdict-refactoring/rpython/llinterp.py
==============================================================================
--- pypy/dist/pypy/rpython/llinterp.py (original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/llinterp.py Thu Mar 23 00:35:06 2006
@@ -449,20 +449,17 @@
def op_getfield(self, obj, field):
assert checkptr(obj)
- result = getattr(obj, field)
# check the difference between op_getfield and op_getsubstruct:
- # the former returns the real field, the latter a pointer to it
- assert lltype.typeOf(result) == getattr(lltype.typeOf(obj).TO, field)
- return result
+ assert not isinstance(getattr(lltype.typeOf(obj).TO, field),
+ lltype.ContainerType)
+ return getattr(obj, field)
def op_getsubstruct(self, obj, field):
assert checkptr(obj)
- result = getattr(obj, field)
# check the difference between op_getfield and op_getsubstruct:
- # the former returns the real field, the latter a pointer to it
- assert (lltype.typeOf(result) ==
- lltype.Ptr(getattr(lltype.typeOf(obj).TO, field)))
- return result
+ assert isinstance(getattr(lltype.typeOf(obj).TO, field),
+ lltype.ContainerType)
+ return getattr(obj, field)
def op_getarraysubstruct(self, array, index):
assert checkptr(array)
Modified: pypy/branch/pypy-rdict-refactoring/rpython/lltypesystem/lltype.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/lltype.py (original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/lltypesystem/lltype.py Thu Mar 23 00:35:06 2006
@@ -686,9 +686,17 @@
o = getattr(self._obj, field_name)
return _expose(o, self._solid)
if isinstance(self._T, ContainerType):
- adtmeth = self._T._adtmeths.get(field_name)
- if adtmeth is not None:
- return adtmeth.__get__(self)
+ try:
+ adtmeth = self._T._adtmeths[field_name]
+ except KeyError:
+ pass
+ else:
+ try:
+ getter = adtmeth.__get__
+ except AttributeError:
+ return adtmeth
+ else:
+ return getter(self)
raise AttributeError("%r instance has no field %r" % (self._T,
field_name))
Modified: pypy/branch/pypy-rdict-refactoring/rpython/lltypesystem/rclass.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/rclass.py (original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/lltypesystem/rclass.py Thu Mar 23 00:35:06 2006
@@ -620,6 +620,8 @@
return obj.typeptr.rtti
def ll_inst_hash(ins):
+ if not ins:
+ return 0 # for None
cached = ins.hash_cache
if cached == 0:
cached = ins.hash_cache = id(ins)
Modified: pypy/branch/pypy-rdict-refactoring/rpython/raddress.py
==============================================================================
--- pypy/dist/pypy/rpython/raddress.py (original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/raddress.py Thu Mar 23 00:35:06 2006
@@ -44,6 +44,8 @@
def get_ll_hash_function(self):
return ll_addrhash
+ get_ll_fasthash_function = get_ll_hash_function
+
def ll_addrhash(addr1):
return cast_adr_to_int(addr1)
Modified: pypy/branch/pypy-rdict-refactoring/rpython/rclass.py
==============================================================================
--- pypy/dist/pypy/rpython/rclass.py (original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/rclass.py Thu Mar 23 00:35:06 2006
@@ -182,6 +182,9 @@
def get_ll_eq_function(self):
return None # defaults to compare by identity ('==' on pointers)
+ def can_ll_be_null(self, s_value):
+ return s_value.can_be_none()
+
# ____________________________________________________________
def rtype_new_instance(rtyper, classdef, llops):
Modified: pypy/branch/pypy-rdict-refactoring/rpython/rdict.py
==============================================================================
--- pypy/dist/pypy/rpython/rdict.py (original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/rdict.py Thu Mar 23 00:35:06 2006
@@ -13,17 +13,15 @@
# generic implementation of RPython dictionary, with parametric DICTKEY and
# DICTVALUE types.
#
-# XXX this should be re-optimized for specific types of keys; e.g.
-# for string keys we don't need the two boolean flags but can use
-# a NULL and a special 'dummy' keys. Similarily, for immutable dicts,
-# the array should be inlined and num_pristine_entries is not needed.
+# XXX for immutable dicts, the array should be inlined and
+# num_pristine_entries and everused are not needed.
#
# struct dictentry {
# DICTKEY key;
-# bool valid; # to mark if the entry is filled
-# bool everused; # to mark if the entry is or has ever been filled
+# bool f_valid; # (optional) the entry is filled
+# bool f_everused; # (optional) the entry is or has ever been filled
# DICTVALUE value;
-# int hash;
+# int f_hash; # (optional) key hash, if hard to recompute
# }
#
# struct dicttable {
@@ -61,39 +59,10 @@
def rtyper_makekey(self):
return (self.__class__, self.dictdef.dictkey, self.dictdef.dictvalue)
-class DictTrait:
- # avoid explosion of one helper function per repr
- # vs. layout and functions
-
- def __init__(self, dictrepr):
- self.DICT = dictrepr.DICT
- self.DICTENTRYARRAY = dictrepr.DICTENTRYARRAY
- self.custom_eq_hash = False
- self.ll_keyhash = dictrepr.ll_keyhash
- self.ll_keyeq = dictrepr.ll_keyeq
-
- def _freeze_(self):
- return True
-
- def __repr__(self):
- return "DictT %s" % self.DICT._short_name()
-
-def dict_trait(rtyper, dictrepr):
- if dictrepr.custom_eq_hash: # in this case use there is not much point in not just using the repr
- return dictrepr
-
- key = (dictrepr.DICT, dictrepr.ll_keyhash, dictrepr.ll_keyeq)
- try:
- return rtyper._dict_traits[key]
- except KeyError:
- trait = DictTrait(dictrepr)
- rtyper._dict_traits[key] = trait
- return trait
-
class DictRepr(rmodel.Repr):
- def __init__(self, rtyper, key_repr, value_repr, dictkey=None, dictvalue=None,
+ def __init__(self, rtyper, key_repr, value_repr, dictkey, dictvalue,
custom_eq_hash=None):
self.rtyper = rtyper
self.DICT = lltype.GcForwardReference()
@@ -113,7 +82,6 @@
self.dictvalue = dictvalue
self.dict_cache = {}
self._custom_eq_hash_repr = custom_eq_hash
- self._trait = None # cache for get_trait
# setup() needs to be called to finish this initialization
def pickrepr(self, item_repr):
@@ -142,12 +110,77 @@
if isinstance(self.DICT, lltype.GcForwardReference):
self.DICTKEY = self.key_repr.lowleveltype
self.DICTVALUE = self.value_repr.lowleveltype
- self.DICTENTRY = lltype.Struct("dictentry",
- ("key", self.DICTKEY),
- ("hash", lltype.Signed),
- ("valid", lltype.Bool),
- ("everused", lltype.Bool),
- ("value", self.DICTVALUE))
+
+ # compute the shape of the DICTENTRY structure
+ entryfields = []
+ entrymeths = {
+ 'must_clear_key': (isinstance(self.DICTKEY, lltype.Ptr)
+ and self.DICTKEY._needsgc()),
+ 'must_clear_value': (isinstance(self.DICTVALUE, lltype.Ptr)
+ and self.DICTVALUE._needsgc()),
+ }
+
+ # * the key
+ entryfields.append(("key", self.DICTKEY))
+
+ # * if NULL is not a valid ll value for the key or the value
+ # field of the entry, it can be used as a marker for
+ # never-used entries. Otherwise, we need an explicit flag.
+ s_key = self.dictkey.s_value
+ s_value = self.dictvalue.s_value
+ nullkeymarker = not self.key_repr.can_ll_be_null(s_key)
+ nullvaluemarker = not self.value_repr.can_ll_be_null(s_value)
+
+ if nullkeymarker:
+ entrymeths['everused'] = ll_everused_from_key
+ elif nullvaluemarker:
+ entrymeths['everused'] = ll_everused_from_value
+ else:
+ entryfields.append(("f_everused", lltype.Bool))
+ entrymeths['everused'] = ll_everused_from_flag
+
+ # * if the key or the value can also contain a "dummy" non-null
+ # marker, we use it for deleted entries.
+ rtyper = self.rtyper
+ dummy_obj = self.key_repr.get_ll_dummyval_obj(rtyper, s_key)
+ if dummy_obj:
+ entrymeths['dummy_obj'] = dummy_obj
+ entrymeths['valid'] = ll_valid_from_key
+ entrymeths['mark_deleted'] = ll_mark_deleted_in_key
+ # the key is overwritten by 'dummy' when the entry is deleted
+ entrymeths['must_clear_key'] = False
+ else:
+ dummy_obj = self.value_repr.get_ll_dummyval_obj(rtyper,
+ s_value)
+ if dummy_obj:
+ entrymeths['dummy_obj'] = dummy_obj
+ entrymeths['valid'] = ll_valid_from_value
+ entrymeths['mark_deleted'] = ll_mark_deleted_in_value
+ # value is overwritten by 'dummy' when entry is deleted
+ entrymeths['must_clear_value'] = False
+ else:
+ entryfields.append(("f_valid", lltype.Bool))
+ entrymeths['valid'] = ll_valid_from_flag
+ entrymeths['mark_deleted'] = ll_mark_deleted_in_flag
+
+ # * the value
+ entryfields.append(("value", self.DICTVALUE))
+
+ # * the hash, if needed
+ if self.custom_eq_hash:
+ fasthashfn = None
+ else:
+ fasthashfn = self.key_repr.get_ll_fasthash_function()
+ if fasthashfn is None:
+ entryfields.append(("f_hash", lltype.Signed))
+ entrymeths['hash'] = ll_hash_from_cache
+ else:
+ entrymeths['hash'] = ll_hash_recomputed
+ entrymeths['fasthashfn'] = fasthashfn
+
+ # Build the lltype data structures
+ self.DICTENTRY = lltype.Struct("dictentry", adtmeths=entrymeths,
+ *entryfields)
self.DICTENTRYARRAY = lltype.GcArray(self.DICTENTRY)
fields = [ ("num_items", lltype.Signed),
("num_pristine_entries", lltype.Signed),
@@ -156,11 +189,25 @@
self.r_rdict_eqfn, self.r_rdict_hashfn = self._custom_eq_hash_repr()
fields.extend([ ("fnkeyeq", self.r_rdict_eqfn.lowleveltype),
("fnkeyhash", self.r_rdict_hashfn.lowleveltype) ])
- self.DICT.become(lltype.GcStruct("dicttable", *fields))
- if 'll_keyhash' not in self.__dict__ and not self.custom_eq_hash:
- # figure out which functions must be used to hash and compare keys
- self.ll_keyeq = self.key_repr.get_ll_eq_function() # can be None
- self.ll_keyhash = self.key_repr.get_ll_hash_function()
+ adtmeths = {
+ 'keyhash': ll_keyhash_custom,
+ 'keyeq': ll_keyeq_custom,
+ 'r_rdict_eqfn': self.r_rdict_eqfn,
+ 'r_rdict_hashfn': self.r_rdict_hashfn,
+ }
+ else:
+ # figure out which functions must be used to hash and compare
+ ll_keyhash = self.key_repr.get_ll_hash_function()
+ ll_keyeq = self.key_repr.get_ll_eq_function() # can be None
+ ll_keyhash = staticmethod(ll_keyhash)
+ if ll_keyeq is not None:
+ ll_keyeq = staticmethod(ll_keyeq)
+ adtmeths = {
+ 'keyhash': ll_keyhash,
+ 'keyeq': ll_keyeq,
+ }
+ self.DICT.become(lltype.GcStruct("dicttable", adtmeths=adtmeths,
+ *fields))
def recast_value(self, llops, v):
return llops.convertvar(v, self.value_repr, self.external_value_repr)
@@ -168,11 +215,6 @@
def recast_key(self, llops, v):
return llops.convertvar(v, self.key_repr, self.external_key_repr)
- def get_trait(self):
- if self._trait is None:
- self._trait = dict_trait(self.rtyper, self)
- return self._trait
-
def convert_const(self, dictobj):
# get object from bound dict methods
#dictobj = getattr(dictobj, '__self__', dictobj)
@@ -185,7 +227,7 @@
return self.dict_cache[key]
except KeyError:
self.setup()
- l_dict = ll_newdict(self)
+ l_dict = ll_newdict_size(self.DICT, len(dictobj))
self.dict_cache[key] = l_dict
r_key = self.key_repr
r_value = self.value_repr
@@ -196,38 +238,20 @@
if self.r_rdict_hashfn.lowleveltype != lltype.Void:
l_fn = self.r_rdict_hashfn.convert_const(dictobj.key_hash)
l_dict.fnkeyhash = l_fn
- # a dummy object with ll_keyeq and ll_keyhash methods to
- # pass to ll_dict_setitem()
- class Dummy:
- custom_eq_hash = False
- def ll_keyeq(self, key1, key2):
- # theory: all low-level values we consider as keys
- # can be compared by equality (i.e. identity for
- # pointers) because the r_dict itself should have
- # ensured that it does not store duplicate equal keys.
- return key1 == key2
- def ll_keyhash(self, key):
- # theoretically slow, but well (see theory above)
- for llkey, hash in self.cache:
- if key == llkey:
- return hash
- raise TyperError("hash missing in convert_const(%r)" %
- (dictobj,))
- dummy = Dummy()
- dummy.cache = []
for dictkeycontainer, dictvalue in dictobj._dict.items():
llkey = r_key.convert_const(dictkeycontainer.key)
llvalue = r_value.convert_const(dictvalue)
- dummy.cache.insert(0, (llkey, dictkeycontainer.hash))
- ll_dict_setitem(l_dict, llkey, llvalue, dummy)
+ ll_dict_insertclean(l_dict, llkey, llvalue,
+ dictkeycontainer.hash)
return l_dict
else:
for dictkey, dictvalue in dictobj.items():
llkey = r_key.convert_const(dictkey)
llvalue = r_value.convert_const(dictvalue)
- ll_dict_setitem(l_dict, llkey, llvalue, self)
+ ll_dict_insertclean(l_dict, llkey, llvalue,
+ l_dict.keyhash(llkey))
return l_dict
def rtype_len(self, hop):
@@ -244,22 +268,19 @@
def rtype_method_get(self, hop):
v_dict, v_key, v_default = hop.inputargs(self, self.key_repr,
self.value_repr)
- ctrait = hop.inputconst(lltype.Void, self.get_trait())
hop.exception_cannot_occur()
- v_res = hop.gendirectcall(ll_get, v_dict, v_key, v_default, ctrait)
+ v_res = hop.gendirectcall(ll_get, v_dict, v_key, v_default)
return self.recast_value(hop.llops, v_res)
def rtype_method_copy(self, hop):
v_dict, = hop.inputargs(self)
- ctrait = hop.inputconst(lltype.Void, self.get_trait())
hop.exception_cannot_occur()
- return hop.gendirectcall(ll_copy, v_dict, ctrait)
+ return hop.gendirectcall(ll_copy, v_dict)
def rtype_method_update(self, hop):
v_dic1, v_dic2 = hop.inputargs(self, self)
- ctrait = hop.inputconst(lltype.Void, self.get_trait())
hop.exception_cannot_occur()
- return hop.gendirectcall(ll_update, v_dic1, v_dic2, ctrait)
+ return hop.gendirectcall(ll_update, v_dic1, v_dic2)
def _rtype_method_kvi(self, hop, spec):
v_dic, = hop.inputargs(self)
@@ -299,28 +320,24 @@
def rtype_getitem((r_dict, r_key), hop):
v_dict, v_key = hop.inputargs(r_dict, r_dict.key_repr)
- ctrait = hop.inputconst(lltype.Void, r_dict.get_trait())
hop.has_implicit_exception(KeyError) # record that we know about it
hop.exception_is_here()
- v_res = hop.gendirectcall(ll_dict_getitem, v_dict, v_key, ctrait)
+ v_res = hop.gendirectcall(ll_dict_getitem, v_dict, v_key)
return r_dict.recast_value(hop.llops, v_res)
def rtype_delitem((r_dict, r_key), hop):
v_dict, v_key = hop.inputargs(r_dict, r_dict.key_repr)
- ctrait = hop.inputconst(lltype.Void, r_dict.get_trait())
hop.has_implicit_exception(KeyError) # record that we know about it
hop.exception_is_here()
- return hop.gendirectcall(ll_dict_delitem, v_dict, v_key, ctrait)
+ return hop.gendirectcall(ll_dict_delitem, v_dict, v_key)
def rtype_setitem((r_dict, r_key), hop):
v_dict, v_key, v_value = hop.inputargs(r_dict, r_dict.key_repr, r_dict.value_repr)
- ctrait = hop.inputconst(lltype.Void, r_dict.get_trait())
- hop.gendirectcall(ll_dict_setitem, v_dict, v_key, v_value, ctrait)
+ hop.gendirectcall(ll_dict_setitem, v_dict, v_key, v_value)
def rtype_contains((r_dict, r_key), hop):
v_dict, v_key = hop.inputargs(r_dict, r_dict.key_repr)
- ctrait = hop.inputconst(lltype.Void, r_dict.get_trait())
- return hop.gendirectcall(ll_contains, v_dict, v_key, ctrait)
+ return hop.gendirectcall(ll_contains, v_dict, v_key)
class __extend__(pairtype(DictRepr, DictRepr)):
def convert_from_to((r_dict1, r_dict2), v, llops):
@@ -342,6 +359,56 @@
# be direct_call'ed from rtyped flow graphs, which means that they will
# get flowed and annotated, mostly with SomePtr.
+def ll_everused_from_flag(entry):
+ return entry.f_everused
+
+def ll_everused_from_key(entry):
+ return bool(entry.key)
+
+def ll_everused_from_value(entry):
+ return bool(entry.value)
+
+def ll_valid_from_flag(entry):
+ return entry.f_valid
+
+def ll_mark_deleted_in_flag(entry):
+ entry.f_valid = False
+
+def ll_valid_from_key(entry):
+ ENTRY = lltype.typeOf(entry).TO
+ dummy = ENTRY.dummy_obj.ll_dummy_value
+ return entry.everused() and entry.key != dummy
+
+def ll_mark_deleted_in_key(entry):
+ ENTRY = lltype.typeOf(entry).TO
+ dummy = ENTRY.dummy_obj.ll_dummy_value
+ entry.key = dummy
+
+def ll_valid_from_value(entry):
+ ENTRY = lltype.typeOf(entry).TO
+ dummy = ENTRY.dummy_obj.ll_dummy_value
+ return entry.everused() and entry.value != dummy
+
+def ll_mark_deleted_in_value(entry):
+ ENTRY = lltype.typeOf(entry).TO
+ dummy = ENTRY.dummy_obj.ll_dummy_value
+ entry.value = dummy
+
+def ll_hash_from_cache(entry):
+ return entry.f_hash
+
+def ll_hash_recomputed(entry):
+ ENTRY = lltype.typeOf(entry).TO
+ return ENTRY.fasthashfn(entry.key)
+
+def ll_keyhash_custom(d, key):
+ DICT = lltype.typeOf(d).TO
+ return hlinvoke(DICT.r_rdict_hashfn, d.fnkeyhash, key)
+
+def ll_keyeq_custom(d, key1, key2):
+ DICT = lltype.typeOf(d).TO
+ return hlinvoke(DICT.r_rdict_eqfn, d.fnkeyeq, key1, key2)
+
def dum_keys(): pass
def dum_values(): pass
def dum_items():pass
@@ -356,51 +423,69 @@
# check if a dict is True, allowing for None
return bool(d) and d.num_items != 0
-def ll_dict_getitem(d, key, dictrait):
- entry = ll_dict_lookup(d, key, dictrait)
- if entry.valid:
+def ll_dict_getitem(d, key):
+ entry = ll_dict_lookup(d, key, d.keyhash(key))
+ if entry.valid():
return entry.value
else:
raise KeyError
-def ll_dict_setitem(d, key, value, dictrait):
- entry = ll_dict_lookup(d, key, dictrait)
+def ll_dict_setitem(d, key, value):
+ hash = d.keyhash(key)
+ entry = ll_dict_lookup(d, key, hash)
+ everused = entry.everused()
+ valid = entry.valid()
+ # set up the new entry
+ ENTRY = lltype.typeOf(entry).TO
entry.value = value
- if entry.valid:
+ if valid:
return
- if dictrait.custom_eq_hash:
- hash = hlinvoke(dictrait.r_rdict_hashfn, d.fnkeyhash, key)
- else:
- hash = dictrait.ll_keyhash(key)
- entry.key = key
- entry.hash = hash
- entry.valid = True
+ entry.key = key
+ if hasattr(ENTRY, 'f_hash'): entry.f_hash = hash
+ if hasattr(ENTRY, 'f_valid'): entry.f_valid = True
d.num_items += 1
- if not entry.everused:
- entry.everused = True
+ if not everused:
+ if hasattr(ENTRY, 'f_everused'): entry.f_everused = True
d.num_pristine_entries -= 1
if d.num_pristine_entries <= len(d.entries) / 3:
- ll_dict_resize(d, dictrait)
+ ll_dict_resize(d)
-def ll_dict_delitem(d, key, dictrait):
- entry = ll_dict_lookup(d, key, dictrait)
- if not entry.valid:
+def ll_dict_insertclean(d, key, value, hash):
+ # Internal routine used by ll_dict_resize() to insert an item which is
+ # known to be absent from the dict. This routine also assumes that
+ # the dict contains no deleted entries. This routine has the advantage
+ # of never calling d.keyhash() and d.keyeq(), so it cannot call back
+ # to user code. ll_dict_insertclean() doesn't resize the dict, either.
+ entry = ll_dict_lookup_clean(d, hash)
+ ENTRY = lltype.typeOf(entry).TO
+ entry.value = value
+ entry.key = key
+ if hasattr(ENTRY, 'f_hash'): entry.f_hash = hash
+ if hasattr(ENTRY, 'f_valid'): entry.f_valid = True
+ if hasattr(ENTRY, 'f_everused'): entry.f_everused = True
+ d.num_items += 1
+ d.num_pristine_entries -= 1
+
+def ll_dict_delitem(d, key):
+ entry = ll_dict_lookup(d, key, d.keyhash(key))
+ if not entry.valid():
raise KeyError
- entry.valid = False
+ entry.mark_deleted()
d.num_items -= 1
- # clear the key and the value if they are pointers
- keytype = lltype.typeOf(entry).TO.key
- if isinstance(keytype, lltype.Ptr):
- key = entry.key # careful about destructor side effects
- entry.key = lltype.nullptr(keytype.TO)
- valuetype = lltype.typeOf(entry).TO.value
- if isinstance(valuetype, lltype.Ptr):
- entry.value = lltype.nullptr(valuetype.TO)
+ # clear the key and the value if they are GC pointers
+ ENTRY = lltype.typeOf(entry).TO
+ if ENTRY.must_clear_key:
+ key = entry.key # careful about destructor side effects:
+ # keep key alive until entry.value has also
+ # been zeroed (if it must be)
+ entry.key = lltype.nullptr(ENTRY.key.TO)
+ if ENTRY.must_clear_value:
+ entry.value = lltype.nullptr(ENTRY.value.TO)
num_entries = len(d.entries)
if num_entries > DICT_INITSIZE and d.num_items < num_entries / 4:
- ll_dict_resize(d, dictrait)
+ ll_dict_resize(d)
-def ll_dict_resize(d, dictrait):
+def ll_dict_resize(d):
old_entries = d.entries
old_size = len(old_entries)
# make a 'new_size' estimate and shrink it if there are many
@@ -409,49 +494,41 @@
while new_size > DICT_INITSIZE and d.num_items < new_size / 4:
new_size /= 2
d.entries = lltype.malloc(lltype.typeOf(old_entries).TO, new_size)
- d.num_pristine_entries = new_size - d.num_items
+ d.num_items = 0
+ d.num_pristine_entries = new_size
i = 0
while i < old_size:
entry = old_entries[i]
- if entry.valid:
- new_entry = ll_dict_lookup(d, entry.key, dictrait)
- new_entry.key = entry.key
- new_entry.hash = entry.hash
- new_entry.value = entry.value
- new_entry.valid = True
- new_entry.everused = True
+ if entry.valid():
+ ll_dict_insertclean(d, entry.key, entry.value, entry.hash())
i += 1
# ------- a port of CPython's dictobject.c's lookdict implementation -------
PERTURB_SHIFT = 5
-def ll_dict_lookup(d, key, dictrait):
- if dictrait.custom_eq_hash:
- hash = hlinvoke(dictrait.r_rdict_hashfn, d.fnkeyhash, key)
- else:
- hash = dictrait.ll_keyhash(key)
+def ll_dict_lookup(d, key, hash):
entries = d.entries
mask = len(entries) - 1
i = r_uint(hash & mask)
# do the first try before any looping
entry = entries[i]
- if entry.valid:
+ if entry.valid():
checkingkey = entry.key
if checkingkey == key:
return entry # found the entry
- if entry.hash == hash:
- if dictrait.custom_eq_hash:
- res = hlinvoke(dictrait.r_rdict_eqfn, d.fnkeyeq, checkingkey, key)
+ if d.keyeq is not None and entry.hash() == hash:
+ # correct hash, maybe the key is e.g. a different pointer to
+ # an equal object
+ found = d.keyeq(checkingkey, key)
+ if 1: # XXX not always needed
if (entries != d.entries or
- not entry.valid or entry.key != checkingkey):
+ not entry.valid() or entry.key != checkingkey):
# the compare did major nasty stuff to the dict: start over
- return ll_dict_lookup(d, key, dictrait)
- else:
- res = dictrait.ll_keyeq is not None and dictrait.ll_keyeq(checkingkey, key)
- if res:
+ return ll_dict_lookup(d, key, hash)
+ if found:
return entry # found the entry
freeslot = lltype.nullptr(lltype.typeOf(entry).TO)
- elif entry.everused:
+ elif entry.everused():
freeslot = entry
else:
return entry # pristine entry -- lookup failed
@@ -462,44 +539,65 @@
while 1:
i = ((i << 2) + i + perturb + 1) & mask
entry = entries[i]
- if not entry.everused:
+ if not entry.everused():
return freeslot or entry
- elif entry.valid:
+ elif entry.valid():
checkingkey = entry.key
if checkingkey == key:
return entry
- if entry.hash == hash:
- if dictrait.custom_eq_hash:
- res = hlinvoke(dictrait.r_rdict_eqfn, d.fnkeyeq, checkingkey, key)
+ if d.keyeq is not None and entry.hash() == hash:
+ # correct hash, maybe the key is e.g. a different pointer to
+ # an equal object
+ found = d.keyeq(checkingkey, key)
+ if 1: # XXX not always needed
if (entries != d.entries or
- not entry.valid or entry.key != checkingkey):
- # the compare did major nasty stuff to the dict: start over
- return ll_dict_lookup(d, key, dictrait)
- else:
- res = dictrait.ll_keyeq is not None and dictrait.ll_keyeq(checkingkey, key)
- if res:
- return entry
+ not entry.valid() or entry.key != checkingkey):
+ # the compare did major nasty stuff to the dict:
+ # start over
+ return ll_dict_lookup(d, key, hash)
+ if found:
+ return entry # found the entry
elif not freeslot:
freeslot = entry
perturb >>= PERTURB_SHIFT
+def ll_dict_lookup_clean(d, hash):
+ # a simplified version of ll_dict_lookup() which assumes that the
+ # key is new, and the dictionary doesn't contain deleted entries.
+ # It only find the next free slot for the given hash.
+ entries = d.entries
+ mask = len(entries) - 1
+ i = r_uint(hash & mask)
+ entry = entries[i]
+ perturb = r_uint(hash)
+ while entry.everused():
+ i = ((i << 2) + i + perturb + 1) & mask
+ entry = entries[i]
+ return entry
+
# ____________________________________________________________
#
# Irregular operations.
DICT_INITSIZE = 8
-def ll_newdict(dictrait):
- d = lltype.malloc(dictrait.DICT)
- d.entries = lltype.malloc(dictrait.DICTENTRYARRAY, DICT_INITSIZE)
- d.num_items = 0 # but still be explicit
+def ll_newdict(DICT):
+ d = lltype.malloc(DICT)
+ d.entries = lltype.malloc(DICT.entries.TO, DICT_INITSIZE)
+ #d.num_items = 0 -- defaults
d.num_pristine_entries = DICT_INITSIZE
return d
-def ll_copy_extra_data(targetdict, sourcedict, dictrait):
- if dictrait.custom_eq_hash:
- targetdict.fnkeyeq = sourcedict.fnkeyeq
- targetdict.fnkeyhash = sourcedict.fnkeyhash
+def ll_newdict_size(DICT, length_estimate):
+ length_estimate = (length_estimate // 2) * 3
+ n = DICT_INITSIZE
+ while n < length_estimate:
+ n *= 2
+ d = lltype.malloc(DICT)
+ d.entries = lltype.malloc(DICT.entries.TO, n)
+ #d.num_items = 0 -- defaults
+ d.num_pristine_entries = DICT_INITSIZE
+ return d
def rtype_newdict(hop):
hop.inputargs() # no arguments expected
@@ -507,8 +605,8 @@
if r_dict == robject.pyobj_repr: # special case: SomeObject: SomeObject dicts!
cdict = hop.inputconst(robject.pyobj_repr, dict)
return hop.genop('simple_call', [cdict], resulttype = robject.pyobj_repr)
- ctrait = hop.inputconst(lltype.Void, r_dict.get_trait())
- v_result = hop.gendirectcall(ll_newdict, ctrait)
+ cDICT = hop.inputconst(lltype.Void, r_dict.DICT)
+ v_result = hop.gendirectcall(ll_newdict, cDICT)
return v_result
def rtype_r_dict(hop):
@@ -517,9 +615,9 @@
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)
- ctrait = hop.inputconst(lltype.Void, r_dict)
+ cDICT = hop.inputconst(lltype.Void, r_dict.DICT)
hop.exception_cannot_occur()
- v_result = hop.gendirectcall(ll_newdict, ctrait)
+ v_result = hop.gendirectcall(ll_newdict, cDICT)
if r_dict.r_rdict_eqfn.lowleveltype != lltype.Void:
cname = hop.inputconst(lltype.Void, 'fnkeyeq')
hop.genop('setfield', [v_result, cname, v_eqfn])
@@ -579,7 +677,7 @@
while index < entries_len:
entry = entries[index]
index = index + 1
- if entry.valid:
+ if entry.valid():
iter.index = index
if func is dum_items:
r = lltype.malloc(RETURNTYPE.TO)
@@ -597,49 +695,51 @@
# _____________________________________________________________
# methods
-def ll_get(dict, key, default, dictrait):
- entry = ll_dict_lookup(dict, key, dictrait)
- if entry.valid:
+def ll_get(dict, key, default):
+ entry = ll_dict_lookup(dict, key, dict.keyhash(key))
+ if entry.valid():
return entry.value
else:
return default
-def ll_copy(dict, dictrait):
+def ll_copy(dict):
+ DICT = lltype.typeOf(dict).TO
dictsize = len(dict.entries)
- d = lltype.malloc(dictrait.DICT)
- d.entries = lltype.malloc(dictrait.DICTENTRYARRAY, dictsize)
+ d = lltype.malloc(DICT)
+ d.entries = lltype.malloc(DICT.entries.TO, dictsize)
d.num_items = dict.num_items
d.num_pristine_entries = dict.num_pristine_entries
- ll_copy_extra_data(d, dict, dictrait)
+ if hasattr(DICT, 'fnkeyeq'): d.fnkeyeq = dict.fnkeyeq
+ if hasattr(DICT, 'fnkeyhash'): d.fnkeyhash = dict.fnkeyhash
i = 0
while i < dictsize:
d_entry = d.entries[i]
entry = dict.entries[i]
+ ENTRY = lltype.typeOf(entry).TO
d_entry.key = entry.key
- d_entry.hash = entry.hash
+ if hasattr(ENTRY, 'f_valid'): d_entry.f_valid = entry.f_valid
+ if hasattr(ENTRY, 'f_everused'): d_entry.f_everused = entry.f_everused
d_entry.value = entry.value
- d_entry.valid = entry.valid
- d_entry.everused = entry.everused
+ if hasattr(ENTRY, 'f_hash'): d_entry.f_hash = entry.f_hash
i += 1
return d
def ll_clear(d):
if len(d.entries) == d.num_pristine_entries == DICT_INITSIZE:
return
- DICTPTR = lltype.typeOf(d)
- d.entries = lltype.malloc(DICTPTR.TO.entries.TO, DICT_INITSIZE)
+ DICT = lltype.typeOf(d).TO
+ d.entries = lltype.malloc(DICT.entries.TO, DICT_INITSIZE)
d.num_items = 0
d.num_pristine_entries = DICT_INITSIZE
-def ll_update(dic1, dic2, dictrait):
- # XXX warning, no protection against ll_dict_setitem mutating dic2
- d2len = len(dic2.entries)
+def ll_update(dic1, dic2):
entries = dic2.entries
+ d2len = len(entries)
i = 0
while i < d2len:
entry = entries[i]
- if entry.valid:
- ll_dict_setitem(dic1, entry.key, entry.value, dictrait)
+ if entry.valid():
+ ll_dict_setitem(dic1, entry.key, entry.value)
i += 1
# this is an implementation of keys(), values() and items()
@@ -655,14 +755,14 @@
def ll_kvi(dic, LIST, func):
res = LIST.ll_newlist(dic.num_items)
- dlen = len(dic.entries)
entries = dic.entries
+ dlen = len(entries)
items = res.ll_items()
i = 0
p = 0
while i < dlen:
entry = entries[i]
- if entry.valid:
+ if entry.valid():
ELEM = lltype.typeOf(items).TO.OF
if func is dum_items:
r = lltype.malloc(ELEM.TO)
@@ -672,11 +772,11 @@
elif func is dum_keys:
items[p] = recast(ELEM, entry.key)
elif func is dum_values:
- items[p] = recast(ELEM, entry.value)
+ items[p] = recast(ELEM, entry.value)
p += 1
i += 1
return res
-def ll_contains(d, key, dictrait):
- entry = ll_dict_lookup(d, key, dictrait)
- return entry.valid
+def ll_contains(d, key):
+ entry = ll_dict_lookup(d, key, d.keyhash(key))
+ return entry.valid()
Modified: pypy/branch/pypy-rdict-refactoring/rpython/rfloat.py
==============================================================================
--- pypy/dist/pypy/rpython/rfloat.py (original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/rfloat.py Thu Mar 23 00:35:06 2006
@@ -154,8 +154,6 @@
v = (v - float(hipart)) * TAKE_NEXT
x = hipart + int(v) + (expo << 15)
return x
-ll_hash_float.cache_in_dict = True
-
#
# _________________________ Conversions _________________________
Modified: pypy/branch/pypy-rdict-refactoring/rpython/rint.py
==============================================================================
--- pypy/dist/pypy/rpython/rint.py (original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/rint.py Thu Mar 23 00:35:06 2006
@@ -225,6 +225,17 @@
def get_ll_hash_function(self):
return ll_hash_int
+ get_ll_fasthash_function = get_ll_hash_function
+
+ def get_ll_dummyval_obj(self, rtyper, s_value):
+ # if >= 0, then all negative values are special
+ if s_value.nonneg and not s_value.unsigned:
+ return signed_repr # whose ll_dummy_value is -1
+ else:
+ return None
+
+ ll_dummy_value = -1
+
def rtype_chr(_, hop):
vlist = hop.inputargs(Signed)
if hop.has_implicit_exception(ValueError):
@@ -416,9 +427,11 @@
j += 1
return result
-def ll_hash_int(n):
+def ll_identity(n):
return n
+ll_hash_int = ll_identity
+
def ll_check_chr(n):
if 0 <= n <= 255:
return
Modified: pypy/branch/pypy-rdict-refactoring/rpython/rmodel.py
==============================================================================
--- pypy/dist/pypy/rpython/rmodel.py (original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/rmodel.py Thu Mar 23 00:35:06 2006
@@ -5,6 +5,7 @@
from pypy.rpython.lltypesystem.lltype import \
Void, Bool, Float, Signed, Char, UniChar, \
typeOf, LowLevelType, Ptr, PyObject, isCompatibleType
+from pypy.rpython.lltypesystem import lltype
from pypy.rpython.ootypesystem import ootype
from pypy.rpython.error import TyperError, MissingRTypeOperation
@@ -135,12 +136,41 @@
def get_ll_hash_function(self):
"""Return a hash(x) function for low-level values of this Repr.
- As a hint, the function can have a flag 'cache_in_dict=True' if it
- makes non-trivial computations and its result should be cached in
- dictionary entries.
"""
raise TyperError, 'no hashing function for %r' % self
+ def get_ll_fasthash_function(self):
+ """Return a 'fast' hash(x) function for low-level values of this
+ Repr. The function can assume that 'x' is already stored as a
+ key in a dict. get_ll_fasthash_function() should return None if
+ the hash should rather be cached in the dict entry.
+ """
+ return None
+
+ def can_ll_be_null(self, s_value):
+ """Check if the low-level repr can take the value 0/NULL.
+ The annotation s_value is provided as a hint because it may
+ contain more information than the Repr.
+ """
+ return True # conservative
+
+ def get_ll_dummyval_obj(self, rtyper, s_value):
+ """A dummy value is a special low-level value, not otherwise
+ used. It should not be the NULL value even if it is special.
+ This returns either None, or a hashable object that has a
+ (possibly lazy) attribute 'll_dummy_value'.
+ The annotation s_value is provided as a hint because it may
+ contain more information than the Repr.
+ """
+ T = self.lowleveltype
+ if (isinstance(T, lltype.Ptr) and
+ isinstance(T.TO, (lltype.Struct,
+ lltype.Array,
+ lltype.ForwardReference))):
+ return DummyValueBuilder(rtyper, T.TO)
+ else:
+ return None
+
def rtype_bltn_list(self, hop):
raise TyperError, 'no list() support for %r' % self
@@ -310,6 +340,7 @@
lowleveltype = Void
def get_ll_eq_function(self): return None
def get_ll_hash_function(self): return ll_hash_void
+ get_ll_fasthash_function = get_ll_hash_function
impossible_repr = VoidRepr()
# ____________________________________________________________
@@ -390,7 +421,43 @@
return item_repr, rclass.getinstancerepr(rtyper, None)
else:
return item_repr, item_repr
-
+
+
+class DummyValueBuilder(object):
+
+ def __init__(self, rtyper, TYPE):
+ self.rtyper = rtyper
+ self.TYPE = TYPE
+
+ def _freeze_(self):
+ return True
+
+ def __hash__(self):
+ return hash(self.TYPE)
+
+ def __eq__(self, other):
+ return (isinstance(other, DummyValueBuilder) and
+ self.rtyper is other.rtyper and
+ self.TYPE == other.TYPE)
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def build_ll_dummy_value(self):
+ TYPE = self.TYPE
+ try:
+ return self.rtyper.cache_dummy_values[TYPE]
+ except KeyError:
+ # generate a dummy ptr to an immortal placeholder struct/array
+ if TYPE._is_varsize():
+ p = lltype.malloc(TYPE, 0, immortal=True)
+ else:
+ p = lltype.malloc(TYPE, immortal=True)
+ self.rtyper.cache_dummy_values[TYPE] = p
+ return p
+
+ ll_dummy_value = property(build_ll_dummy_value)
+
# logging/warning
Modified: pypy/branch/pypy-rdict-refactoring/rpython/rstr.py
==============================================================================
--- pypy/dist/pypy/rpython/rstr.py (original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/rstr.py Thu Mar 23 00:35:06 2006
@@ -80,6 +80,16 @@
def get_ll_hash_function(self):
return ll_strhash
+ def get_ll_fasthash_function(self):
+ return ll_strfasthash
+
+ def can_ll_be_null(self, s_value):
+ if self is string_repr:
+ return s_value.can_be_none()
+ else:
+ return True # for CharRepr/UniCharRepr subclasses,
+ # where NULL is always valid: it is chr(0)
+
def rtype_len(_, hop):
v_str, = hop.inputargs(string_repr)
return hop.gendirectcall(ll_strlen, v_str)
@@ -404,6 +414,8 @@
def get_ll_hash_function(self):
return ll_char_hash
+ get_ll_fasthash_function = get_ll_hash_function
+
def ll_str(self, ch):
return ll_chr2str(ch)
@@ -476,6 +488,8 @@
def get_ll_hash_function(self):
return ll_unichar_hash
+ get_ll_fasthash_function = get_ll_hash_function
+
## def rtype_len(_, hop):
## return hop.inputconst(Signed, 1)
##
@@ -641,6 +655,9 @@
s.hash = x
return x
+def ll_strfasthash(s):
+ return s.hash # assumes that the hash is already computed
+
def ll_strconcat(s1, s2):
len1 = len(s1.chars)
len2 = len(s2.chars)
Modified: pypy/branch/pypy-rdict-refactoring/rpython/rtuple.py
==============================================================================
--- pypy/dist/pypy/rpython/rtuple.py (original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/rtuple.py Thu Mar 23 00:35:06 2006
@@ -81,7 +81,6 @@
source = source % body
exec source in miniglobals
ll_hash = miniglobals['ll_hash']
- ll_hash.cache_in_dict = True
_gen_hash_function_cache[key] = ll_hash
return ll_hash
@@ -127,6 +126,9 @@
def get_ll_hash_function(self):
return gen_hash_function(self.items_r)
+ def can_ll_be_null(self, s_value):
+ return False
+
def rtype_len(self, hop):
return hop.inputconst(Signed, len(self.items_r))
Modified: pypy/branch/pypy-rdict-refactoring/rpython/rtyper.py
==============================================================================
--- pypy/dist/pypy/rpython/rtyper.py (original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/rtyper.py Thu Mar 23 00:35:06 2006
@@ -59,6 +59,7 @@
self.concrete_calltables = {}
self.class_pbc_attributes = {}
self.oo_meth_impls = {}
+ self.cache_dummy_values = {}
self.typererrors = []
self.typererror_count = 0
# make the primitive_to_repr constant mapping
Modified: pypy/branch/pypy-rdict-refactoring/rpython/test/test_rdict.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_rdict.py (original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/test/test_rdict.py Thu Mar 23 00:35:06 2006
@@ -123,7 +123,7 @@
res = interpret(func2, [ord(x), ord(y)])
for i in range(len(res.entries)):
- assert not (res.entries[i].everused and not res.entries[i].valid)
+ assert not (res.entries[i].everused() and not res.entries[i].valid())
def func3(c0, c1, c2, c3, c4, c5, c6, c7):
d = {}
@@ -143,7 +143,7 @@
for i in range(rdict.DICT_INITSIZE)])
count_frees = 0
for i in range(len(res.entries)):
- if not res.entries[i].everused:
+ if not res.entries[i].everused():
count_frees += 1
assert count_frees >= 3
@@ -444,9 +444,13 @@
yield x
def test_stress():
- dictrepr = rdict.DictRepr(None, rint.signed_repr, rint.signed_repr)
+ from pypy.annotation.dictdef import DictKey, DictValue
+ from pypy.annotation import model as annmodel
+ dictrepr = rdict.DictRepr(None, rint.signed_repr, rint.signed_repr,
+ DictKey(None, annmodel.SomeInteger()),
+ DictValue(None, annmodel.SomeInteger()))
dictrepr.setup()
- l_dict = rdict.ll_newdict(dictrepr)
+ l_dict = rdict.ll_newdict(dictrepr.DICT)
referencetable = [None] * 400
referencelength = 0
value = 0
@@ -454,7 +458,7 @@
def complete_check():
for n, refvalue in zip(range(len(referencetable)), referencetable):
try:
- gotvalue = rdict.ll_dict_getitem(l_dict, n, dictrepr)
+ gotvalue = rdict.ll_dict_getitem(l_dict, n)
except KeyError:
assert refvalue is None
else:
@@ -464,18 +468,18 @@
n = int(x*100.0) # 0 <= x < 400
op = repr(x)[-1]
if op <= '2' and referencetable[n] is not None:
- rdict.ll_dict_delitem(l_dict, n, dictrepr)
+ rdict.ll_dict_delitem(l_dict, n)
referencetable[n] = None
referencelength -= 1
elif op <= '6':
- rdict.ll_dict_setitem(l_dict, n, value, dictrepr)
+ rdict.ll_dict_setitem(l_dict, n, value)
if referencetable[n] is None:
referencelength += 1
referencetable[n] = value
value += 1
else:
try:
- gotvalue = rdict.ll_dict_getitem(l_dict, n, dictrepr)
+ gotvalue = rdict.ll_dict_getitem(l_dict, n)
except KeyError:
assert referencetable[n] is None
else:
@@ -486,6 +490,78 @@
assert l_dict.num_items == referencelength
complete_check()
+# ____________________________________________________________
+
+def test_opt_nullkeymarker():
+ def f():
+ d = {"hello": None}
+ d["world"] = None
+ return "hello" in d, d
+ res = interpret(f, [])
+ assert res.item0 == True
+ DICT = lltype.typeOf(res.item1).TO
+ assert not hasattr(DICT.entries.TO.OF, 'f_everused')# non-None string keys
+ assert not hasattr(DICT.entries.TO.OF, 'f_valid') # strings have a dummy
+
+def test_opt_nullvaluemarker():
+ def f(n):
+ d = {-5: "abcd"}
+ d[123] = "def"
+ return len(d[n]), d
+ res = interpret(f, [-5])
+ assert res.item0 == 4
+ DICT = lltype.typeOf(res.item1).TO
+ assert not hasattr(DICT.entries.TO.OF, 'f_everused')# non-None str values
+ assert not hasattr(DICT.entries.TO.OF, 'f_valid') # strs have a dummy
+
+def test_opt_nonullmarker():
+ class A:
+ pass
+ def f(n):
+ if n > 5:
+ a = A()
+ else:
+ a = None
+ d = {a: -5441}
+ d[A()] = n+9872
+ return d[a], d
+ res = interpret(f, [-5])
+ assert res.item0 == -5441
+ DICT = lltype.typeOf(res.item1).TO
+ assert hasattr(DICT.entries.TO.OF, 'f_everused') # can-be-None A instances
+ assert not hasattr(DICT.entries.TO.OF, 'f_valid')# with a dummy A instance
+
+ res = interpret(f, [6])
+ assert res.item0 == -5441
+
+def test_opt_nonnegint_dummy():
+ def f(n):
+ d = {n: 12}
+ d[-87] = 24
+ del d[n]
+ return len(d.copy()), d[-87], d
+ res = interpret(f, [5])
+ assert res.item0 == 1
+ assert res.item1 == 24
+ DICT = lltype.typeOf(res.item2).TO
+ assert hasattr(DICT.entries.TO.OF, 'f_everused') # all ints can be zero
+ assert not hasattr(DICT.entries.TO.OF, 'f_valid')# nonneg int: dummy -1
+
+def test_opt_no_dummy():
+ def f(n):
+ d = {n: 12}
+ d[-87] = -24
+ del d[n]
+ return len(d.copy()), d[-87], d
+ res = interpret(f, [5])
+ assert res.item0 == 1
+ assert res.item1 == -24
+ DICT = lltype.typeOf(res.item2).TO
+ assert hasattr(DICT.entries.TO.OF, 'f_everused') # all ints can be zero
+ assert hasattr(DICT.entries.TO.OF, 'f_valid') # no dummy available
+
+# ____________________________________________________________
+
def test_id_instances_keys():
class A:
pass
@@ -560,4 +636,14 @@
res = interpret(f, [2])
assert res == f(2)
-
+def test_dict_of_dict():
+ def f(n):
+ d = {}
+ d[5] = d
+ d[6] = {}
+ return len(d[n])
+
+ res = interpret(f, [5])
+ assert res == 2
+ res = interpret(f, [6])
+ assert res == 0
More information about the Pypy-commit
mailing list