python: Fix AddrRange legacy ParamValue wrapper
[gem5.git] / src / python / m5 / SimObject.py
index 97cf6d09688939fe6eb9689b9e7659b405d178fc..dca7d4095d34e008707f969dcd07aca4f767fe8b 100644 (file)
 #          Andreas Sandberg
 
 from __future__ import print_function
+from __future__ import absolute_import
+import six
+if six.PY3:
+    long = int
 
 import sys
 from types import FunctionType, MethodType, ModuleType
@@ -168,10 +172,6 @@ def createCxxConfigDirectoryEntryFile(code, name, simobj, is_header):
         code('#include "base/str.hh"')
         code('#include "cxx_config/${name}.hh"')
 
-        if simobj._ports.values() != []:
-            code('#include "mem/mem_object.hh"')
-            code('#include "mem/port.hh"')
-
         code()
         code('${member_prefix}DirectoryEntry::DirectoryEntry()');
         code('{')
@@ -411,6 +411,7 @@ class MetaSimObject(type):
         'cxx_extra_bases' : list,
         'cxx_exports' : list,
         'cxx_param_exports' : list,
+        'cxx_template_params' : list,
     }
     # Attributes that can be set any time
     keywords = { 'check' : FunctionType }
@@ -450,6 +451,8 @@ class MetaSimObject(type):
             value_dict['cxx_exports'] += cxx_exports
         if 'cxx_param_exports' not in value_dict:
             value_dict['cxx_param_exports'] = []
+        if 'cxx_template_params' not in value_dict:
+            value_dict['cxx_template_params'] = []
         cls_dict['_value_dict'] = value_dict
         cls = super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
         if 'type' in value_dict:
@@ -534,7 +537,7 @@ class MetaSimObject(type):
                 cls._new_port(key, val)
 
             # init-time-only keywords
-            elif cls.init_keywords.has_key(key):
+            elif key in cls.init_keywords:
                 cls._set_keyword(key, val, cls.init_keywords[key])
 
             # default: use normal path (ends up in __setattr__)
@@ -613,11 +616,11 @@ class MetaSimObject(type):
             type.__setattr__(cls, attr, value)
             return
 
-        if cls.keywords.has_key(attr):
+        if attr in cls.keywords:
             cls._set_keyword(attr, value, cls.keywords[attr])
             return
 
-        if cls._ports.has_key(attr):
+        if attr in cls._ports:
             cls._cls_get_port_ref(attr).connect(value)
             return
 
@@ -652,10 +655,10 @@ class MetaSimObject(type):
         if attr == 'cxx_namespaces':
             return cls.cxx_class_path[:-1]
 
-        if cls._values.has_key(attr):
+        if attr in cls._values:
             return cls._values[attr]
 
