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