| # Copyright (c) 2018 ARM Limited |
| # All rights reserved. |
| # |
| # The license below extends only to copyright in the software and shall |
| # not be construed as granting a license to any other intellectual |
| # property including but not limited to intellectual property relating |
| # to a hardware implementation of the functionality of the software |
| # licensed hereunder. You may use the software subject to the license |
| # terms below provided that you ensure that this notice is replicated |
| # unmodified and in its entirety in all distributions of the software, |
| # modified or unmodified, in source code or in binary form. |
| # |
| # Copyright (c) 2004-2006 The Regents of The University of Michigan |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer; |
| # redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution; |
| # neither the name of the copyright holders nor the names of its |
| # contributors may be used to endorse or promote products derived from |
| # this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| ##################################################################### |
| # |
| # Proxy object support. |
| # |
| ##################################################################### |
| |
| import copy |
| |
| |
| class BaseProxy(object): |
| def __init__(self, search_self, search_up): |
| self._search_self = search_self |
| self._search_up = search_up |
| self._ops = [] |
| |
| def __str__(self): |
| if self._search_self and not self._search_up: |
| s = "Self" |
| elif not self._search_self and self._search_up: |
| s = "Parent" |
| else: |
| s = "ConfusedProxy" |
| return s + "." + self.path() |
| |
| def __setattr__(self, attr, value): |
| if not attr.startswith("_"): |
| raise AttributeError( |
| "cannot set attribute '%s' on proxy object" % attr |
| ) |
| super().__setattr__(attr, value) |
| |
| def _gen_op(operation): |
| def op(self, operand): |
| if not (isinstance(operand, (int, float)) or isproxy(operand)): |
| raise TypeError( |
| "Proxy operand must be a constant or a proxy to a param" |
| ) |
| self._ops.append((operation, operand)) |
| return self |
| |
| return op |
| |
| # Support for multiplying proxies by either constants or other proxies |
| __mul__ = _gen_op(lambda operand_a, operand_b: operand_a * operand_b) |
| __rmul__ = __mul__ |
| |
| # Support for dividing proxies by either constants or other proxies |
| __truediv__ = _gen_op(lambda operand_a, operand_b: operand_a / operand_b) |
| __floordiv__ = _gen_op(lambda operand_a, operand_b: operand_a // operand_b) |
| |
| # Support for dividing constants by proxies |
| __rtruediv__ = _gen_op( |
| lambda operand_a, operand_b: operand_b / operand_a.getValue() |
| ) |
| __rfloordiv__ = _gen_op( |
| lambda operand_a, operand_b: operand_b // operand_a.getValue() |
| ) |
| |
| # After all the operators and operands have been defined, this function |
| # should be called to perform the actual operation |
| def _opcheck(self, result, base): |
| from . import params |
| |
| for operation, operand in self._ops: |
| # Get the operand's value |
| if isproxy(operand): |
| operand = operand.unproxy(base) |
| # assert that we are operating with a compatible param |
| if not isinstance(operand, params.NumericParamValue): |
| raise TypeError("Proxy operand must be a numerical param") |
| operand = operand.getValue() |
| |
| # Apply the operation |
| result = operation(result, operand) |
| |
| return result |
| |
| def unproxy(self, base): |
| obj = base |
| done = False |
| |
| if self._search_self: |
| result, done = self.find(obj) |
| |
| if self._search_up: |
| # Search up the tree but mark ourself |
| # as visited to avoid a self-reference |
| self._visited = True |
| obj._visited = True |
| while not done: |
| obj = obj._parent |
| if not obj: |
| break |
| result, done = self.find(obj) |
| |
| self._visited = False |
| base._visited = False |
| |
| if not done: |
| raise AttributeError( |
| "Can't resolve proxy '%s' of type '%s' from '%s'" |
| % (self.path(), self._pdesc.ptype_str, base.path()) |
| ) |
| |
| if isinstance(result, BaseProxy): |
| if result == self: |
| raise RuntimeError("Cycle in unproxy") |
| result = result.unproxy(obj) |
| |
| return self._opcheck(result, base) |
| |
| def getindex(obj, index): |
| if index == None: |
| return obj |
| try: |
| obj = obj[index] |
| except TypeError: |
| if index != 0: |
| raise |
| # if index is 0 and item is not subscriptable, just |
| # use item itself (so cpu[0] works on uniprocessors) |
| return obj |
| |
| getindex = staticmethod(getindex) |
| |
| # This method should be called once the proxy is assigned to a |
| # particular parameter or port to set the expected type of the |
| # resolved proxy |
| def set_param_desc(self, pdesc): |
| self._pdesc = pdesc |
| |
| |
| class AttrProxy(BaseProxy): |
| def __init__(self, search_self, search_up, attr): |
| super().__init__(search_self, search_up) |
| self._attr = attr |
| self._modifiers = [] |
| |
| def __getattr__(self, attr): |
| # python uses __bases__ internally for inheritance |
| if attr.startswith("_"): |
| return super().__getattr__(self, attr) |
| if hasattr(self, "_pdesc"): |
| raise AttributeError( |
| "Attribute reference on bound proxy " f"({self}.{attr})" |
| ) |
| # Return a copy of self rather than modifying self in place |
| # since self could be an indirect reference via a variable or |
| # parameter |
| new_self = copy.deepcopy(self) |
| new_self._modifiers.append(attr) |
| return new_self |
| |
| # support indexing on proxies (e.g., Self.cpu[0]) |
| def __getitem__(self, key): |
| if not isinstance(key, int): |
| raise TypeError("Proxy object requires integer index") |
| if hasattr(self, "_pdesc"): |
| raise AttributeError("Index operation on bound proxy") |
| new_self = copy.deepcopy(self) |
| new_self._modifiers.append(key) |
| return new_self |
| |
| def find(self, obj): |
| try: |
| val = getattr(obj, self._attr) |
| visited = False |
| if hasattr(val, "_visited"): |
| visited = getattr(val, "_visited") |
| |
| if visited: |
| return None, False |
| |
| if not isproxy(val): |
| # for any additional unproxying to be done, pass the |
| # current, rather than the original object so that proxy |
| # has the right context |
| obj = val |
| |
| except: |
| return None, False |
| while isproxy(val): |
| val = val.unproxy(obj) |
| for m in self._modifiers: |
| if isinstance(m, str): |
| val = getattr(val, m) |
| elif isinstance(m, int): |
| val = val[m] |
| else: |
| assert "Item must be string or integer" |
| while isproxy(val): |
| val = val.unproxy(obj) |
| return val, True |
| |
| def path(self): |
| p = self._attr |
| for m in self._modifiers: |
| if isinstance(m, str): |
| p += ".%s" % m |
| elif isinstance(m, int): |
| p += "[%d]" % m |
| else: |
| assert "Item must be string or integer" |
| return p |
| |
| |
| class AnyProxy(BaseProxy): |
| def find(self, obj): |
| return obj.find_any(self._pdesc.ptype) |
| |
| def path(self): |
| return "any" |
| |
| |
| # The AllProxy traverses the entire sub-tree (not only the children) |
| # and adds all objects of a specific type |
| class AllProxy(BaseProxy): |
| def find(self, obj): |
| return obj.find_all(self._pdesc.ptype) |
| |
| def path(self): |
| return "all" |
| |
| |
| def isproxy(obj): |
| from . import params |
| |
| if isinstance(obj, (BaseProxy, params.EthernetAddr)): |
| return True |
| elif isinstance(obj, (list, tuple)): |
| for v in obj: |
| if isproxy(v): |
| return True |
| return False |
| |
| |
| class ProxyFactory(object): |
| def __init__(self, search_self, search_up): |
| self.search_self = search_self |
| self.search_up = search_up |
| |
| def __getattr__(self, attr): |
| if attr == "any": |
| return AnyProxy(self.search_self, self.search_up) |
| elif attr == "all": |
| if self.search_up: |
| assert "Parant.all is not supported" |
| return AllProxy(self.search_self, self.search_up) |
| else: |
| return AttrProxy(self.search_self, self.search_up, attr) |
| |
| |
| # global objects for handling proxies |
| Parent = ProxyFactory(search_self=False, search_up=True) |
| Self = ProxyFactory(search_self=True, search_up=False) |
| |
| # limit exports on 'from proxy import *' |
| __all__ = ["Parent", "Self"] |