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
50import six
51if six.PY3:
52    long = int
53
54import copy
55
56
57class BaseProxy(object):
58    def __init__(self, search_self, search_up):
59        self._search_self = search_self
60        self._search_up = search_up
61        self._multipliers = []
62
63    def __str__(self):
64        if self._search_self and not self._search_up:
65            s = 'Self'
66        elif not self._search_self and self._search_up:
67            s = 'Parent'
68        else:
69            s = 'ConfusedProxy'
70        return s + '.' + self.path()
71
72    def __setattr__(self, attr, value):
73        if not attr.startswith('_'):
74            raise AttributeError(
75                "cannot set attribute '%s' on proxy object" % attr)
76        super(BaseProxy, self).__setattr__(attr, value)
77
78    # support for multiplying proxies by constants or other proxies to
79    # other params
80    def __mul__(self, other):
81        if not (isinstance(other, (int, long, float)) or isproxy(other)):
82            raise TypeError(
83                "Proxy multiplier must be a constant or a proxy to a param")
84        self._multipliers.append(other)
85        return self
86
87    __rmul__ = __mul__
88
89    def _mulcheck(self, result, base):
90        from . import params
91        for multiplier in self._multipliers:
92            if isproxy(multiplier):
93                multiplier = multiplier.unproxy(base)
94                # assert that we are multiplying with a compatible
95                # param
96                if not isinstance(multiplier, params.NumericParamValue):
97                    raise TypeError(
98                        "Proxy multiplier must be a numerical param")
99                multiplier = multiplier.getValue()
100            result = result * multiplier
101        return result
102
103    def unproxy(self, base):
104        obj = base
105        done = False
106
107        if self._search_self:
108            result, done = self.find(obj)
109
110        if self._search_up:
111            # Search up the tree but mark ourself
112            # as visited to avoid a self-reference
113            self._visited = True
114            obj._visited = True
115            while not done:
116                obj = obj._parent
117                if not obj:
118                    break
119                result, done = self.find(obj)
120
121            self._visited = False
122            base._visited = False
123
124        if not done:
125            raise AttributeError(
126                "Can't resolve proxy '%s' of type '%s' from '%s'" % \
127                  (self.path(), self._pdesc.ptype_str, base.path()))
128
129        if isinstance(result, BaseProxy):
130            if result == self:
131                raise RuntimeError("Cycle in unproxy")
132            result = result.unproxy(obj)
133
134        return self._mulcheck(result, base)
135
136    def getindex(obj, index):
137        if index == None:
138            return obj
139        try:
140            obj = obj[index]
141        except TypeError:
142            if index != 0:
143                raise
144            # if index is 0 and item is not subscriptable, just
145            # use item itself (so cpu[0] works on uniprocessors)
146        return obj
147    getindex = staticmethod(getindex)
148
149    # This method should be called once the proxy is assigned to a
150    # particular parameter or port to set the expected type of the
151    # resolved proxy
152    def set_param_desc(self, pdesc):
153        self._pdesc = pdesc
154
155class AttrProxy(BaseProxy):
156    def __init__(self, search_self, search_up, attr):
157        super(AttrProxy, self).__init__(search_self, search_up)
158        self._attr = attr
159        self._modifiers = []
160
161    def __getattr__(self, attr):
162        # python uses __bases__ internally for inheritance
163        if attr.startswith('_'):
164            return super(AttrProxy, self).__getattr__(self, attr)
165        if hasattr(self, '_pdesc'):
166            raise AttributeError("Attribute reference on bound proxy")
167        # Return a copy of self rather than modifying self in place
168        # since self could be an indirect reference via a variable or
169        # parameter
170        new_self = copy.deepcopy(self)
171        new_self._modifiers.append(attr)
172        return new_self
173
174    # support indexing on proxies (e.g., Self.cpu[0])
175    def __getitem__(self, key):
176        if not isinstance(key, int):
177            raise TypeError("Proxy object requires integer index")
178        if hasattr(self, '_pdesc'):
179            raise AttributeError("Index operation on bound proxy")
180        new_self = copy.deepcopy(self)
181        new_self._modifiers.append(key)
182        return new_self
183
184    def find(self, obj):
185        try:
186            val = getattr(obj, self._attr)
187            visited = False
188            if hasattr(val, '_visited'):
189                visited = getattr(val, '_visited')
190
191            if visited:
192                return None, False
193
194            if not isproxy(val):
195                # for any additional unproxying to be done, pass the
196                # current, rather than the original object so that proxy
197                # has the right context
198                obj = val
199
200        except:
201            return None, False
202        while isproxy(val):
203            val = val.unproxy(obj)
204        for m in self._modifiers:
205            if isinstance(m, str):
206                val = getattr(val, m)
207            elif isinstance(m, int):
208                val = val[m]
209            else:
210                assert("Item must be string or integer")
211            while isproxy(val):
212                val = val.unproxy(obj)
213        return val, True
214
215    def path(self):
216        p = self._attr
217        for m in self._modifiers:
218            if isinstance(m, str):
219                p += '.%s' % m
220            elif isinstance(m, int):
221                p += '[%d]' % m
222            else:
223                assert("Item must be string or integer")
224        return p
225
226class AnyProxy(BaseProxy):
227    def find(self, obj):
228        return obj.find_any(self._pdesc.ptype)
229
230    def path(self):
231        return 'any'
232
233# The AllProxy traverses the entire sub-tree (not only the children)
234# and adds all objects of a specific type
235class AllProxy(BaseProxy):
236    def find(self, obj):
237        return obj.find_all(self._pdesc.ptype)
238
239    def path(self):
240        return 'all'
241
242def isproxy(obj):
243    from . import params
244    if isinstance(obj, (BaseProxy, params.EthernetAddr)):
245        return True
246    elif isinstance(obj, (list, tuple)):
247        for v in obj:
248            if isproxy(v):
249                return True
250    return False
251
252class ProxyFactory(object):
253    def __init__(self, search_self, search_up):
254        self.search_self = search_self
255        self.search_up = search_up
256
257    def __getattr__(self, attr):
258        if attr == 'any':
259            return AnyProxy(self.search_self, self.search_up)
260        elif attr == 'all':
261            if self.search_up:
262                assert("Parant.all is not supported")
263            return AllProxy(self.search_self, self.search_up)
264        else:
265            return AttrProxy(self.search_self, self.search_up, attr)
266
267# global objects for handling proxies
268Parent = ProxyFactory(search_self = False, search_up = True)
269Self = ProxyFactory(search_self = True, search_up = False)
270
271# limit exports on 'from proxy import *'
272__all__ = ['Parent', 'Self']
273