File: listdef.py

package info (click to toggle)
pypy 5.6.0%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 97,040 kB
  • ctags: 185,069
  • sloc: python: 1,147,862; ansic: 49,642; cpp: 5,245; asm: 5,169; makefile: 529; sh: 481; xml: 232; lisp: 45
file content (207 lines) | stat: -rw-r--r-- 7,961 bytes parent folder | download
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))