-        if cls._children.has_key(attr):
+        if attr in cls._children:
             return cls._children[attr]
 
         raise AttributeError(
@@ -681,7 +684,7 @@ class MetaSimObject(type):
         # the object itself, not including inherited params (which
         # will also be inherited from the base class's param struct
         # here). Sort the params based on their key
-        params = map(lambda (k, v): v, sorted(cls._params.local.items()))
+        params = map(lambda k_v: k_v[1], sorted(cls._params.local.items()))
         ports = cls._ports.local
 
         code('''#include "pybind11/pybind11.h"
@@ -726,7 +729,7 @@ module_init(py::module &m_internal)
             for k, v in sorted(cls._params.local.items())
         ] + [
             PyBindProperty("port_%s_connection_count" % port.name)
-            for port in ports.itervalues()
+            for port in ports.values()
         ]
         for exp in param_exports:
             exp.export(code, "%sParams" % cls)
@@ -769,6 +772,7 @@ module_init(py::module &m_internal)
         code('static EmbeddedPyBind embed_obj("${0}", module_init, "${1}");',
              cls, cls._base.type if cls._base else "")
 
+    _warned_about_nested_templates = False
 
     # Generate the C++ declaration (.hh file) for this SimObject's
     # param struct.  Called from src/SConscript.
@@ -777,7 +781,7 @@ module_init(py::module &m_internal)
         # the object itself, not including inherited params (which
         # will also be inherited from the base class's param struct
         # here). Sort the params based on their key
-        params = map(lambda (k, v): v, sorted(cls._params.local.items()))
+        params = map(lambda k_v: k_v[1], sorted(cls._params.local.items()))
         ports = cls._ports.local
         try:
             ptypes = [p.ptype for p in params]
@@ -786,7 +790,78 @@ module_init(py::module &m_internal)
             print(params)
             raise
 
-        class_path = cls._value_dict['cxx_class'].split('::')
+        class CxxClass(object):
+            def __init__(self, sig, template_params=[]):
+                # Split the signature into its constituent parts. This could
+                # potentially be done with regular expressions, but
+                # it's simple enough to pick appart a class signature
+                # manually.
+                parts = sig.split('<', 1)
+                base = parts[0]
+                t_args = []
+                if len(parts) > 1:
+                    # The signature had template arguments.
+                    text = parts[1].rstrip(' \t\n>')
+                    arg = ''
+                    # Keep track of nesting to avoid splitting on ","s embedded
+                    # in the arguments themselves.
+                    depth = 0
+                    for c in text:
+                        if c == '<':
+                            depth = depth + 1
+                            if depth > 0 and not \
+                                    self._warned_about_nested_templates:
+                                self._warned_about_nested_templates = True
+                                print('Nested template argument in cxx_class.'
+                                      ' This feature is largely untested and '
+                                      ' may not work.')
+                        elif c == '>':
+                            depth = depth - 1
+                        elif c == ',' and depth == 0:
+                            t_args.append(arg.strip())
+                            arg = ''
+                        else:
+                            arg = arg + c
+                    if arg:
+                        t_args.append(arg.strip())
+                # Split the non-template part on :: boundaries.
+                class_path = base.split('::')
+
+                # The namespaces are everything except the last part of the
+                # class path.
+                self.namespaces = class_path[:-1]
+                # And the class name is the last part.
+                self.name = class_path[-1]
+
+                self.template_params = template_params
+                self.template_arguments = []
+                # Iterate through the template arguments and their values. This
+                # will likely break if parameter packs are used.
+                for arg, param in zip(t_args, template_params):
+                    type_keys = ('class', 'typename')
+                    # If a parameter is a type, parse it recursively. Otherwise
+                    # assume it's a constant, and store it verbatim.
+                    if any(param.strip().startswith(kw) for kw in type_keys):
+                        self.template_arguments.append(CxxClass(arg))
+                    else:
+                        self.template_arguments.append(arg)
+
+            def declare(self, code):
+                # First declare any template argument types.
+                for arg in self.template_arguments:
+                    if isinstance(arg, CxxClass):
+                        arg.declare(code)
+                # Re-open the target namespace.
+                for ns in self.namespaces:
+                    code('namespace $ns {')
+                # If this is a class template...
+                if self.template_params:
+                    code('template <${{", ".join(self.template_params)}}>')
+                # The actual class declaration.
+                code('class ${{self.name}};')
+                # Close the target namespaces.
+                for ns in reversed(self.namespaces):
+                    code('} // namespace $ns')
 
         code('''\
 #ifndef __PARAMS__${cls}__
@@ -802,18 +877,16 @@ module_init(py::module &m_internal)
         if cls == SimObject:
             code('''#include <string>''')
 
+        cxx_class = CxxClass(cls._value_dict['cxx_class'],
+                             cls._value_dict['cxx_template_params'])
+
         # A forward class declaration is sufficient since we are just
         # declaring a pointer.
-        for ns in class_path[:-1]:
-            code('namespace $ns {')
-        code('class $0;', class_path[-1])
-        for ns in reversed(class_path[:-1]):
-            code('} // namespace $ns')
-        code()
+        cxx_class.declare(code)
 
         for param in params:
             param.cxx_predecls(code)
-        for port in ports.itervalues():
+        for port in ports.values():
             port.cxx_predecls(code)
         code()
 
@@ -846,7 +919,7 @@ module_init(py::module &m_internal)
 
         for param in params:
             param.cxx_decl(code)
-        for port in ports.itervalues():
+        for port in ports.values():
             port.cxx_decl(code)
 
         code.dedent()
@@ -875,9 +948,10 @@ def cxxMethod(*args, **kwargs):
     """Decorator to export C++ functions to Python"""
 
     def decorate(func):
-        name = func.func_name
+        name = func.__name__
         override = kwargs.get("override", False)
         cxx_name = kwargs.get("cxx_name", name)
+        return_value_policy = kwargs.get("return_value_policy", None)
 
         args, varargs, keywords, defaults = inspect.getargspec(func)
         if varargs or keywords:
@@ -886,7 +960,8 @@ def cxxMethod(*args, **kwargs):
 
         # Create tuples of (argument, default)
         if defaults:
-            args = args[:-len(defaults)] + zip(args[-len(defaults):], defaults)
+            args = args[:-len(defaults)] + \
+                   list(zip(args[-len(defaults):], defaults))
         # Don't include self in the argument list to PyBind
         args = args[1:]
 
@@ -901,7 +976,8 @@ def cxxMethod(*args, **kwargs):
             return func(self, *args, **kwargs)
 
         f = py_call if override else cxx_call
-        f.__pybind = PyBindMethod(name, cxx_name=cxx_name, args=args)
+        f.__pybind = PyBindMethod(name, cxx_name=cxx_name, args=args,
+                                  return_value_policy=return_value_policy)
 
         return f
 
@@ -1118,7 +1194,7 @@ class SimObject(object):
         # Do children before parameter values so that children that
         # are also param values get cloned properly.
         self._children = {}
-        for key,val in ancestor._children.iteritems():
+        for key,val in ancestor._children.items():
             self.add_child(key, val(_memo=memo_dict))
 
         # Inherit parameter values from class using multidict so
@@ -1127,7 +1203,7 @@ class SimObject(object):
         self._values = multidict(ancestor._values)
         self._hr_values = multidict(ancestor._hr_values)
         # clone SimObject-valued parameters
-        for key,val in ancestor._values.iteritems():
+        for key,val in ancestor._values.items():
             val = tryAsSimObjectOrVector(val)
             if val is not None:
                 self._values[key] = val(_memo=memo_dict)
@@ -1135,10 +1211,10 @@ class SimObject(object):
         # clone port references.  no need to use a multidict here
         # since we will be creating new references for all ports.
         self._port_refs = {}
-        for key,val in ancestor._port_refs.iteritems():
+        for key,val in ancestor._port_refs.items():
             self._port_refs[key] = val.clone(self, memo_dict)
         # apply attribute assignments from keyword args, if any
-        for key,val in kwargs.iteritems():
+        for key,val in kwargs.items():
             setattr(self, key, val)
 
     # "Clone" the current instance by creating another instance of
@@ -1158,7 +1234,7 @@ class SimObject(object):
             # create a new dict and use that.
             memo_dict = {}
             kwargs['_memo'] = memo_dict
-        elif memo_dict.has_key(self):
+        elif self in memo_dict:
             # clone already done & memoized
             return memo_dict[self]
         return self.__class__(_ancestor = self, **kwargs)
@@ -1174,13 +1250,13 @@ class SimObject(object):
         return ref
 
     def __getattr__(self, attr):
-        if self._ports.has_key(attr):
+        if attr in self._ports:
             return self._get_port_ref(attr)
 
-        if self._values.has_key(attr):
+        if attr in self._values:
             return self._values[attr]
 
-        if self._children.has_key(attr):
+        if attr in self._children:
             return self._children[attr]
 
         # If the attribute exists on the C++ object, transparently
@@ -1206,7 +1282,7 @@ class SimObject(object):
             object.__setattr__(self, attr, value)
             return
 
-        if self._ports.has_key(attr):
+        if attr in self._ports:
             # set up port connection
             self._get_port_ref(attr).connect(value)
             return
@@ -1294,7 +1370,7 @@ class SimObject(object):
         if child.has_parent():
             warn("add_child('%s'): child '%s' already has parent", name,
                 child.get_name())
-        if self._children.has_key(name):
+        if name in self._children:
             # This code path had an undiscovered bug that would make it fail
             # at runtime. It had been here for a long time and was only
             # exposed by a buggy script. Changes here will probably not be
@@ -1310,7 +1386,7 @@ class SimObject(object):
     # that when we instantiate all the parameter objects we're still
     # inside the configuration hierarchy.
     def adoptOrphanParams(self):
-        for key,val in self._values.iteritems():
+        for key,val in self._values.items():
             if not isSimObjectVector(val) and isSimObjectSequence(val):
                 # need to convert raw SimObject sequences to
                 # SimObjectVector class so we can call has_parent()
@@ -1345,7 +1421,7 @@ class SimObject(object):
             return self, True
 
         found_obj = None
-        for child in self._children.itervalues():
+        for child in self._children.values():
             visited = False
             if hasattr(child, '_visited'):
               visited = getattr(child, '_visited')
@@ -1357,7 +1433,7 @@ class SimObject(object):
                           (found_obj.path, child.path))
                 found_obj = child
         # search param space
-        for pname,pdesc in self._params.iteritems():
+        for pname,pdesc in self._params.items():
             if issubclass(pdesc.ptype, ptype):
                 match_obj = self._values[pname]
                 if found_obj != None and found_obj != match_obj:
@@ -1370,7 +1446,7 @@ class SimObject(object):
     def find_all(self, ptype):
         all = {}
         # search children
-        for child in self._children.itervalues():
+        for child in self._children.values():
             # a child could be a list, so ensure we visit each item
             if isinstance(child, list):
                 children = child
@@ -1386,7 +1462,7 @@ class SimObject(object):
                     child_all, done = child.find_all(ptype)
                     all.update(dict(zip(child_all, [done] * len(child_all))))
         # search param space
-        for pname,pdesc in self._params.iteritems():
+        for pname,pdesc in self._params.items():
             if issubclass(pdesc.ptype, ptype):
                 match_obj = self._values[pname]
                 if not isproxy(match_obj) and not isNullPointer(match_obj):
@@ -1399,7 +1475,7 @@ class SimObject(object):
         return self
 
     def unproxyParams(self):
-        for param in self._params.iterkeys():
+        for param in self._params.keys():
             value = self._values.get(param)
             if value != None and isproxy(value):
                 try:
@@ -1412,7 +1488,7 @@ class SimObject(object):
 
         # Unproxy ports in sorted order so that 'append' operations on
         # vector ports are done in a deterministic fashion.
-        port_names = self._ports.keys()
+        port_names = list(self._ports.keys())
         port_names.sort()
         for port_name in port_names:
             port = self._port_refs.get(port_name)
@@ -1489,7 +1565,7 @@ class SimObject(object):
         cc_params = cc_params_struct()
         cc_params.name = str(self)
 
-        param_names = self._params.keys()
+        param_names = list(self._params.keys())
         param_names.sort()
         for param in param_names:
             value = self._values.get(param)
@@ -1513,7 +1589,7 @@ class SimObject(object):
             else:
                 setattr(cc_params, param, value)
 
-        port_names = self._ports.keys()
+        port_names = list(self._ports.keys())
         port_names.sort()
         for port_name in port_names:
             port = self._port_refs.get(port_name, None)
@@ -1550,7 +1626,7 @@ class SimObject(object):
         # The order of the dict is implementation dependent, so sort
         # it based on the key (name) to ensure the order is the same
         # on all hosts
-        for (name, child) in sorted(self._children.iteritems()):
+        for (name, child) in sorted(self._children.items()):
             for obj in child.descendants():
                 yield obj
 
@@ -1562,12 +1638,16 @@ class SimObject(object):
     def getValue(self):
         return self.getCCObject()
 
+    @cxxMethod(return_value_policy="reference")
+    def getPort(self, if_name, idx):
+        pass
+
     # Create C++ port connections corresponding to the connections in
     # _port_refs
     def connectPorts(self):
         # Sort the ports based on their attribute name to ensure the
         # order is the same on all hosts
-        for (attr, portRef) in sorted(self._port_refs.iteritems()):
+        for (attr, portRef) in sorted(self._port_refs.items()):
             portRef.ccConnect()
 
     # Default function for generating the device structure.
@@ -1577,7 +1657,7 @@ class SimObject(object):
         yield  # make this function a (null) generator
 
     def recurseDeviceTree(self, state):
-        for child in self._children.itervalues():
+        for child in self._children.values():
             for item in child: # For looping over SimObjectVectors
                 for dt in item.generateDeviceTree(state):
                     yield dt