Only allow SimObject classes to be instantiated (no cloning!).
Provide a makeClass() method to generate a new class using a SimObject instance as a template. All instantiation, subclassing, and class generation is done recursively using "deep copy"-style memoization to maintain object relationships in the face of multiple references to shared objects/classes. src/python/m5/multidict.py: Rename local dict attribute from 'dict' to 'local' for clarity. --HG-- extra : convert_revision : 73ed6836216308709d7bb68d09f8131acd5f1822
This commit is contained in:
parent
cd65504739
commit
39f85a1de4
2 changed files with 163 additions and 57 deletions
|
@ -27,11 +27,10 @@
|
||||||
# Authors: Steve Reinhardt
|
# Authors: Steve Reinhardt
|
||||||
# Nathan Binkert
|
# Nathan Binkert
|
||||||
|
|
||||||
from __future__ import generators
|
|
||||||
import os, re, sys, types, inspect
|
import os, re, sys, types, inspect
|
||||||
|
|
||||||
import m5
|
import m5
|
||||||
panic = m5.panic
|
from m5 import panic
|
||||||
from convert import *
|
from convert import *
|
||||||
from multidict import multidict
|
from multidict import multidict
|
||||||
|
|
||||||
|
@ -137,6 +136,13 @@ class Singleton(type):
|
||||||
def isSimObject(value):
|
def isSimObject(value):
|
||||||
return isinstance(value, SimObject)
|
return isinstance(value, SimObject)
|
||||||
|
|
||||||
|
def isSimObjectClass(value):
|
||||||
|
try:
|
||||||
|
return issubclass(value, SimObject)
|
||||||
|
except TypeError:
|
||||||
|
# happens if value is not a class at all
|
||||||
|
return False
|
||||||
|
|
||||||
def isSimObjSequence(value):
|
def isSimObjSequence(value):
|
||||||
if not isinstance(value, (list, tuple)):
|
if not isinstance(value, (list, tuple)):
|
||||||
return False
|
return False
|
||||||
|
@ -147,6 +153,16 @@ def isSimObjSequence(value):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def isSimObjClassSequence(value):
|
||||||
|
if not isinstance(value, (list, tuple)):
|
||||||
|
return False
|
||||||
|
|
||||||
|
for val in value:
|
||||||
|
if not isNullPointer(val) and not isSimObjectClass(val):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def isNullPointer(value):
|
def isNullPointer(value):
|
||||||
return isinstance(value, NullSimObject)
|
return isinstance(value, NullSimObject)
|
||||||
|
|
||||||
|
@ -170,19 +186,26 @@ class MetaSimObject(type):
|
||||||
# and only allow "private" attributes to be passed to the base
|
# and only allow "private" attributes to be passed to the base
|
||||||
# __new__ (starting with underscore).
|
# __new__ (starting with underscore).
|
||||||
def __new__(mcls, name, bases, dict):
|
def __new__(mcls, name, bases, dict):
|
||||||
# Copy "private" attributes (including special methods such as __new__)
|
if dict.has_key('_init_dict'):
|
||||||
# to the official dict. Everything else goes in _init_dict to be
|
# must have been called from makeSubclass() rather than
|
||||||
# filtered in __init__.
|
# via Python class declaration; bypass filtering process.
|
||||||
cls_dict = {}
|
cls_dict = dict
|
||||||
for key,val in dict.items():
|
else:
|
||||||
if key.startswith('_'):
|
# Copy "private" attributes (including special methods
|
||||||
cls_dict[key] = val
|
# such as __new__) to the official dict. Everything else
|
||||||
del dict[key]
|
# goes in _init_dict to be filtered in __init__.
|
||||||
cls_dict['_init_dict'] = dict
|
cls_dict = {}
|
||||||
|
for key,val in dict.items():
|
||||||
|
if key.startswith('_'):
|
||||||
|
cls_dict[key] = val
|
||||||
|
del dict[key]
|
||||||
|
cls_dict['_init_dict'] = dict
|
||||||
return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
|
return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
|
||||||
|
|
||||||
# initialization
|
# subclass initialization
|
||||||
def __init__(cls, name, bases, dict):
|
def __init__(cls, name, bases, dict):
|
||||||
|
# calls type.__init__()... I think that's a no-op, but leave
|
||||||
|
# it here just in case it's not.
|
||||||
super(MetaSimObject, cls).__init__(name, bases, dict)
|
super(MetaSimObject, cls).__init__(name, bases, dict)
|
||||||
|
|
||||||
# initialize required attributes
|
# initialize required attributes
|
||||||
|
@ -197,27 +220,13 @@ class MetaSimObject(type):
|
||||||
|
|
||||||
base = bases[0]
|
base = bases[0]
|
||||||
|
|
||||||
|
# the only time the following is not true is when we define
|
||||||
|
# the SimObject class itself
|
||||||
if isinstance(base, MetaSimObject):
|
if isinstance(base, MetaSimObject):
|
||||||
cls._params.parent = base._params
|
cls._params.parent = base._params
|
||||||
cls._values.parent = base._values
|
cls._values.parent = base._values
|
||||||
|
|
||||||
# If your parent has a value in it that's a config node, clone
|
# now process the _init_dict items
|
||||||
# it. Do this now so if we update any of the values'
|
|
||||||
# attributes we are updating the clone and not the original.
|
|
||||||
for key,val in base._values.iteritems():
|
|
||||||
|
|
||||||
# don't clone if (1) we're about to overwrite it with
|
|
||||||
# a local setting or (2) we've already cloned a copy
|
|
||||||
# from an earlier (more derived) base
|
|
||||||
if cls._init_dict.has_key(key) or cls._values.has_key(key):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if isSimObject(val):
|
|
||||||
cls._values[key] = val()
|
|
||||||
elif isSimObjSequence(val) and len(val):
|
|
||||||
cls._values[key] = [ v() for v in val ]
|
|
||||||
|
|
||||||
# now process remaining _init_dict items
|
|
||||||
for key,val in cls._init_dict.items():
|
for key,val in cls._init_dict.items():
|
||||||
if isinstance(val, (types.FunctionType, types.TypeType)):
|
if isinstance(val, (types.FunctionType, types.TypeType)):
|
||||||
type.__setattr__(cls, key, val)
|
type.__setattr__(cls, key, val)
|
||||||
|
@ -234,6 +243,33 @@ class MetaSimObject(type):
|
||||||
else:
|
else:
|
||||||
setattr(cls, key, val)
|
setattr(cls, key, val)
|
||||||
|
|
||||||
|
# Pull the deep-copy memoization dict out of the class dict if
|
||||||
|
# it's there...
|
||||||
|
memo = cls.__dict__.get('_memo', {})
|
||||||
|
|
||||||
|
# Handle SimObject values
|
||||||
|
for key,val in cls._values.iteritems():
|
||||||
|
# SimObject instances need to be promoted to classes.
|
||||||
|
# Existing classes should not have any instance values, so
|
||||||
|
# these can only occur at the lowest level dict (the
|
||||||
|
# parameters just being set in this class definition).
|
||||||
|
if isSimObject(val):
|
||||||
|
assert(val == cls._values.local[key])
|
||||||
|
cls._values[key] = val.makeClass(memo)
|
||||||
|
elif isSimObjSequence(val) and len(val):
|
||||||
|
assert(val == cls._values.local[key])
|
||||||
|
cls._values[key] = [ v.makeClass(memo) for v in val ]
|
||||||
|
# SimObject classes need to be subclassed so that
|
||||||
|
# parameters that get set at this level only affect this
|
||||||
|
# level and derivatives.
|
||||||
|
elif isSimObjectClass(val):
|
||||||
|
assert(not cls._values.local.has_key(key))
|
||||||
|
cls._values[key] = val.makeSubclass({}, memo)
|
||||||
|
elif isSimObjClassSequence(val) and len(val):
|
||||||
|
assert(not cls._values.local.has_key(key))
|
||||||
|
cls._values[key] = [ v.makeSubclass({}, memo) for v in val ]
|
||||||
|
|
||||||
|
|
||||||
def _set_keyword(cls, keyword, val, kwtype):
|
def _set_keyword(cls, keyword, val, kwtype):
|
||||||
if not isinstance(val, kwtype):
|
if not isinstance(val, kwtype):
|
||||||
raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
|
raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
|
||||||
|
@ -284,6 +320,22 @@ class MetaSimObject(type):
|
||||||
raise AttributeError, \
|
raise AttributeError, \
|
||||||
"object '%s' has no attribute '%s'" % (cls.__name__, attr)
|
"object '%s' has no attribute '%s'" % (cls.__name__, attr)
|
||||||
|
|
||||||
|
# Create a subclass of this class. Basically a function interface
|
||||||
|
# to the standard Python class definition mechanism, primarily for
|
||||||
|
# internal use. 'memo' dict param supports "deep copy" (really
|
||||||
|
# "deep subclass") operations... within a given operation,
|
||||||
|
# multiple references to a class should result in a single
|
||||||
|
# subclass object with multiple references to it (as opposed to
|
||||||
|
# mutiple unique subclasses).
|
||||||
|
def makeSubclass(cls, init_dict, memo = {}):
|
||||||
|
subcls = memo.get(cls)
|
||||||
|
if not subcls:
|
||||||
|
name = cls.__name__ + '_' + str(cls._anon_subclass_counter)
|
||||||
|
cls._anon_subclass_counter += 1
|
||||||
|
subcls = MetaSimObject(name, (cls,),
|
||||||
|
{ '_init_dict': init_dict, '_memo': memo })
|
||||||
|
return subcls
|
||||||
|
|
||||||
# The ConfigNode class is the root of the special hierarchy. Most of
|
# The ConfigNode class is the root of the special hierarchy. Most of
|
||||||
# the code in this class deals with the configuration hierarchy itself
|
# the code in this class deals with the configuration hierarchy itself
|
||||||
# (parent/child node relationships).
|
# (parent/child node relationships).
|
||||||
|
@ -292,27 +344,78 @@ class SimObject(object):
|
||||||
# get this metaclass.
|
# get this metaclass.
|
||||||
__metaclass__ = MetaSimObject
|
__metaclass__ = MetaSimObject
|
||||||
|
|
||||||
def __init__(self, _value_parent = None, **kwargs):
|
# __new__ operator allocates new instances of the class. We
|
||||||
|
# override it here just to support "deep instantiation" operation
|
||||||
|
# via the _memo dict. When recursively instantiating an object
|
||||||
|
# hierarchy we want to make sure that each class is instantiated
|
||||||
|
# only once, and that if there are multiple references to the same
|
||||||
|
# original class, we end up with the corresponding instantiated
|
||||||
|
# references all pointing to the same instance.
|
||||||
|
def __new__(cls, _memo = None, **kwargs):
|
||||||
|
if _memo is not None and _memo.has_key(cls):
|
||||||
|
# return previously instantiated object
|
||||||
|
assert(len(kwargs) == 0)
|
||||||
|
return _memo[cls]
|
||||||
|
else:
|
||||||
|
# Need a new one... if it needs to be memoized, this will
|
||||||
|
# happen in __init__. We defer the insertion until then
|
||||||
|
# so __init__ can use the memo dict to tell whether or not
|
||||||
|
# to perform the initialization.
|
||||||
|
return super(SimObject, cls).__new__(cls, **kwargs)
|
||||||
|
|
||||||
|
# Initialize new instance previously allocated by __new__. For
|
||||||
|
# objects with SimObject-valued params, we need to recursively
|
||||||
|
# instantiate the classes represented by those param values as
|
||||||
|
# well (in a consistent "deep copy"-style fashion; see comment
|
||||||
|
# above).
|
||||||
|
def __init__(self, _memo = None, **kwargs):
|
||||||
|
if _memo is not None:
|
||||||
|
# We're inside a "deep instantiation"
|
||||||
|
assert(isinstance(_memo, dict))
|
||||||
|
assert(len(kwargs) == 0)
|
||||||
|
if _memo.has_key(self.__class__):
|
||||||
|
# __new__ returned an existing, already initialized
|
||||||
|
# instance, so there's nothing to do here
|
||||||
|
assert(_memo[self.__class__] == self)
|
||||||
|
return
|
||||||
|
# no pre-existing object, so remember this one here
|
||||||
|
_memo[self.__class__] = self
|
||||||
|
else:
|
||||||
|
# This is a new top-level instantiation... don't memoize
|
||||||
|
# this objcet, but prepare to memoize any recursively
|
||||||
|
# instantiated objects.
|
||||||
|
_memo = {}
|
||||||
|
|
||||||
self._children = {}
|
self._children = {}
|
||||||
if _value_parent and type(_value_parent) != type(self):
|
# Inherit parameter values from class using multidict so
|
||||||
# this was called as a type conversion rather than a clone
|
# individual value settings can be overridden.
|
||||||
raise TypeError, "Cannot convert %s to %s" % \
|
self._values = multidict(self.__class__._values)
|
||||||
(_value_parent.__class__.__name__, self.__class__.__name__)
|
# For SimObject-valued parameters, the class should have
|
||||||
if not _value_parent:
|
# classes (not instances) for the values. We need to
|
||||||
_value_parent = self.__class__
|
# instantiate these classes rather than just inheriting the
|
||||||
# clone values
|
# class object.
|
||||||
self._values = multidict(_value_parent._values)
|
for key,val in self.__class__._values.iteritems():
|
||||||
for key,val in _value_parent._values.iteritems():
|
if isSimObjectClass(val):
|
||||||
if isSimObject(val):
|
setattr(self, key, val(_memo))
|
||||||
setattr(self, key, val())
|
elif isSimObjClassSequence(val) and len(val):
|
||||||
elif isSimObjSequence(val) and len(val):
|
setattr(self, key, [ v(_memo) for v in val ])
|
||||||
setattr(self, key, [ v() for v in val ])
|
|
||||||
# apply attribute assignments from keyword args, if any
|
# apply attribute assignments from keyword args, if any
|
||||||
for key,val in kwargs.iteritems():
|
for key,val in kwargs.iteritems():
|
||||||
setattr(self, key, val)
|
setattr(self, key, val)
|
||||||
|
|
||||||
|
# Use this instance as a template to create a new class.
|
||||||
|
def makeClass(self, memo = {}):
|
||||||
|
cls = memo.get(self)
|
||||||
|
if not cls:
|
||||||
|
cls = self.__class__.makeSubclass(self._values.local)
|
||||||
|
memo[self] = cls
|
||||||
|
return cls
|
||||||
|
|
||||||
|
# Direct instantiation of instances (cloning) is no longer
|
||||||
|
# allowed; must generate class from instance first.
|
||||||
def __call__(self, **kwargs):
|
def __call__(self, **kwargs):
|
||||||
return self.__class__(_value_parent = self, **kwargs)
|
raise TypeError, "cannot instantiate SimObject; "\
|
||||||
|
"use makeClass() to make class first"
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
if self._values.has_key(attr):
|
if self._values.has_key(attr):
|
||||||
|
@ -1069,7 +1172,10 @@ class EthernetAddr(ParamValue):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.value == NextEthernetAddr:
|
if self.value == NextEthernetAddr:
|
||||||
return self.addr
|
if hasattr(self, 'addr'):
|
||||||
|
return self.addr
|
||||||
|
else:
|
||||||
|
return "NextEthernetAddr (unresolved)"
|
||||||
else:
|
else:
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ __all__ = [ 'multidict' ]
|
||||||
class multidict(object):
|
class multidict(object):
|
||||||
__nodefault = object()
|
__nodefault = object()
|
||||||
def __init__(self, parent = {}, **kwargs):
|
def __init__(self, parent = {}, **kwargs):
|
||||||
self.dict = dict(**kwargs)
|
self.local = dict(**kwargs)
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.deleted = {}
|
self.deleted = {}
|
||||||
|
|
||||||
|
@ -42,11 +42,11 @@ class multidict(object):
|
||||||
return `dict(self.items())`
|
return `dict(self.items())`
|
||||||
|
|
||||||
def __contains__(self, key):
|
def __contains__(self, key):
|
||||||
return self.dict.has_key(key) or self.parent.has_key(key)
|
return self.local.has_key(key) or self.parent.has_key(key)
|
||||||
|
|
||||||
def __delitem__(self, key):
|
def __delitem__(self, key):
|
||||||
try:
|
try:
|
||||||
del self.dict[key]
|
del self.local[key]
|
||||||
except KeyError, e:
|
except KeyError, e:
|
||||||
if key in self.parent:
|
if key in self.parent:
|
||||||
self.deleted[key] = True
|
self.deleted[key] = True
|
||||||
|
@ -55,11 +55,11 @@ class multidict(object):
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
self.deleted.pop(key, False)
|
self.deleted.pop(key, False)
|
||||||
self.dict[key] = value
|
self.local[key] = value
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
try:
|
try:
|
||||||
return self.dict[key]
|
return self.local[key]
|
||||||
except KeyError, e:
|
except KeyError, e:
|
||||||
if not self.deleted.get(key, False) and key in self.parent:
|
if not self.deleted.get(key, False) and key in self.parent:
|
||||||
return self.parent[key]
|
return self.parent[key]
|
||||||
|
@ -67,15 +67,15 @@ class multidict(object):
|
||||||
raise KeyError, e
|
raise KeyError, e
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self.dict) + len(self.parent)
|
return len(self.local) + len(self.parent)
|
||||||
|
|
||||||
def next(self):
|
def next(self):
|
||||||
for key,value in self.dict.items():
|
for key,value in self.local.items():
|
||||||
yield key,value
|
yield key,value
|
||||||
|
|
||||||
if self.parent:
|
if self.parent:
|
||||||
for key,value in self.parent.next():
|
for key,value in self.parent.next():
|
||||||
if key not in self.dict and key not in self.deleted:
|
if key not in self.local and key not in self.deleted:
|
||||||
yield key,value
|
yield key,value
|
||||||
|
|
||||||
def has_key(self, key):
|
def has_key(self, key):
|
||||||
|
@ -116,22 +116,22 @@ class multidict(object):
|
||||||
return self[key]
|
return self[key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.deleted.pop(key, False)
|
self.deleted.pop(key, False)
|
||||||
self.dict[key] = default
|
self.local[key] = default
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def _dump(self):
|
def _dump(self):
|
||||||
print 'multidict dump'
|
print 'multidict dump'
|
||||||
node = self
|
node = self
|
||||||
while isinstance(node, multidict):
|
while isinstance(node, multidict):
|
||||||
print ' ', node.dict
|
print ' ', node.local
|
||||||
node = node.parent
|
node = node.parent
|
||||||
|
|
||||||
def _dumpkey(self, key):
|
def _dumpkey(self, key):
|
||||||
values = []
|
values = []
|
||||||
node = self
|
node = self
|
||||||
while isinstance(node, multidict):
|
while isinstance(node, multidict):
|
||||||
if key in node.dict:
|
if key in node.local:
|
||||||
values.append(node.dict[key])
|
values.append(node.local[key])
|
||||||
node = node.parent
|
node = node.parent
|
||||||
print key, values
|
print key, values
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue