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