1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
|
from rpython.flowspace.model import Constant
from rpython.rtyper.lltypesystem import lltype, llmemory, rdict
from rpython.rtyper.lltypesystem.llmemory import weakref_create, weakref_deref
from rpython.rtyper import rclass
from rpython.rtyper.rclass import getinstancerepr
from rpython.rtyper.rmodel import Repr
from rpython.rlib.rweakref import RWeakKeyDictionary
from rpython.rlib import jit
from rpython.rlib.objectmodel import compute_identity_hash
# Warning: this implementation of RWeakKeyDictionary is not exactly
# leaking, but can keep around some values for a long time, even after
# the corresponding keys were freed. They will be eventually freed if
# you continue to manipulate the dictionary. Avoid to use this if the
# values are objects that might keep alive tons of memory.
class WeakKeyDictRepr(Repr):
def __init__(self, rtyper):
self.rtyper = rtyper
self.lowleveltype = lltype.Ptr(WEAKDICT)
self.dict_cache = {}
def convert_const(self, weakdict):
if not isinstance(weakdict, RWeakKeyDictionary):
raise TyperError("expected an RWeakKeyDictionary: %r" % (
weakdict,))
try:
key = Constant(weakdict)
return self.dict_cache[key]
except KeyError:
self.setup()
if weakdict.length() != 0:
raise TyperError("got a non-empty prebuilt RWeakKeyDictionary")
l_dict = ll_new_weakdict()
self.dict_cache[key] = l_dict
return l_dict
def rtype_method_get(self, hop):
r_object = getinstancerepr(self.rtyper, None)
v_d, v_key = hop.inputargs(self, r_object)
hop.exception_cannot_occur()
v_result = hop.gendirectcall(ll_get, v_d, v_key)
v_result = hop.genop("cast_pointer", [v_result],
resulttype=hop.r_result.lowleveltype)
return v_result
def rtype_method_set(self, hop):
r_object = getinstancerepr(self.rtyper, None)
v_d, v_key, v_value = hop.inputargs(self, r_object, r_object)
hop.exception_cannot_occur()
if hop.args_s[2].is_constant() and hop.args_s[2].const is None:
hop.gendirectcall(ll_set_null, v_d, v_key)
else:
hop.gendirectcall(ll_set, v_d, v_key, v_value)
def rtype_method_length(self, hop):
v_d, = hop.inputargs(self)
hop.exception_cannot_occur()
return hop.gendirectcall(ll_length, v_d)
def specialize_make_weakdict(hop):
hop.exception_cannot_occur()
v_d = hop.gendirectcall(ll_new_weakdict)
return v_d
# ____________________________________________________________
NULLVALUE = lltype.nullptr(rclass.OBJECTPTR.TO)
WEAKDICTENTRY = lltype.Struct("weakdictentry",
("key", llmemory.WeakRefPtr),
("value", rclass.OBJECTPTR),
("f_hash", lltype.Signed))
def ll_debugrepr(x):
if x:
h = compute_identity_hash(x)
else:
h = 0
return '<%x>' % (h,)
def ll_valid(entries, i):
key = entries[i].key
if not key:
return False
elif weakref_deref(rclass.OBJECTPTR, key):
return True
else:
# The entry might be a dead weakref still holding a strong
# reference to the value; for this case, we clear the old
# value from the entry, if any.
entries[i].value = NULLVALUE
return False
def ll_everused(entries, i):
return bool(entries[i].key)
entrymeths = {
'allocate': lltype.typeMethod(rdict._ll_malloc_entries),
'delete': rdict._ll_free_entries,
'valid': ll_valid,
'everused': ll_everused,
'hash': rdict.ll_hash_from_cache,
'no_direct_compare': True,
}
WEAKDICTENTRYARRAY = lltype.GcArray(WEAKDICTENTRY,
adtmeths=entrymeths,
hints={'weakarray': 'key'})
# NB. the 'hints' is not used so far ^^^
@jit.dont_look_inside
def ll_new_weakdict():
d = lltype.malloc(WEAKDICT)
d.entries = WEAKDICT.entries.TO.allocate(rdict.DICT_INITSIZE)
d.num_items = 0
d.resize_counter = rdict.DICT_INITSIZE * 2
return d
@jit.dont_look_inside
def ll_get(d, llkey):
hash = compute_identity_hash(llkey)
i = rdict.ll_dict_lookup(d, llkey, hash) & rdict.MASK
#llop.debug_print(lltype.Void, i, 'get', hex(hash),
# ll_debugrepr(d.entries[i].key),
# ll_debugrepr(d.entries[i].value))
# NB. ll_valid() above was just called at least on entry i, so if
# it is an invalid entry with a dead weakref, the value was reset
# to NULLVALUE.
return d.entries[i].value
@jit.dont_look_inside
def ll_set(d, llkey, llvalue):
if llvalue:
ll_set_nonnull(d, llkey, llvalue)
else:
ll_set_null(d, llkey)
@jit.dont_look_inside
def ll_set_nonnull(d, llkey, llvalue):
hash = compute_identity_hash(llkey)
keyref = weakref_create(llkey) # GC effects here, before the rest
i = rdict.ll_dict_lookup(d, llkey, hash) & rdict.MASK
everused = d.entries.everused(i)
d.entries[i].key = keyref
d.entries[i].value = llvalue
d.entries[i].f_hash = hash
#llop.debug_print(lltype.Void, i, 'stored', hex(hash),
# ll_debugrepr(llkey),
# ll_debugrepr(llvalue))
if not everused:
d.resize_counter -= 3
if d.resize_counter <= 0:
#llop.debug_print(lltype.Void, 'RESIZE')
ll_weakdict_resize(d)
@jit.dont_look_inside
def ll_set_null(d, llkey):
hash = compute_identity_hash(llkey)
i = rdict.ll_dict_lookup(d, llkey, hash) & rdict.MASK
if d.entries.everused(i):
# If the entry was ever used, clean up its key and value.
# We don't store a NULL value, but a dead weakref, because
# the entry must still be marked as everused().
d.entries[i].key = llmemory.dead_wref
d.entries[i].value = NULLVALUE
#llop.debug_print(lltype.Void, i, 'zero')
def ll_update_num_items(d):
entries = d.entries
num_items = 0
for i in range(len(entries)):
if entries.valid(i):
num_items += 1
d.num_items = num_items
def ll_weakdict_resize(d):
# first set num_items to its correct, up-to-date value
ll_update_num_items(d)
rdict.ll_dict_resize(d)
def ll_keyeq(d, weakkey1, realkey2):
# only called by ll_dict_lookup() with the first arg coming from an
# entry.key, and the 2nd arg being the argument to ll_dict_lookup().
if not weakkey1:
assert bool(realkey2)
return False
return weakref_deref(rclass.OBJECTPTR, weakkey1) == realkey2
@jit.dont_look_inside
def ll_length(d):
# xxx slow, but it's only for debugging
ll_update_num_items(d)
#llop.debug_print(lltype.Void, 'length:', d.num_items)
return d.num_items
dictmeths = {
'll_get': ll_get,
'll_set': ll_set,
'keyeq': ll_keyeq,
'paranoia': False,
}
WEAKDICT = lltype.GcStruct("weakkeydict",
("num_items", lltype.Signed),
("resize_counter", lltype.Signed),
("entries", lltype.Ptr(WEAKDICTENTRYARRAY)),
adtmeths=dictmeths)
|