[pypy-svn] r17455 - in pypy/dist/pypy/rpython: . test
arigo at codespeak.net
arigo at codespeak.net
Sun Sep 11 13:32:58 CEST 2005
Author: arigo
Date: Sun Sep 11 13:32:56 2005
New Revision: 17455
Modified:
pypy/dist/pypy/rpython/llinterp.py
pypy/dist/pypy/rpython/objectmodel.py
pypy/dist/pypy/rpython/rbuiltin.py
pypy/dist/pypy/rpython/rdict.py
pypy/dist/pypy/rpython/rpbc.py
pypy/dist/pypy/rpython/test/test_objectmodel.py
pypy/dist/pypy/rpython/test/test_rconstantdict.py
Log:
Progress on test_rtype_r_dict, but not passing yet.
* the r_dict becomes a GcStruct with two new fields, fnkeyeq and fnkeyhash,
that are function pointers (which usually means just Voids, if the functions
in question are constant).
* we pass a test about a constant prebuilt r_dict (thanks pedronis for hints).
* ll_dict_lookup() is slightly verbose now :-(
* fix in FunctionsPBCRepr.convert_const(), which should return None for Voids.
* llinterp prints more infos about the LLExceptions it got -- at least the
exception's class name.
* the _r_dictkey now caches the hash -- needed for rdict.convert_const().
Modified: pypy/dist/pypy/rpython/llinterp.py
==============================================================================
--- pypy/dist/pypy/rpython/llinterp.py (original)
+++ pypy/dist/pypy/rpython/llinterp.py Sun Sep 11 13:32:56 2005
@@ -39,6 +39,11 @@
llframe = LLFrame(graph, args, self)
try:
return llframe.eval()
+ except LLException, e:
+ etype, evalue = e.args
+ print "LLEXCEPTION:", etype.name
+ self.print_traceback()
+ raise
except Exception, e:
print "AN ERROR OCCURED:", e
self.print_traceback()
Modified: pypy/dist/pypy/rpython/objectmodel.py
==============================================================================
--- pypy/dist/pypy/rpython/objectmodel.py (original)
+++ pypy/dist/pypy/rpython/objectmodel.py Sun Sep 11 13:32:56 2005
@@ -101,24 +101,20 @@
def clear(self):
self._dict.clear()
- def key_eq(self, key1, key2):
- "Called to compare two keys. Can be overridden in subclasses."
- return key1 == key2
-
- def key_hash(self, key):
- "Called to compute the hash of a key. Can be overridden in subclasses."
- return hash(key)
-
def __repr__(self):
"Representation for debugging purposes."
return 'r_dict(%r)' % (dict(self.items()),)
+ def __hash__(self):
+ raise TypeError("cannot hash r_dict instances")
+
class _r_dictkey(object):
- __slots__ = ['dic', 'key']
+ __slots__ = ['dic', 'key', 'hash']
def __init__(self, dic, key):
self.dic = dic
self.key = key
+ self.hash = dic.key_hash(key)
def __eq__(self, other):
if not isinstance(other, _r_dictkey):
return NotImplemented
@@ -128,4 +124,4 @@
return NotImplemented
return not self.dic.key_eq(self.key, other.key)
def __hash__(self):
- return self.dic.key_hash(self.key)
+ return self.hash
Modified: pypy/dist/pypy/rpython/rbuiltin.py
==============================================================================
--- pypy/dist/pypy/rpython/rbuiltin.py (original)
+++ pypy/dist/pypy/rpython/rbuiltin.py Sun Sep 11 13:32:56 2005
@@ -9,6 +9,7 @@
from pypy.rpython.robject import pyobj_repr
from pypy.rpython.rfloat import float_repr, FloatRepr
from pypy.rpython.rbool import bool_repr
+from pypy.rpython.rdict import rtype_r_dict
from pypy.rpython import rclass
from pypy.tool import sourcetools
@@ -154,6 +155,8 @@
#def rtype_builtin_xrange(hop): see rrange.py
+#def rtype_r_dict(hop): see rdict.py
+
def rtype_intmask(hop):
vlist = hop.inputargs(lltype.Signed)
return vlist[0]
@@ -305,6 +308,7 @@
BUILTIN_TYPER[lltype.runtime_type_info] = rtype_runtime_type_info
BUILTIN_TYPER[rarithmetic.intmask] = rtype_intmask
BUILTIN_TYPER[rarithmetic.r_uint] = rtype_r_uint
+BUILTIN_TYPER[objectmodel.r_dict] = rtype_r_dict
BUILTIN_TYPER[objectmodel.instantiate] = rtype_instantiate
BUILTIN_TYPER[objectmodel.we_are_translated] = rtype_we_are_translated
Modified: pypy/dist/pypy/rpython/rdict.py
==============================================================================
--- pypy/dist/pypy/rpython/rdict.py (original)
+++ pypy/dist/pypy/rpython/rdict.py Sun Sep 11 13:32:56 2005
@@ -3,8 +3,10 @@
from pypy.objspace.flow.model import Constant
from pypy.rpython import rmodel, lltype
from pypy.rpython.rarithmetic import r_uint
+from pypy.rpython.objectmodel import hlinvoke
from pypy.rpython import rlist
from pypy.rpython import robject
+from pypy.rpython import objectmodel
# ____________________________________________________________
#
@@ -17,7 +19,7 @@
# the array should be inlined and num_pristine_entries is not needed.
#
# struct dictentry {
-# DICTSTR key;
+# DICTKEY key;
# bool valid; # to mark if the entry is filled
# bool everused; # to mark if the entry is or has ever been filled
# DICTVALUE value;
@@ -26,7 +28,9 @@
# struct dicttable {
# int num_items;
# int num_pristine_entries; # never used entries
-# Array *entries;
+# Array *entries;
+# (Function DICTKEY, DICTKEY -> bool) *fnkeyeq;
+# (Function DICTKEY -> int) *fnkeyhash;
# }
#
#
@@ -41,17 +45,24 @@
s_value.__class__ is annmodel.SomeObject and s_value.knowntype == object):
return robject.pyobj_repr
else:
+ if dictkey.custom_eq_hash:
+ custom_eq_hash = lambda: (rtyper.getrepr(dictkey.s_rdict_eqfn),
+ rtyper.getrepr(dictkey.s_rdict_hashfn))
+ else:
+ custom_eq_hash = None
return DictRepr(lambda: rtyper.getrepr(s_key),
lambda: rtyper.getrepr(s_value),
dictkey,
- dictvalue)
+ dictvalue,
+ custom_eq_hash)
def rtyper_makekey(self):
return (self.__class__, self.dictdef.dictkey, self.dictdef.dictvalue)
class DictRepr(rmodel.Repr):
- def __init__(self, key_repr, value_repr, dictkey=None, dictvalue=None):
+ def __init__(self, key_repr, value_repr, dictkey=None, dictvalue=None,
+ custom_eq_hash=None):
self.DICT = lltype.GcForwardReference()
self.lowleveltype = lltype.Ptr(self.DICT)
if not isinstance(key_repr, rmodel.Repr): # not computed yet, done by setup()
@@ -67,6 +78,8 @@
self.dictkey = dictkey
self.dictvalue = dictvalue
self.dict_cache = {}
+ self.custom_eq_hash = custom_eq_hash is not None
+ self._custom_eq_hash_repr = custom_eq_hash
# setup() needs to be called to finish this initialization
def _setup_repr(self):
@@ -83,11 +96,15 @@
("everused", lltype.Bool),
("value", self.DICTVALUE))
self.DICTENTRYARRAY = lltype.GcArray(self.DICTENTRY)
- self.DICT.become(lltype.GcStruct("dicttable",
- ("num_items", lltype.Signed),
+ fields = [ ("num_items", lltype.Signed),
("num_pristine_entries", lltype.Signed),
- ("entries", lltype.Ptr(self.DICTENTRYARRAY))))
- if 'll_keyhash' not in self.__dict__:
+ ("entries", lltype.Ptr(self.DICTENTRYARRAY)) ]
+ if self.custom_eq_hash:
+ 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()
@@ -97,22 +114,49 @@
#dictobj = getattr(dictobj, '__self__', dictobj)
if dictobj is None:
return nullptr(self.DICT)
- if not isinstance(dictobj, dict):
+ if not isinstance(dictobj, (dict, objectmodel.r_dict)):
raise TyperError("expected a dict: %r" % (dictobj,))
try:
key = Constant(dictobj)
return self.dict_cache[key]
except KeyError:
self.setup()
- l_dict = ll_newdict(self.lowleveltype)
- self.dict_cache[key] = l_dict
- r_key = self.key_repr
- r_value = self.value_repr
- 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)
- return l_dict
+ if isinstance(dictobj, objectmodel.r_dict):
+ l_eqfn = self.r_rdict_eqfn .convert_const(dictobj.key_eq)
+ l_hashfn = self.r_rdict_hashfn.convert_const(dictobj.key_hash)
+ l_dict = ll_newdict_custom_eq_hash(l_eqfn, l_hashfn, self)
+ # 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: ll_dict_lookup() will only see new items,
+ # which are never equal to any existing one
+ return False
+ def ll_keyhash(self, key):
+ return self.currenthash
+
+ self.dict_cache[key] = l_dict
+ r_key = self.key_repr
+ r_value = self.value_repr
+ for dictkeycontainer, dictvalue in dictobj._dict.items():
+ dummy = Dummy()
+ dummy.currenthash = dictkeycontainer.hash
+ llkey = r_key.convert_const(dictkeycontainer.key)
+ llvalue = r_value.convert_const(dictvalue)
+ ll_dict_setitem(l_dict, llkey, llvalue, dummy)
+ return l_dict
+
+ else:
+ l_dict = ll_newdict(self)
+ self.dict_cache[key] = l_dict
+ r_key = self.key_repr
+ r_value = self.value_repr
+ 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)
+ return l_dict
def rtype_len(self, hop):
v_dict, = hop.inputargs(self)
@@ -134,8 +178,9 @@
def rtype_method_copy(self, hop):
v_dict, = hop.inputargs(self)
+ crepr = hop.inputconst(lltype.Void, self)
hop.exception_cannot_occur()
- return hop.gendirectcall(ll_copy, v_dict)
+ return hop.gendirectcall(ll_copy, v_dict, crepr)
def rtype_method_update(self, hop):
v_dic1, v_dic2 = hop.inputargs(self, self)
@@ -301,7 +346,10 @@
PERTURB_SHIFT = 5
def ll_dict_lookup(d, key, dictrepr):
- hash = dictrepr.ll_keyhash(key)
+ if dictrepr.custom_eq_hash:
+ hash = hlinvoke(dictrepr.r_rdict_hashfn, d.fnkeyhash, key)
+ else:
+ hash = dictrepr.ll_keyhash(key)
entries = d.entries
mask = len(entries) - 1
i = r_uint(hash & mask)
@@ -313,7 +361,11 @@
if entry.valid:
if entry.key == key:
return entry # found the entry
- if dictrepr.ll_keyeq is not None and dictrepr.ll_keyeq(entry.key, key):
+ if dictrepr.custom_eq_hash:
+ res = hlinvoke(dictrepr.r_rdict_eqfn, d.fnkeyeq, entry.key, key)
+ else:
+ res = dictrepr.ll_keyeq is not None and dictrepr.ll_keyeq(entry.key, key)
+ if res:
return entry # found the entry
freeslot = lltype.nullptr(lltype.typeOf(entry).TO)
elif entry.everused:
@@ -332,7 +384,11 @@
elif entry.valid:
if entry.key == key:
return entry
- if dictrepr.ll_keyeq is not None and dictrepr.ll_keyeq(entry.key, key):
+ if dictrepr.custom_eq_hash:
+ res = hlinvoke(dictrepr.r_rdict_eqfn, d.fnkeyeq, entry.key, key)
+ else:
+ res = dictrepr.ll_keyeq is not None and dictrepr.ll_keyeq(entry.key, key)
+ if res:
return entry
elif not freeslot:
freeslot = entry
@@ -341,22 +397,52 @@
# ____________________________________________________________
#
# Irregular operations.
+
DICT_INITSIZE = 8
-def ll_newdict(DICTPTR):
- d = lltype.malloc(DICTPTR.TO)
- d.entries = lltype.malloc(DICTPTR.TO.entries.TO, DICT_INITSIZE)
+def ll_newdict(dictrepr):
+ assert not dictrepr.custom_eq_hash # use ll_newdict_custom_eq_hash() instead
+ d = lltype.malloc(dictrepr.DICT)
+ d.entries = lltype.malloc(dictrepr.DICTENTRYARRAY, DICT_INITSIZE)
+ d.num_items = 0 # but still be explicit
+ d.num_pristine_entries = DICT_INITSIZE
+ return d
+
+def ll_newdict_custom_eq_hash(eqfn, hashfn, dictrepr):
+ assert dictrepr.custom_eq_hash
+ d = lltype.malloc(dictrepr.DICT)
+ d.entries = lltype.malloc(dictrepr.DICTENTRYARRAY, DICT_INITSIZE)
d.num_items = 0 # but still be explicit
- d.num_pristine_entries = DICT_INITSIZE
+ d.num_pristine_entries = DICT_INITSIZE
+ d.fnkeyeq = eqfn
+ d.fnkeyhash = hashfn
return d
+def ll_copy_extra_data(targetdict, sourcedict, dictrepr):
+ if dictrepr.custom_eq_hash:
+ targetdict.fnkeyeq = sourcedict.fnkeyeq
+ targetdict.fnkeyhash = sourcedict.fnkeyhash
+
def rtype_newdict(hop):
+ hop.inputargs() # no arguments expected
r_dict = hop.r_result
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)
- c1 = hop.inputconst(lltype.Void, r_dict.lowleveltype)
- v_result = hop.gendirectcall(ll_newdict, c1)
+ crepr = hop.inputconst(lltype.Void, r_dict)
+ v_result = hop.gendirectcall(ll_newdict, crepr)
+ return v_result
+
+def rtype_r_dict(hop):
+ 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)
+ crepr = hop.inputconst(lltype.Void, r_dict)
+ hop.exception_cannot_occur()
+ v_result = hop.gendirectcall(ll_newdict_custom_eq_hash,
+ v_eqfn, v_hashfn, crepr)
return v_result
# ____________________________________________________________
@@ -423,15 +509,15 @@
else:
return default
-def ll_copy(dict):
- DICTPTR = lltype.typeOf(dict)
- d = lltype.malloc(DICTPTR.TO)
- d.entries = lltype.malloc(DICTPTR.TO.entries.TO, len(dict.entries))
+def ll_copy(dict, dictrepr):
+ dictsize = len(dict.entries)
+ d = lltype.malloc(dictrepr.DICT)
+ d.entries = lltype.malloc(dictrepr.DICTENTRYARRAY, dictsize)
d.num_items = dict.num_items
d.num_pristine_entries = dict.num_pristine_entries
+ ll_copy_extra_data(d, dict, dictrepr)
i = 0
- dictlen = len(d.entries)
- while i < dictlen:
+ while i < dictsize:
d_entry = d.entries[i]
entry = dict.entries[i]
d_entry.key = entry.key
Modified: pypy/dist/pypy/rpython/rpbc.py
==============================================================================
--- pypy/dist/pypy/rpython/rpbc.py (original)
+++ pypy/dist/pypy/rpython/rpbc.py Sun Sep 11 13:32:56 2005
@@ -402,8 +402,11 @@
if value not in self.function_signatures():
raise TyperError("%r not in %r" % (value,
self.s_pbc.prebuiltinstances))
- f, rinputs, rresult = self.function_signatures()[value]
- return f
+ if self.lowleveltype == Void:
+ return None
+ else:
+ f, rinputs, rresult = self.function_signatures()[value]
+ return f
def rtype_simple_call(self, hop):
f, rinputs, rresult = self.function_signatures().itervalues().next()
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 Sun Sep 11 13:32:56 2005
@@ -28,7 +28,8 @@
assert False, "should have raised"
assert len(d) == 1
assert 'oops' not in d
- assert list(d) == ['hello']
+ x, = d
+ assert x == 'hello'
assert d.get('hola', -1) == 42
assert d.get('salut', -1) == -1
d1 = d.copy()
@@ -38,9 +39,12 @@
d.update(d1)
assert d.values() == [42]
assert d.items() == [('hello', 42)]
- assert list(d.iterkeys()) == ['hello']
- assert list(d.itervalues()) == [42]
- assert list(d.iteritems()) == [('hello', 42)]
+ x, = d.iterkeys()
+ assert x == 'hello'
+ x, = d.itervalues()
+ assert x == 42
+ x, = d.iteritems()
+ assert x == ('hello', 42)
d.clear()
assert d.keys() == []
return True # for the tests below
Modified: pypy/dist/pypy/rpython/test/test_rconstantdict.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_rconstantdict.py (original)
+++ pypy/dist/pypy/rpython/test/test_rconstantdict.py Sun Sep 11 13:32:56 2005
@@ -1,5 +1,6 @@
import py
-from pypy.rpython.test.test_llinterp import interpret
+from pypy.rpython.test.test_llinterp import interpret
+from pypy.rpython.objectmodel import r_dict
def test_constant_int_dict():
d = {1: 2, 2: 3, 3: 4}
@@ -36,3 +37,19 @@
assert res == 123
res = interpret(func, [63])
assert res == 321
+
+def test_constant_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])
+
+ d = r_dict(strange_key_eq, strange_key_hash)
+ d['hello'] = 42
+ d['world'] = 43
+ def func(i):
+ return d[chr(i)]
+ res = interpret(func, [ord('h')])
+ assert res == 42
+ res = interpret(func, [ord('w')])
+ assert res == 43
More information about the Pypy-commit
mailing list