proxy.py revision 13742
110816SN/A# Copyright (c) 2018 ARM Limited
29288SN/A# All rights reserved.
39288SN/A#
49288SN/A# The license below extends only to copyright in the software and shall
59288SN/A# not be construed as granting a license to any other intellectual
69288SN/A# property including but not limited to intellectual property relating
79288SN/A# to a hardware implementation of the functionality of the software
89288SN/A# licensed hereunder.  You may use the software subject to the license
99288SN/A# terms below provided that you ensure that this notice is replicated
109288SN/A# unmodified and in its entirety in all distributions of the software,
119288SN/A# modified or unmodified, in source code or in binary form.
129288SN/A#
134486SN/A# Copyright (c) 2004-2006 The Regents of The University of Michigan
144486SN/A# All rights reserved.
154486SN/A#
164486SN/A# Redistribution and use in source and binary forms, with or without
174486SN/A# modification, are permitted provided that the following conditions are
184486SN/A# met: redistributions of source code must retain the above copyright
194486SN/A# notice, this list of conditions and the following disclaimer;
204486SN/A# redistributions in binary form must reproduce the above copyright
214486SN/A# notice, this list of conditions and the following disclaimer in the
224486SN/A# documentation and/or other materials provided with the distribution;
234486SN/A# neither the name of the copyright holders nor the names of its
244486SN/A# contributors may be used to endorse or promote products derived from
254486SN/A# this software without specific prior written permission.
264486SN/A#
274486SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
284486SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
294486SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
304486SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
314486SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
324486SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
334486SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
344486SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
354486SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
364486SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
374486SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
384486SN/A#
394486SN/A# Authors: Steve Reinhardt
4011053Sandreas.hansson@arm.com#          Nathan Binkert
414486SN/A
423102SN/A#####################################################################
438833SN/A#
442826SN/A# Proxy object support.
458831SN/A#
469796SN/A#####################################################################
471615SN/A
482826SN/Afrom __future__ import print_function
491366SN/Afrom __future__ import absolute_import
5011053Sandreas.hansson@arm.comimport six
519338SN/Aif six.PY3:
5210816SN/A    long = int
5310816SN/A
5410816SN/Aimport copy
5510816SN/A
5611722Ssophiane.senni@gmail.com
5711722Ssophiane.senni@gmail.comclass BaseProxy(object):
5810816SN/A    def __init__(self, search_self, search_up):
5910816SN/A        self._search_self = search_self
6012513Sodanrc@yahoo.com.br        self._search_up = search_up
6112513Sodanrc@yahoo.com.br        self._multipliers = []
6212513Sodanrc@yahoo.com.br
631310SN/A    def __str__(self):
6410816SN/A        if self._search_self and not self._search_up:
6510816SN/A            s = 'Self'
6610816SN/A        elif not self._search_self and self._search_up:
6710816SN/A            s = 'Parent'
6810816SN/A        else:
6910816SN/A            s = 'ConfusedProxy'
7010816SN/A        return s + '.' + self.path()
7110884SN/A
7210816SN/A    def __setattr__(self, attr, value):
7310816SN/A        if not attr.startswith('_'):
745875SN/A            raise AttributeError(
7510816SN/A                "cannot set attribute '%s' on proxy object" % attr)
7610816SN/A        super(BaseProxy, self).__setattr__(attr, value)
7710816SN/A
7810025SN/A    # support for multiplying proxies by constants or other proxies to
7910025SN/A    # other params
8010816SN/A    def __mul__(self, other):
8110816SN/A        if not (isinstance(other, (int, long, float)) or isproxy(other)):
8210816SN/A            raise TypeError(
8310816SN/A                "Proxy multiplier must be a constant or a proxy to a param")
8410816SN/A        self._multipliers.append(other)
8510816SN/A        return self
8610816SN/A
8710816SN/A    __rmul__ = __mul__
8811053Sandreas.hansson@arm.com
8911197Sandreas.hansson@arm.com    def _mulcheck(self, result, base):
9011197Sandreas.hansson@arm.com        for multiplier in self._multipliers:
9111197Sandreas.hansson@arm.com            if isproxy(multiplier):
9211197Sandreas.hansson@arm.com                multiplier = multiplier.unproxy(base)
9311053Sandreas.hansson@arm.com                # assert that we are multiplying with a compatible
9411053Sandreas.hansson@arm.com                # param
9511053Sandreas.hansson@arm.com                if not isinstance(multiplier, params.NumericParamValue):
9611197Sandreas.hansson@arm.com                    raise TypeError(
9711197Sandreas.hansson@arm.com                        "Proxy multiplier must be a numerical param")
9811197Sandreas.hansson@arm.com                multiplier = multiplier.getValue()
9911197Sandreas.hansson@arm.com            result *= multiplier
10011197Sandreas.hansson@arm.com        return result
10111197Sandreas.hansson@arm.com
10211197Sandreas.hansson@arm.com    def unproxy(self, base):
10311197Sandreas.hansson@arm.com        obj = base
10411197Sandreas.hansson@arm.com        done = False
10511197Sandreas.hansson@arm.com
10611197Sandreas.hansson@arm.com        if self._search_self:
10711197Sandreas.hansson@arm.com            result, done = self.find(obj)
10811199Sandreas.hansson@arm.com
10911199Sandreas.hansson@arm.com        if self._search_up:
11011199Sandreas.hansson@arm.com            # Search up the tree but mark ourself
11111199Sandreas.hansson@arm.com            # as visited to avoid a self-reference
11211199Sandreas.hansson@arm.com            self._visited = True
11311199Sandreas.hansson@arm.com            obj._visited = True
11411199Sandreas.hansson@arm.com            while not done:
11511199Sandreas.hansson@arm.com                obj = obj._parent
116                if not obj:
117                    break
118                result, done = self.find(obj)
119
120            self._visited = False
121            base._visited = False
122
123        if not done:
124            raise AttributeError(
125                "Can't resolve proxy '%s' of type '%s' from '%s'" % \
126                  (self.path(), self._pdesc.ptype_str, base.path()))
127
128        if isinstance(result, BaseProxy):
129            if result == self:
130                raise RuntimeError("Cycle in unproxy")
131            result = result.unproxy(obj)
132
133        return self._mulcheck(result, base)
134
135    def getindex(obj, index):
136        if index == None:
137            return obj
138        try:
139            obj = obj[index]
140        except TypeError:
141            if index != 0:
142                raise
143            # if index is 0 and item is not subscriptable, just
144            # use item itself (so cpu[0] works on uniprocessors)
145        return obj
146    getindex = staticmethod(getindex)
147
148    # This method should be called once the proxy is assigned to a
149    # particular parameter or port to set the expected type of the
150    # resolved proxy
151    def set_param_desc(self, pdesc):
152        self._pdesc = pdesc
153
154class AttrProxy(BaseProxy):
155    def __init__(self, search_self, search_up, attr):
156        super(AttrProxy, self).__init__(search_self, search_up)
157        self._attr = attr
158        self._modifiers = []
159
160    def __getattr__(self, attr):
161        # python uses __bases__ internally for inheritance
162        if attr.startswith('_'):
163            return super(AttrProxy, self).__getattr__(self, attr)
164        if hasattr(self, '_pdesc'):
165            raise AttributeError("Attribute reference on bound proxy")
166        # Return a copy of self rather than modifying self in place
167        # since self could be an indirect reference via a variable or
168        # parameter
169        new_self = copy.deepcopy(self)
170        new_self._modifiers.append(attr)
171        return new_self
172
173    # support indexing on proxies (e.g., Self.cpu[0])
174    def __getitem__(self, key):
175        if not isinstance(key, int):
176            raise TypeError("Proxy object requires integer index")
177        if hasattr(self, '_pdesc'):
178            raise AttributeError("Index operation on bound proxy")
179        new_self = copy.deepcopy(self)
180        new_self._modifiers.append(key)
181        return new_self
182
183    def find(self, obj):
184        try:
185            val = getattr(obj, self._attr)
186            visited = False
187            if hasattr(val, '_visited'):
188                visited = getattr(val, '_visited')
189
190            if visited:
191                return None, False
192
193            if not isproxy(val):
194                # for any additional unproxying to be done, pass the
195                # current, rather than the original object so that proxy
196                # has the right context
197                obj = val
198
199        except:
200            return None, False
201        while isproxy(val):
202            val = val.unproxy(obj)
203        for m in self._modifiers:
204            if isinstance(m, str):
205                val = getattr(val, m)
206            elif isinstance(m, int):
207                val = val[m]
208            else:
209                assert("Item must be string or integer")
210            while isproxy(val):
211                val = val.unproxy(obj)
212        return val, True
213
214    def path(self):
215        p = self._attr
216        for m in self._modifiers:
217            if isinstance(m, str):
218                p += '.%s' % m
219            elif isinstance(m, int):
220                p += '[%d]' % m
221            else:
222                assert("Item must be string or integer")
223        return p
224
225class AnyProxy(BaseProxy):
226    def find(self, obj):
227        return obj.find_any(self._pdesc.ptype)
228
229    def path(self):
230        return 'any'
231
232# The AllProxy traverses the entire sub-tree (not only the children)
233# and adds all objects of a specific type
234class AllProxy(BaseProxy):
235    def find(self, obj):
236        return obj.find_all(self._pdesc.ptype)
237
238    def path(self):
239        return 'all'
240
241def isproxy(obj):
242    from . import params
243    if isinstance(obj, (BaseProxy, params.EthernetAddr)):
244        return True
245    elif isinstance(obj, (list, tuple)):
246        for v in obj:
247            if isproxy(v):
248                return True
249    return False
250
251class ProxyFactory(object):
252    def __init__(self, search_self, search_up):
253        self.search_self = search_self
254        self.search_up = search_up
255
256    def __getattr__(self, attr):
257        if attr == 'any':
258            return AnyProxy(self.search_self, self.search_up)
259        elif attr == 'all':
260            if self.search_up:
261                assert("Parant.all is not supported")
262            return AllProxy(self.search_self, self.search_up)
263        else:
264            return AttrProxy(self.search_self, self.search_up, attr)
265
266# global objects for handling proxies
267Parent = ProxyFactory(search_self = False, search_up = True)
268Self = ProxyFactory(search_self = True, search_up = False)
269
270# limit exports on 'from proxy import *'
271__all__ = ['Parent', 'Self']
272