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