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
|
from rpython.annotator.model import s_ImpossibleValue
from rpython.annotator.model import SomeList, SomeString
from rpython.annotator.model import unionof, TLS, UnionError, AnnotatorError
class TooLateForChange(AnnotatorError):
pass
class ListChangeUnallowed(AnnotatorError):
pass
class ListItem(object):
mutated = False # True for lists mutated after creation
resized = False # True for lists resized after creation
range_step = None # the step -- only for lists only created by a range()
dont_change_any_more = False # set to True when too late for changes
immutable = False # for getattr out of _immutable_fields_ = ['attr[*]']
must_not_resize = False # make_sure_not_resized()
# what to do if range_step is different in merge.
# - if one is a list (range_step is None), unify to a list.
# - if both have a step, unify to use a variable step (indicated by 0)
_step_map = {
(type(None), int): None,
(int, type(None)): None,
(int, int) : 0,
}
def __init__(self, bookkeeper, s_value):
self.s_value = s_value
self.bookkeeper = bookkeeper
self.itemof = {} # set of all ListDefs using this ListItem
self.read_locations = {}
if bookkeeper is None:
self.dont_change_any_more = True
def mutate(self):
if not self.mutated:
if self.dont_change_any_more:
raise TooLateForChange
self.immutable = False
self.mutated = True
def resize(self):
if not self.resized:
if self.dont_change_any_more:
raise TooLateForChange
if self.must_not_resize:
raise ListChangeUnallowed("resizing list")
self.resized = True
def setrangestep(self, step):
if step != self.range_step:
if self.dont_change_any_more:
raise TooLateForChange
self.range_step = step
def merge(self, other):
if self is not other:
if getattr(TLS, 'no_side_effects_in_union', 0):
raise UnionError(self, other)
if other.dont_change_any_more:
if self.dont_change_any_more:
raise TooLateForChange
else:
# lists using 'other' don't expect it to change any more,
# so we try merging into 'other', which will give
# TooLateForChange if it actually tries to make
# things more general
self, other = other, self
self.immutable &= other.immutable
if other.must_not_resize:
if self.resized:
raise ListChangeUnallowed("list merge with a resized")
self.must_not_resize = True
if other.mutated:
self.mutate()
if other.resized:
self.resize()
if other.range_step != self.range_step:
self.setrangestep(self._step_map[type(self.range_step),
type(other.range_step)])
self.itemof.update(other.itemof)
s_value = self.s_value
s_other_value = other.s_value
s_new_value = unionof(s_value, s_other_value)
if s_new_value != s_value:
if self.dont_change_any_more:
raise TooLateForChange
self.patch() # which should patch all refs to 'other'
if s_new_value != s_value:
self.s_value = s_new_value
self.notify_update()
if s_new_value != s_other_value:
other.notify_update()
self.read_locations.update(other.read_locations)
def patch(self):
for listdef in self.itemof:
listdef.listitem = self
def notify_update(self):
'''Reflow from all reading points'''
for position_key in self.read_locations:
self.bookkeeper.annotator.reflowfromposition(position_key)
def generalize(self, s_other_value):
s_new_value = unionof(self.s_value, s_other_value)
updated = s_new_value != self.s_value
if updated:
if self.dont_change_any_more:
raise TooLateForChange
self.s_value = s_new_value
self.notify_update()
return updated
class ListDef(object):
"""A list definition remembers how general the items in that particular
list have to be. Every list creation makes a new ListDef, and the union
of two lists merges the ListItems that each ListDef stores."""
def __init__(self, bookkeeper, s_item=s_ImpossibleValue,
mutated=False, resized=False):
self.listitem = ListItem(bookkeeper, s_item)
self.listitem.mutated = mutated | resized
self.listitem.resized = resized
self.listitem.itemof[self] = True
def read_item(self, position_key):
self.listitem.read_locations[position_key] = True
return self.listitem.s_value
def same_as(self, other):
return self.listitem is other.listitem
def union(self, other):
self.listitem.merge(other.listitem)
return self
def agree(self, bookkeeper, other):
position = bookkeeper.position_key
s_self_value = self.read_item(position)
s_other_value = other.read_item(position)
self.generalize(s_other_value)
other.generalize(s_self_value)
if self.listitem.range_step is not None:
self.generalize_range_step(other.listitem.range_step)
if other.listitem.range_step is not None:
other.generalize_range_step(self.listitem.range_step)
def offspring(self, bookkeeper, *others):
position = bookkeeper.position_key
s_self_value = self.read_item(position)
s_other_values = []
for other in others:
s_other_values.append(other.read_item(position))
s_newlst = bookkeeper.newlist(s_self_value, *s_other_values)
s_newvalue = s_newlst.listdef.read_item(position)
self.generalize(s_newvalue)
for other in others:
other.generalize(s_newvalue)
return s_newlst
def generalize(self, s_value):
self.listitem.generalize(s_value)
def generalize_range_step(self, range_step):
newlistitem = ListItem(self.listitem.bookkeeper, s_ImpossibleValue)
newlistitem.range_step = range_step
self.listitem.merge(newlistitem)
def __repr__(self):
return '<[%r]%s%s%s%s>' % (self.listitem.s_value,
self.listitem.mutated and 'm' or '',
self.listitem.resized and 'r' or '',
self.listitem.immutable and 'I' or '',
self.listitem.must_not_resize and '!R' or '')
def mutate(self):
self.listitem.mutate()
def resize(self):
self.listitem.mutate()
self.listitem.resize()
def never_resize(self):
if self.listitem.resized:
raise ListChangeUnallowed("list already resized")
self.listitem.must_not_resize = True
def mark_as_immutable(self):
# Sets the 'immutable' flag. Note that unlike "never resized",
# the immutable flag is only a hint. It is cleared again e.g.
# when we merge with a "normal" list that doesn't have it. It
# is thus expected to live only shortly, mostly for the case
# of writing 'x.list[n]'.
self.never_resize()
if not self.listitem.mutated:
self.listitem.immutable = True
#else: it's fine, don't set immutable=True at all (see
# test_can_merge_immutable_list_with_regular_list)
s_list_of_strings = SomeList(ListDef(None, SomeString(no_nul=True),
resized = True))
|