proxy.py revision 9280:fda147c035b3
1# Copyright (c) 2004-2006 The Regents of The University of Michigan
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met: redistributions of source code must retain the above copyright
7# notice, this list of conditions and the following disclaimer;
8# redistributions in binary form must reproduce the above copyright
9# notice, this list of conditions and the following disclaimer in the
10# documentation and/or other materials provided with the distribution;
11# neither the name of the copyright holders nor the names of its
12# contributors may be used to endorse or promote products derived from
13# this software without specific prior written permission.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
27# Authors: Steve Reinhardt
28#          Nathan Binkert
29
30#####################################################################
31#
32# Proxy object support.
33#
34#####################################################################
35
36import copy
37
38class BaseProxy(object):
39    def __init__(self, search_self, search_up):
40        self._search_self = search_self
41        self._search_up = search_up
42        self._multiplier = None
43
44    def __str__(self):
45        if self._search_self and not self._search_up:
46            s = 'Self'
47        elif not self._search_self and self._search_up:
48            s = 'Parent'
49        else:
50            s = 'ConfusedProxy'
51        return s + '.' + self.path()
52
53    def __setattr__(self, attr, value):
54        if not attr.startswith('_'):
55            raise AttributeError, \
56                  "cannot set attribute '%s' on proxy object" % attr
57        super(BaseProxy, self).__setattr__(attr, value)
58
59    # support multiplying proxies by constants
60    def __mul__(self, other):
61        if not isinstance(other, (int, long, float)):
62            raise TypeError, "Proxy multiplier must be integer"
63        if self._multiplier == None:
64            self._multiplier = other
65        else:
66            # support chained multipliers
67            self._multiplier *= other
68        return self
69
70    __rmul__ = __mul__
71
72    def _mulcheck(self, result):
73        if self._multiplier == None:
74            return result
75        return result * self._multiplier
76
77    def unproxy(self, base):
78        obj = base
79        done = False
80
81        if self._search_self:
82            result, done = self.find(obj)
83
84        if self._search_up:
85            while not done:
86                obj = obj._parent
87                if not obj:
88                    break
89                result, done = self.find(obj)
90
91        if not done:
92            raise AttributeError, \
93                  "Can't resolve proxy '%s' of type '%s' from '%s'" % \
94                  (self.path(), self._pdesc.ptype_str, base.path())
95
96        if isinstance(result, BaseProxy):
97            if result == self:
98                raise RuntimeError, "Cycle in unproxy"
99            result = result.unproxy(obj)
100
101        return self._mulcheck(result)
102
103    def getindex(obj, index):
104        if index == None:
105            return obj
106        try:
107            obj = obj[index]
108        except TypeError:
109            if index != 0:
110                raise
111            # if index is 0 and item is not subscriptable, just
112            # use item itself (so cpu[0] works on uniprocessors)
113        return obj
114    getindex = staticmethod(getindex)
115
116    # This method should be called once the proxy is assigned to a
117    # particular parameter or port to set the expected type of the
118    # resolved proxy
119    def set_param_desc(self, pdesc):
120        self._pdesc = pdesc
121
122class AttrProxy(BaseProxy):
123    def __init__(self, search_self, search_up, attr):
124        super(AttrProxy, self).__init__(search_self, search_up)
125        self._attr = attr
126        self._modifiers = []
127
128    def __getattr__(self, attr):
129        # python uses __bases__ internally for inheritance
130        if attr.startswith('_'):
131            return super(AttrProxy, self).__getattr__(self, attr)
132        if hasattr(self, '_pdesc'):
133            raise AttributeError, "Attribute reference on bound proxy"
134        # Return a copy of self rather than modifying self in place
135        # since self could be an indirect reference via a variable or
136        # parameter
137        new_self = copy.deepcopy(self)
138        new_self._modifiers.append(attr)
139        return new_self
140
141    # support indexing on proxies (e.g., Self.cpu[0])
142    def __getitem__(self, key):
143        if not isinstance(key, int):
144            raise TypeError, "Proxy object requires integer index"
145        if hasattr(self, '_pdesc'):
146            raise AttributeError, "Index operation on bound proxy"
147        new_self = copy.deepcopy(self)
148        new_self._modifiers.append(key)
149        return new_self
150
151    def find(self, obj):
152        try:
153            val = getattr(obj, self._attr)
154            # for any additional unproxying to be done, pass the
155            # current, rather than the original object so that proxy
156            # has the right context
157            obj = val
158        except:
159            return None, False
160        while isproxy(val):
161            val = val.unproxy(obj)
162        for m in self._modifiers:
163            if isinstance(m, str):
164                val = getattr(val, m)
165            elif isinstance(m, int):
166                val = val[m]
167            else:
168                assert("Item must be string or integer")
169            while isproxy(val):
170                val = val.unproxy(obj)
171        return val, True
172
173    def path(self):
174        p = self._attr
175        for m in self._modifiers:
176            if isinstance(m, str):
177                p += '.%s' % m
178            elif isinstance(m, int):
179                p += '[%d]' % m
180            else:
181                assert("Item must be string or integer")
182        return p
183
184class AnyProxy(BaseProxy):
185    def find(self, obj):
186        return obj.find_any(self._pdesc.ptype)
187
188    def path(self):
189        return 'any'
190
191# The AllProxy traverses the entire sub-tree (not only the children)
192# and adds all objects of a specific type
193class AllProxy(BaseProxy):
194    def find(self, obj):
195        return obj.find_all(self._pdesc.ptype)
196
197    def path(self):
198        return 'all'
199
200def isproxy(obj):
201    if isinstance(obj, (BaseProxy, params.EthernetAddr)):
202        return True
203    elif isinstance(obj, (list, tuple)):
204        for v in obj:
205            if isproxy(v):
206                return True
207    return False
208
209class ProxyFactory(object):
210    def __init__(self, search_self, search_up):
211        self.search_self = search_self
212        self.search_up = search_up
213
214    def __getattr__(self, attr):
215        if attr == 'any':
216            return AnyProxy(self.search_self, self.search_up)
217        elif attr == 'all':
218            if self.search_up:
219                assert("Parant.all is not supported")
220            return AllProxy(self.search_self, self.search_up)
221        else:
222            return AttrProxy(self.search_self, self.search_up, attr)
223
224# global objects for handling proxies
225Parent = ProxyFactory(search_self = False, search_up = True)
226Self = ProxyFactory(search_self = True, search_up = False)
227
228# limit exports on 'from proxy import *'
229__all__ = ['Parent', 'Self']
230
231# see comment on imports at end of __init__.py.
232import params # for EthernetAddr
233