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