proxy.py revision 8459
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        except:
155            return None, False
156        while isproxy(val):
157            val = val.unproxy(obj)
158        for m in self._modifiers:
159            if isinstance(m, str):
160                val = getattr(val, m)
161            elif isinstance(m, int):
162                val = val[m]
163            else:
164                assert("Item must be string or integer")
165            while isproxy(val):
166                val = val.unproxy(obj)
167        return val, True
168
169    def path(self):
170        p = self._attr
171        for m in self._modifiers:
172            if isinstance(m, str):
173                p += '.%s' % m
174            elif isinstance(m, int):
175                p += '[%d]' % m
176            else:
177                assert("Item must be string or integer")
178        return p
179
180class AnyProxy(BaseProxy):
181    def find(self, obj):
182        return obj.find_any(self._pdesc.ptype)
183
184    def path(self):
185        return 'any'
186
187class AllProxy(BaseProxy):
188    def find(self, obj):
189        return obj.find_all(self._pdesc.ptype)
190
191    def path(self):
192        return 'all'
193
194def isproxy(obj):
195    if isinstance(obj, (BaseProxy, params.EthernetAddr)):
196        return True
197    elif isinstance(obj, (list, tuple)):
198        for v in obj:
199            if isproxy(v):
200                return True
201    return False
202
203class ProxyFactory(object):
204    def __init__(self, search_self, search_up):
205        self.search_self = search_self
206        self.search_up = search_up
207
208    def __getattr__(self, attr):
209        if attr == 'any':
210            return AnyProxy(self.search_self, self.search_up)
211        elif attr == 'all':
212            if self.search_up:
213                assert("Parant.all is not supported")
214            return AllProxy(self.search_self, self.search_up)
215        else:
216            return AttrProxy(self.search_self, self.search_up, attr)
217
218# global objects for handling proxies
219Parent = ProxyFactory(search_self = False, search_up = True)
220Self = ProxyFactory(search_self = True, search_up = False)
221
222# limit exports on 'from proxy import *'
223__all__ = ['Parent', 'Self']
224
225# see comment on imports at end of __init__.py.
226import params # for EthernetAddr
227