[pypy-commit] pypy default: Merged in anton_gulenko/pypy (pull request #306)

cfbolz noreply at buildbot.pypy.org
Wed Apr 22 12:21:03 CEST 2015


Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch: 
Changeset: r76883:62106262ee6c
Date: 2015-04-22 12:20 +0200
http://bitbucket.org/pypy/pypy/changeset/62106262ee6c/

Log:	Merged in anton_gulenko/pypy (pull request #306)

	rstrategies - A library for Storage Strategies

diff too long, truncating to 2000 out of 2151 lines

diff --git a/rpython/doc/rlib.rst b/rpython/doc/rlib.rst
--- a/rpython/doc/rlib.rst
+++ b/rpython/doc/rlib.rst
@@ -128,6 +128,14 @@
 a hierarchy of Address classes, in a typical static-OO-programming style.
 
 
+rstrategies
+-----------
+
+The :source:`rpython/rlib/rstrategies` module contains a library to implement storage strategies in
+RPython VMs. The library is language-independent and extensible.
+More details and examples can be found in the :doc:`rstrategies documentation <rstrategies>`.
+
+
 streamio
 --------
 
diff --git a/rpython/doc/rstrategies.rst b/rpython/doc/rstrategies.rst
new file mode 100644
--- /dev/null
+++ b/rpython/doc/rstrategies.rst
@@ -0,0 +1,209 @@
+rstrategies
+===========
+
+A library to implement storage strategies in VMs based on the RPython
+toolchain. rstrategies can be used in VMs for any language or language
+family.
+
+This library has been developed as part of a Masters Thesis by `Anton
+Gulenko <https://github.com/antongulenko>`__.
+
+The original paper describing the optimization "Storage Strategies for
+collections in dynamically typed languages" by C.F. Bolz, L. Diekmann
+and L. Tratt can be found
+`here <http://stups.hhu.de/mediawiki/images/3/3b/Pub-BoDiTr13_246.pdf>`__.
+
+So far, this library has been adpoted by 3 VMs:
+`RSqueak <https://github.com/HPI-SWA-Lab/RSqueak>`__,
+`Topaz <https://github.com/topazproject/topaz>`__ (`Forked
+here <https://github.com/antongulenko/topaz/tree/rstrategies>`__) and
+`Pycket <https://github.com/samth/pycket>`__ (`Forked
+here <https://github.com/antongulenko/pycket/tree/rstrategies>`__).
+
+Concept
+-------
+
+Collections are often used homogeneously, i.e. they contain only objects
+of the same type. Primitive numeric types like ints or floats are
+especially interesting for optimization. These cases can be optimized by
+storing the unboxed data of these objects in consecutive memory. This is
+done by letting a special "strategy" object handle the entire storage of
+a collection. The collection object holds two separate references: one
+to its strategy and one to its storage. Every operation on the
+collection is delegated to the strategy, which accesses the storage when
+needed. The strategy can be switched to a more suitable one, which might
+require converting the storage array.
+
+Usage
+~~~~~
+
+The following are the steps needed to integrated rstrategies in an
+RPython VM. Because of the special nature of this library it is not
+enough to simply call some API methods; the library must be integrated
+within existing VM classes using a metaclass, mixins and other
+meta-programming techniques.
+
+The sequence of steps described here is something like a "setup
+walkthrough", and might be a bit abstract. To see a concrete example,
+look at
+`SingletonStorageStrategy <https://github.com/HPI-SWA-Lab/RSqueak/blob/d048f713002c01c9b121c80e8eb9bea33ed742d6/spyvm/storage.py#L73>`__,
+`StrategyFactory <https://github.com/HPI-SWA-Lab/RSqueak/blob/d048f713002c01c9b121c80e8eb9bea33ed742d6/spyvm/storage.py#L126>`__
+and
+`W\_PointersObject <https://github.com/HPI-SWA-Lab/RSqueak/blob/d048f713002c01c9b121c80e8eb9bea33ed742d6/spyvm/model.py#L616>`__
+from the `RSqueak VM <https://github.com/HPI-SWA-Lab/RSqueak>`__. The
+code is also well commented.
+
+Basics
+-------
+
+Currently the rstrategies library supports fixed sized and variable
+sized collections. This can be used to optimize a wide range of
+primitive data structures like arrays, lists or regular objects. Any of
+these are called 'collections' in this context. The VM should have a
+central class or class hierarchy for collections. In order to extend
+these classes and use strategies, the library needs accessor methods for
+two attributes of collection objects: strategy and storage. The easiest
+way is adding the following line to the body of the root collection
+class:
+
+::
+
+    rstrategies.make_accessors(strategy='strategy', storage='storage')
+
+This will generate the 4 accessor methods
+``_[get/set]_[storage/strategy]()`` for the respective attributes.
+Alternatively, implement these methods manually or overwrite the
+getters/setters in ``StrategyFactory``.
+
+Next, the strategy classes must be defined. This requires a small class
+hierarchy with a dedicated root class. In the definition of this root
+class, include the following lines:
+
+::
+
+        __metaclass__ = rstrategies.StrategyMetaclass
+        import_from_mixin(rstrategies.AbstractStrategy)
+        import_from_mixin(rstrategies.SafeIndexingMixin)
+
+``import_from_mixin`` can be found in ``rpython.rlib.objectmodel``. If
+index-checking is performed safely at other places in the VM, you can
+use ``rstrategies.UnsafeIndexingMixin`` instead. If you need your own
+metaclass, you can combine yours with the rstrategies one using multiple
+inheritance `like
+here <https://github.com/HPI-SWA-Lab/RSqueak/blob/d5ff2572106d23a5246884de6f8b86f46d85f4f7/spyvm/storage_contexts.py#L24>`__.
+Also implement a ``storage_factory()`` method, which returns an instance
+of ``rstrategies.StorageFactory``, which is described below.
+
+An example ``AbstractStrategy`` class, which also stores an additional ``space`` parameter could looks like this:
+
+::
+
+    class AbstractStrategy(AbstractStrategy):
+        _attrs_ = ['space']
+        _immutable_fields_ = ['space']
+        __metaclass__ = rstrat.StrategyMetaclass
+        import_from_mixin(rstrat.AbstractStrategy)
+        import_from_mixin(rstrategies.SafeIndexingMixin)
+        
+        def __init__(self, space):
+            self.space = space
+        
+        def strategy_factory(self):
+            return self.space.strategy_factory
+
+
+Strategy classes
+----------------
+
+Now you can create the actual strategy classes, subclassing them from
+the single root class. The following list summarizes the basic
+strategies available.
+
+- ``EmptyStrategy`` A strategy for empty collections; very efficient, but limited. Does not allocate anything.
+- ``SingleValueStrategy`` A strategy for collections containing the same object ``n`` times. Only allocates memory to store the size of the collection.
+- ``GenericStrategy`` A non-optimized strategy backed by a generic python list. This is the fallback strategy, since it can store everything, but is not optimized.
+- ``WeakGenericStrategy`` Like ``GenericStrategy``, but uses ``weakref`` to hold on weakly to its elements.
+- ``SingleTypeStrategy`` Can store a single unboxed type like int or float. This is the main optimizing strategy
+- ``TaggingStrategy`` Extension of SingleTypeStrategy. Uses a specific value in the value range of the unboxed type to represent one additional, arbitrary object. For example, one of ``float``'s ``NaN`` representations can be used to represent special value like ``nil``.
+
+There are also intermediate classes, which allow creating new, more
+customized strategies. For this, you should get familiar with the code.
+
+Include one of these mixin classes using ``import_from_mixin``. The
+mixin classes contain comments describing methods or fields which are
+also required in the strategy class in order to use them. Additionally,
+add the ``@rstrategies.strategy(generalize=alist)`` decorator to all
+strategy classes. The ``alist`` parameter must contain all strategies,
+which the decorated strategy can switch to, if it can not represent a
+new element anymore.
+`Example <https://github.com/HPI-SWA-Lab/RSqueak/blob/d5ff2572106d23a5246884de6f8b86f46d85f4f7/spyvm/storage.py#L87>`__
+for an implemented strategy. See the other strategy classes behind this
+link for more examples.
+
+An example strategy class for optimized ``int`` storage could look like this:
+
+::
+
+    @rstrat.strategy(generalize=[GenericStrategy])
+    class IntegerOrNilStrategy(AbstractStrategy):
+        import_from_mixin(rstrat.TaggingStrategy)
+        contained_type = model.W_Integer
+        def wrap(self, val): return self.space.wrap_int(val)
+        def unwrap(self, w_val): return self.space.unwrap_int(w_val)
+        def wrapped_tagged_value(self): return self.space.w_nil
+        def unwrapped_tagged_value(self): return constants.MAXINT
+
+Strategy Factory
+----------------
+
+The last part is subclassing ``rstrategies.StrategyFactory``,
+overwriting the method ``instantiate_strategy`` if necessary and passing
+the strategies root class to the constructor. The factory provides the
+methods ``switch_strategy``, ``set_initial_strategy``,
+``strategy_type_for`` which can be used by the VM code to use the
+mechanism behind strategies. See the comments in the source code.
+
+The strategy mixins offer the following methods to manipulate the
+contents of the collection:
+
+- basic API
+
+  - ``size``
+
+- fixed size API
+
+  - ``store``, ``fetch``, ``slice``, ``store_all``, ``fetch_all``
+
+- variable size API
+
+  - ``insert``, ``delete``, ``append``, ``pop``
+
+If the collection has a fixed size, simply never use any of the variable
+size methods in the VM code. Since the strategies are singletons, these
+methods need the collection object as first parameter. For convenience,
+more fitting accessor methods should be implemented on the collection
+class itself.
+
+An example strategy factory for the ``AbstractStrategy`` class above could look like this:
+
+::
+
+    class StrategyFactory(rstrategies.StrategyFactory):
+        _attrs_ = ['space']
+        _immutable_fields_ = ['space']
+        
+        def __init__(self, space):
+            self.space = space
+            rstrat.StrategyFactory.__init__(self, AbstractStrategy)
+        
+        def instantiate_strategy(self, strategy_type):
+            return strategy_type(self.space)
+        
+        def strategy_type_for(self, list_w, weak=False):
+            """
+            Helper method for handling weak objects specially
+            """
+            if weak:
+                return WeakListStrategy
+        return rstrategies.StrategyFactory.strategy_type_for(self, list_w)
+    
\ No newline at end of file
diff --git a/rpython/rlib/rstrategies/.coveragerc b/rpython/rlib/rstrategies/.coveragerc
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rstrategies/.coveragerc
@@ -0,0 +1,10 @@
+# .coveragerc file to control coverage.py (code coverage plugin for pytest)
+# Get it here: https://pypi.python.org/pypi/pytest-cov
+# Examples:
+# $ python -m pytest test --cov rpython.rlib.rstrategies --cov-report html --cov-config .coveragerc
+
+[run]
+omit =
+    test/*
+    */__init__.py
+    logparser.py
diff --git a/rpython/rlib/rstrategies/__init__.py b/rpython/rlib/rstrategies/__init__.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rstrategies/__init__.py
@@ -0,0 +1,1 @@
+# Empy
diff --git a/rpython/rlib/rstrategies/logger.py b/rpython/rlib/rstrategies/logger.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rstrategies/logger.py
@@ -0,0 +1,58 @@
+
+class LogEntry(object):
+    def __init__(self):
+        self.slots = 0
+        self.objects = 0
+        self.element_typenames = {}
+        
+    def add(self, size, element_typename):
+        self.slots += size
+        self.objects += 1
+        if element_typename:
+            self.element_typenames[element_typename] = None
+    
+    def classnames(self):
+        return self.element_typenames.keys()
+
+class Logger(object):
+    _attrs_ = ["active", "aggregate", "logs"]
+    _immutable_fields_ = ["active?", "aggregate?", "logs"]
+    
+    def __init__(self):
+        self.active = False
+        self.aggregate = False
+        self.logs = {}
+    
+    def activate(self, aggregate=False):
+        self.active = True
+        self.aggregate = self.aggregate or aggregate
+    
+    def log(self, new_strategy, size, cause="", old_strategy="", typename="", element_typename=""):
+        if self.aggregate:
+            key = (cause, old_strategy, new_strategy, typename)
+            if key not in self.logs:
+                self.logs[key] = LogEntry()
+            entry = self.logs[key]
+            entry.add(size, element_typename)
+        else:
+            element_typenames = [ element_typename ] if element_typename else []
+            self.output(cause, old_strategy, new_strategy, typename, size, 1, element_typenames)
+    
+    def print_aggregated_log(self):
+        if not self.aggregate:
+            return
+        for key, entry in self.logs.items():
+            cause, old_strategy, new_strategy, typename = key
+            slots, objects, element_typenames = entry.slots, entry.objects, entry.classnames()
+            self.output(cause, old_strategy, new_strategy, typename, slots, objects, element_typenames)
+    
+    def output(self, cause, old_strategy, new_strategy, typename, slots, objects, element_typenames):
+        old_strategy_string = "%s -> " % old_strategy if old_strategy else ""
+        classname_string = " of %s" % typename if typename else ""
+        element_string = (" elements: " + " ".join(element_typenames)) if element_typenames else ""
+        format = (cause, old_strategy_string, new_strategy, classname_string, slots, objects, element_string)
+        self.do_print("%s (%s%s)%s size %d objects %d%s" % format)
+    
+    def do_print(self, str):
+        # Hook to increase testability
+        print str
diff --git a/rpython/rlib/rstrategies/logparser.py b/rpython/rlib/rstrategies/logparser.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rstrategies/logparser.py
@@ -0,0 +1,694 @@
+
+import re, os, sys, operator
+
+"""
+This script parses a log produced by rstrategies_logger.py into a graph and converts it to various outputs.
+The most useful outputs are the dot* commands producing a visualization of the log using the dot-command of graphviz.
+Every strategy is a node in the graph, and the edges are collections or objects that transition between
+two strategies at some point during the log.
+Artificial nodes are created for log entries without an explicit source node. These are the events when a
+collection is created.
+The input to this script is a logfile, a command and optional flags.
+If the name of the logfile includes one of the AVAILABLE_VMS as a substring, the first three global variables
+are automatically configured.
+The script should work without these configurations, but the output will probably not be that pretty.
+To avoid errors, the -a flag is implied when running without proper configuration.
+"""
+
+# This should contain a full list of storage nodes (strategies).
+# All strategies not included here will be combined into a single "Other"-node, if the -a flag is not given.
+STORAGE_NODES = []
+
+# This allows arbitrary renamings of storage strategy nodes
+NODE_RENAMINGS = {}
+
+# Artificial storage-source nodes are automatically named like the associated operation.
+# This dict allows customizing the names of these nodes.
+STORAGE_SOURCES = {}
+
+def SET_VM(vm_name):
+    global STORAGE_NODES
+    global NODE_RENAMINGS
+    global STORAGE_SOURCES
+    if vm_name == 'RSqueak':
+        STORAGE_NODES = ['List', 'WeakList', 'SmallIntegerOrNil', 'FloatOrNil', 'AllNil']
+        NODE_RENAMINGS = dict((x+'Strategy', x) for x in STORAGE_NODES)
+        STORAGE_SOURCES = {'Filledin': 'Image Loading', 'Initialized': 'Object Creation'}
+    elif vm_name == 'Pycket':
+        STORAGE_SOURCES = {'Created': 'Array Creation'}
+        # TODO
+    elif vm_name == 'Topaz':
+        # TODO
+        pass
+    else:
+        raise Exception("Unhandled vm name %s" % vm_name)
+
+AVAILABLE_VMS = ['RSqueak', 'Pycket', 'Topaz']
+
+def configure_vm(logfile, flags):
+    vm_config_name = None
+    for vm_name in AVAILABLE_VMS:
+        if vm_name in logfile:
+            vm_config_name = vm_name
+            break
+    if vm_config_name is not None:
+        print "Using VM configuration %s" % vm_name
+        SET_VM(vm_name)
+    else:
+        print "No VM configuration found in filename '%s'. Available configurations: %s" % \
+                (logfile, AVAILABLE_VMS)
+        print "Please add new VM configuration or rename logfile. Turning on -a flag to avoid errors."
+        flags.allstorage = True
+
+# ====================================================================
+# ======== Logfile parsing
+# ====================================================================
+
+def percent(part, total):
+    if total == 0:
+        return 0
+    return float(part)*100 / total
+
+def parse(filename, flags, callback):
+    parsed_entries = 0
+    if filename == "-":
+        opener = lambda: sys.stdin
+    else:
+        opener = lambda: open(filename, 'r', 1)
+    with opener() as file:
+        while True:
+            line = file.readline()
+            if len(line) == 0:
+                break
+            entry = parse_line(line, flags)
+            if entry:
+                parsed_entries += 1
+                callback(entry)
+    return parsed_entries
+
+line_pattern = re.compile("^(?P<operation>\w+) \(((?P<old>\w+) -> )?(?P<new>\w+)\)( of (?P<classname>.+))? size (?P<size>[0-9]+)( objects (?P<objects>[0-9]+))?( elements: (?P<classnames>.+( .+)*))?$")
+
+def parse_line(line, flags):
+    result = line_pattern.match(line)
+    if result is None:
+        if flags.verbose:
+            print "Could not parse line: %s" % line[:-1]
+        return None
+    operation = str(result.group('operation'))
+    old_storage = result.group('old')
+    new_storage = str(result.group('new'))
+    classname = str(result.group('classname'))
+    size = int(result.group('size'))
+    objects = result.group('objects')
+    objects = int(objects) if objects else 1
+    classnames = result.group('classnames')
+    if classnames is not None:
+        classnames = classnames.split(' ')
+        classnames = set(classnames)
+    else:
+        classnames = set()
+    
+    is_storage_source = old_storage is None
+    if is_storage_source:
+        if operation in STORAGE_SOURCES:
+            old_storage = STORAGE_SOURCES[operation]
+        else:
+            print "Using operation %s as storage source." % operation
+    old_storage = str(old_storage)
+    
+    if new_storage in NODE_RENAMINGS:
+        new_storage = NODE_RENAMINGS[new_storage]
+    if old_storage in NODE_RENAMINGS:
+        old_storage = NODE_RENAMINGS[old_storage]
+    
+    return LogEntry(operation, old_storage, new_storage, classname, size, objects, classnames, is_storage_source)
+
+class LogEntry(object):
+    
+    def __init__(self, operation, old_storage, new_storage, classname, size, objects, classnames, is_storage_source):
+        self.operation = operation
+        self.old_storage = old_storage
+        self.new_storage = new_storage
+        self.classname = classname
+        self.size = size
+        self.objects = objects
+        self.classnames = classnames
+        self.is_storage_source = is_storage_source
+        assert old_storage != new_storage, "old and new storage identical in log entry: %s" % self
+    
+    def full_key(self):
+        return (self.operation, self.old_storage, self.new_storage)
+    
+    def __lt__(self, other):
+        return self.classname < other.classname
+    
+    def __repr__(self):
+        return "%s(%s)" % (self.__str__(), object.__repr__(self))
+    
+    def __str__(self):
+        old_storage_string = "%s -> " % self.old_storage if self.old_storage else ""
+        classname_string = " of %s" % self.classname if self.classname else ""
+        objects_string = " objects %d" % self.objects if self.objects > 1 else ""
+        return "%s (%s%s)%s size %d%s" % (self.operation, old_storage_string, self.new_storage, classname_string, self.size, objects_string)
+
+# ====================================================================
+# ======== Graph parsing
+# ====================================================================
+
+class Operations(object):
+    
+    def __init__(self, objects=0, slots=0, element_classnames=[]):
+        self.objects = objects
+        self.slots = slots
+        self.element_classnames = set(element_classnames)
+    
+    def __str__(self, total=None):
+        if self.objects == 0:
+            avg_slots = 0
+        else:
+            avg_slots = float(self.slots) / self.objects
+        if total is not None and total.slots != 0:
+            percent_slots = " (%.1f%%)" % percent(self.slots, total.slots)
+        else:
+            percent_slots = ""
+        if total is not None and total.objects != 0:
+            percent_objects = " (%.1f%%)" % percent(self.objects, total.objects)
+        else:
+            percent_objects = ""
+        slots = format(self.slots, ",d")
+        objects = format(self.objects, ",d")
+        classnames = (" [ elements: %s ]" % ' '.join([str(x) for x in self.element_classnames])) \
+                                    if len(self.element_classnames) else ""
+        return "%s%s slots in %s%s objects (avg size: %.1f)%s" % (slots, percent_slots, objects, percent_objects, avg_slots, classnames)
+    
+    def __repr__(self):
+        return "%s(%s)" % (self.__str__(), object.__repr__(self))
+    
+    def add_log_entry(self, entry):
+        self.slots = self.slots + entry.size
+        self.objects = self.objects + entry.objects
+        self.element_classnames |= entry.classnames
+    
+    def __sub__(self, other):
+        return Operations(self.objects - other.objects, self.slots - other.slots)
+    
+    def __add__(self, other):
+        return Operations(self.objects + other.objects, self.slots + other.slots)
+    
+    def __lt__(self, other):
+        return self.slots < other.slots
+    
+    def empty(self):
+        return self.objects == 0 and self.slots == 0
+    
+    def prefixprint(self, key="", total=None):
+        if not self.empty():
+            print "%s%s" % (key, self.__str__(total))
+    
+class ClassOperations(object):
+    
+    def __init__(self):
+        self.classes = {}
+    
+    def cls(self, name):
+        if name not in self.classes:
+            self.classes[name] = Operations()
+        return self.classes[name]
+    
+    def total(self):
+        return reduce(operator.add, self.classes.values(), Operations())
+    
+    def __str__(self):
+        return "ClassOperations(%s)" % self.classes
+    
+    def __repr__(self):
+        return "%s(%s)" % (self.__str__(), object.__repr__(self))
+    
+    def __add__(self, other):
+        result = ClassOperations()
+        result.classes = dict(self.classes)
+        for classname, other_class in other.classes.items():
+            result.cls(classname) # Make sure exists.
+            result.classes[classname] += other_class
+        return result
+    
+    def __sub__(self, other):
+        result = ClassOperations()
+        result.classes = dict(self.classes)
+        for classname, other_class in other.classes.items():
+            result.cls(classname) # Make sure exists.
+            result.classes[classname] -= other_class
+        return result
+    
+class StorageEdge(object):
+    
+    def __init__(self, operation="None", origin=None, target=None):
+        self.operation = operation
+        self.classes = ClassOperations()
+        self.origin = origin
+        self.target = target
+        self.is_storage_source = False
+    
+    def full_key(self):
+        return (self.operation, self.origin.name, self.target.name)
+    
+    def cls(self, classname):
+        return self.classes.cls(classname)
+    
+    def total(self):
+        return self.classes.total()
+    
+    def notify_nodes(self):
+        self.origin.note_outgoing(self)
+        self.target.note_incoming(self)
+    
+    def add_log_entry(self, entry):
+        self.cls(entry.classname).add_log_entry(entry)
+        if entry.is_storage_source:
+            self.is_storage_source = True
+    
+    def as_log_entries(self):
+        entries = []
+        for classname, ops in self.classes.classes.items():
+            origin = None if self.is_storage_source else self.origin.name
+            entry = LogEntry(self.operation, origin, self.target.name, classname,
+                            ops.slots, ops.objects, ops.element_classnames, self.is_storage_source)
+            entries.append(entry)
+        return entries
+    
+    def __lt__(self, other):
+        return self.full_key() < other.full_key()
+    
+    def __str__(self):
+        return "[%s %s -> %s]" % (self.operation, self.origin, self.target)
+    
+    def __repr__(self):
+        return "%s(%s)" % (self.__str__(), object.__repr__(self))
+    
+    def __add__(self, other):
+        origin = self.origin if self.origin is not None else other.origin
+        target = self.target if self.target is not None else other.target
+        result = StorageEdge(self.operation, origin, target)
+        result.classes += self.classes + other.classes
+        return result
+    
+    def __sub__(self, other):
+        origin = self.origin if self.origin is not None else other.origin
+        target = self.target if self.target is not None else other.target
+        result = StorageEdge(self.operation, origin, target)
+        result.classes += self.classes - other.classes
+        return result
+    
+class StorageNode(object):
+    
+    def __init__(self, name):
+        self.name = name
+        self.incoming = set()
+        self.outgoing = set()
+    
+    def note_incoming(self, edge):
+        assert edge.target is self
+        if edge not in self.incoming:
+            self.incoming.add(edge)
+        
+    def note_outgoing(self, edge):
+        assert edge.origin is self
+        if edge not in self.outgoing:
+            self.outgoing.add(edge)
+        
+    def incoming_edges(self, operation):
+        return filter(lambda x: x.operation == operation, self.incoming)
+    
+    def outgoing_edges(self, operation):
+        return filter(lambda x: x.operation == operation, self.outgoing)
+    
+    def sum_incoming(self, operation):
+        return reduce(operator.add, self.incoming_edges(operation), StorageEdge(operation))
+        
+    def sum_outgoing(self, operation):
+        return reduce(operator.add, self.outgoing_edges(operation), StorageEdge(operation))
+    
+    def sum_all_incoming(self):
+        return reduce(operator.add, self.incoming, StorageEdge())
+    
+    def sum_all_outgoing(self):
+        return reduce(operator.add, self.outgoing, StorageEdge())
+    
+    def __str__(self):
+        return self.name
+    
+    def __repr__(self):
+        return "%s(%s)" % (self.__str__(), object.__repr__(self))
+    
+    def merge_edge_sets(self, set1, set2, key_slot):
+        getter = lambda edge: edge.__dict__[key_slot]
+        set_dict = dict([(getter(edge), edge) for edge in set1])
+        for edge in set2:
+            key = getter(edge)
+            if key not in set_dict:
+                set_dict[key] = edge
+            else:
+                set_dict[key] += edge
+        return set(set_dict.values())
+    
+    def __add__(self, other):
+        result = StorageNode("%s %s" % (self.name, other.name))
+        result.incoming = self.merge_edge_sets(self.incoming, other.incoming, "origin")
+        # TODO bad code
+        for edge in result.incoming:
+            edge.target = result
+        result.outgoing = self.merge_edge_sets(self.outgoing, other.outgoing, "target")
+        for edge in result.outgoing:
+            edge.origin = result
+        return result
+    
+    def __lt__(self, other):
+        return self.name < other.name
+    
+    def is_artificial(self):
+        for outgoing in self.outgoing:
+            if outgoing.is_storage_source:
+                return True
+        return False
+    
+    def is_storage_node(self):
+        return self.is_artificial() or self.name in STORAGE_NODES
+    
+    def dot_name(self):
+        return self.name.replace(" ", "_")
+    
+class StorageGraph(object):
+    
+    def __init__(self):
+        self.nodes = {}
+        self.edges = {}
+        self.operations = set()
+    
+    def node(self, name):
+        if name not in self.nodes:
+            self.nodes[name] = StorageNode(name)
+        return self.nodes[name]
+    
+    def assert_sanity(self):
+        visited_edges = set()
+        for node in self.nodes.values():
+            for edge in node.incoming:
+                assert edge in self.edges.values(), "Edge not in graph's edges: %s" % edge
+                visited_edges.add(edge)
+                if not edge.target is node:
+                    print "Wrong edge target: %s\nIncoming edge: %s\nIn node: %s" % (edge.target, edge, node)
+                    assert False
+                if not edge in edge.origin.outgoing:
+                    print "Edge not in origin's outgoing: %s\nIncoming edge: %s\nIn node: %s" % (edge.origin.outgoing, edge, node)
+                    assert False
+            for edge in node.outgoing:
+                assert edge in self.edges.values(), "Edge not in graph's edges: %s" % edge
+                visited_edges.add(edge)
+                if not edge.origin is node:
+                    print "Wrong edge origin: %s\nOutgoing edge: %s\nIn node: %s" % (edge.origin, edge, node)
+                    assert False
+                if not edge in edge.target.incoming:
+                    print "Edge not in origin's incoming: %s\nOutgoing edge: %s\nIn node: %s" % (edge.target.incoming, edge, node)
+                    assert False
+        assert len(visited_edges) == len(self.edges.values()), "Not all of graph's edges visited."
+    
+    def add_log_entry(self, log_entry):
+        self.operations.add(log_entry.operation)
+        key = log_entry.full_key()
+        if key not in self.edges:
+            edge = StorageEdge(log_entry.operation, self.node(log_entry.old_storage), self.node(log_entry.new_storage))
+            self.edges[key] = edge
+            edge.notify_nodes()
+        self.edges[key].add_log_entry(log_entry)
+    
+    def collapse_nodes(self, collapsed_nodes, new_name=None):
+        if len(collapsed_nodes) == 0:
+            return
+        for node in collapsed_nodes:
+            del self.nodes[node.name]
+            for edge in node.incoming:
+                del self.edges[edge.full_key()]
+            for edge in node.outgoing:
+                del self.edges[edge.full_key()]
+        new_node = reduce(operator.add, collapsed_nodes)
+        if new_name is not None:
+            new_node.name = new_name
+        self.nodes[new_node.name] = new_node
+        # TODO bad code
+        for node in collapsed_nodes:
+            for edge in node.incoming:
+                edge.origin.outgoing.remove(edge)
+                new_edges = filter(lambda filtered: filtered.origin == edge.origin, new_node.incoming)
+                assert len(new_edges) == 1
+                edge.origin.outgoing.add(new_edges[0])
+            for edge in node.outgoing:
+                edge.target.incoming.remove(edge)
+                new_edges = filter(lambda filtered: filtered.target == edge.target, new_node.outgoing)
+                assert len(new_edges) == 1
+                edge.target.incoming.add(new_edges[0])
+        for edge in new_node.incoming:
+            self.edges[edge.full_key()] = edge
+        for edge in new_node.outgoing:
+            self.edges[edge.full_key()] = edge
+        self.assert_sanity()
+    
+    def collapse_nonstorage_nodes(self, new_name=None):
+        nodes = filter(lambda x: not x.is_storage_node(), self.nodes.values())
+        self.collapse_nodes(nodes, new_name)
+    
+    def sorted_nodes(self):
+        nodes = self.nodes.values()
+        nodes.sort()
+        return nodes
+    
+def make_graph(logfile, flags):
+    graph = StorageGraph()
+    def callback(entry):
+        graph.add_log_entry(entry)
+    parse(logfile, flags, callback)
+    graph.assert_sanity()
+    return graph
+
+# ====================================================================
+# ======== Command - Summarize log content
+# ====================================================================
+
+def command_summarize(logfile, flags):
+    graph = make_graph(logfile, flags)
+    if not flags.allstorage:
+        graph.collapse_nonstorage_nodes()
+    for node in graph.sorted_nodes():
+        node.print_summary(flags, graph.operations)
+
+def StorageNode_print_summary(self, flags, all_operations):
+    print "\n%s:" % self.name
+    sum = StorageEdge()
+    total_incoming = self.sum_all_incoming().total() if flags.percent else None
+    
+    print "\tIncoming:"
+    for operation in all_operations:
+        if flags.detailed:
+            edges = [ (edge.origin.name, edge) for edge in self.incoming_edges(operation) ]
+        else:
+            edges = [ (operation, self.sum_incoming(operation)) ]
+        for edgename, edge in edges:
+            edge.print_with_name("\t\t\t", edgename, total_incoming, flags)
+            sum += edge
+    
+    print "\tOutgoing:"
+    for operation in all_operations:
+        if flags.detailed:
+            edges = [ (edge.target.name, edge) for edge in self.outgoing_edges(operation) ]
+        else:
+            edges = [ (operation, self.sum_outgoing(operation)) ]
+        for edgename, edge in edges:
+            edge.print_with_name("\t\t\t", edgename, total_incoming, flags)
+            sum -= edge
+    
+    sum.print_with_name("\t", "Remaining", total_incoming, flags)
+
+StorageNode.print_summary = StorageNode_print_summary
+
+def StorageEdge_print_with_name(self, prefix, edgename, total_reference, flags):
+    if flags.classes:   
+        print "%s%s:" % (prefix, edgename)
+        prefix += "\t\t"
+        operations = self.classes.classes.items()
+        operations.sort(reverse=True, key=operator.itemgetter(1))
+    else:
+        operations = [ (edgename, self.total()) ]
+    for classname, classops in operations:
+        classops.prefixprint("%s%s: " % (prefix, classname), total_reference)
+    
+StorageEdge.print_with_name = StorageEdge_print_with_name
+
+# ====================================================================
+# ======== Command - DOT output
+# ====================================================================
+
+# Output is valid dot code and can be parsed by the graphviz dot utility.
+def command_print_dot(logfile, flags):
+    graph = make_graph(logfile, flags)
+    print "/*"
+    print "Storage Statistics (dot format):"
+    print "================================"
+    print "*/"
+    print dot_string(graph, flags)
+
+def run_dot(logfile, flags, output_type):
+    import subprocess
+    dot = dot_string(make_graph(logfile, flags), flags)
+    command = ["dot", "-T%s" % output_type, "-o%s.%s" % (flags.logfile, output_type)]
+    print "Running:\n%s" % " ".join(command)
+    p = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    output = p.communicate(input=dot)[0]
+    print output
+
+def command_dot(logfile, flags):
+    run_dot(logfile, flags, "jpg")
+def command_dot_ps(logfile, flags):
+    run_dot(logfile, flags, "ps")
+def command_dot_pdf(logfile, flags):
+    run_dot(logfile, flags, "pdf")
+def command_dot_svg(logfile, flags):
+    run_dot(logfile, flags, "svg")
+
+def dot_string(graph, flags):
+    result = "digraph G {"
+    incoming_cache = {}
+    if not flags.allstorage:
+        graph.collapse_nonstorage_nodes("Other")
+    
+    def make_label(edge, prefix="", total_edge=None, slots_per_object=False):
+        object_suffix = " objects"
+        slots_suffix = " slots"
+        if not flags.objects or not flags.slots:
+            object_suffix = slots_suffix = ""
+        if total_edge and flags.percent and total_edge.objects != 0:
+            percent_objects = " (%.1f%%)" % percent(edge.objects, total_edge.objects)
+            percent_slots = " (%.1f%%)" % percent(edge.slots, total_edge.slots)
+        else:
+            percent_objects = percent_slots = ""
+        label = ""
+        if flags.objects:
+            label += "%s%s%s%s<BR/>" % (prefix, format(edge.objects, ",.0f"), object_suffix, percent_objects)
+        if flags.slots:
+            label += "%s%s%s%s<BR/>" % (prefix, format(edge.slots, ",.0f"), slots_suffix, percent_slots)
+        if slots_per_object and flags.slotsPerObject:
+            label += "%.1f slots/object<BR/>" % (float(total.slots) / total.objects)
+        return label
+    
+    for node in graph.nodes.values():
+        incoming = node.sum_all_incoming().total()
+        outgoing = node.sum_all_outgoing().total()
+        remaining = incoming - outgoing
+        if node.is_artificial():
+            incoming_cache[node.name] = outgoing
+            shape = ",shape=box"
+            label = make_label(outgoing)
+        else:
+            incoming_cache[node.name] = incoming
+            shape = ""
+            label = make_label(incoming, "Incoming: ")
+            if remaining.objects != incoming.objects:
+                label += make_label(remaining, "Remaining: ", incoming)
+        result += "%s [label=<<B><U>%s</U></B><BR/>%s>%s];" % (node.dot_name(), node.name, label, shape)
+    
+    for edge in graph.edges.values():
+        total = edge.total()
+        incoming = incoming_cache[edge.origin.name]
+        label = make_label(total, "", incoming, slots_per_object=True)
+        target_node = edge.target.dot_name()
+        source_node = edge.origin.dot_name()
+        result += "%s -> %s [label=<%s>];" % (source_node, target_node, label)
+    
+    result += "}"
+    return result
+
+# ====================================================================
+# ======== Other commands
+# ====================================================================
+
+def command_aggregate(logfile, flags):
+    graph = make_graph(logfile, flags)
+    edges = graph.edges.values()
+    edges.sort()
+    for edge in edges:
+        logentries = edge.as_log_entries()
+        logentries.sort()
+        for entry in logentries:
+            print entry
+
+def command_print_entries(logfile, flags):
+    def callback(entry):
+        print entry
+    parse(logfile, flags, callback)
+
+# ====================================================================
+# ======== Main
+# ====================================================================
+
+class Flags(object):
+    
+    def __init__(self, flags):
+        self.flags = {}
+        for name, short in flags:
+            self.__dict__[name] = False
+            self.flags[short] = name
+    
+    def handle(self, arg):
+        if arg in self.flags:
+            self.__dict__[self.flags[arg]] = True
+            return True
+        else:
+            return False
+    
+    def __str__(self):
+        descriptions = [ ("%s (%s)" % description) for description in self.flags.items() ]
+        return "[%s]" % " | ".join(descriptions)
+    
+def usage(flags, commands):
+    print "Arguments: logfile command %s" % flags
+    print "Available commands: %s" % commands
+    exit(1)
+
+def main(argv):
+    flags = Flags([
+        # General
+        ('verbose', '-v'),
+        
+        # All outputs
+        ('percent', '-p'),
+        ('allstorage', '-a'),
+        
+        # Text outputs
+        ('detailed', '-d'),
+        ('classes', '-c'),
+        
+        # dot outputs
+        ('slots', '-s'),
+        ('objects', '-o'),
+        ('slotsPerObject', '-S'),
+    ])
+    
+    command_prefix = "command_"
+    module = sys.modules[__name__].__dict__
+    commands = [ a[len(command_prefix):] for a in module.keys() if a.startswith(command_prefix) ]
+    
+    if len(argv) < 2:
+        usage(flags, commands)
+    logfile = argv[0]
+    flags.logfile = logfile
+    configure_vm(logfile, flags)
+    command = argv[1]
+    for flag in argv[2:]:
+        if not flags.handle(flag):
+            usage(flags, commands)
+    if command not in commands:
+        usage(flags, commands)
+    
+    func = module[command_prefix + command]
+    func(logfile, flags)
+
+if __name__ == "__main__":
+    main(sys.argv[1:])
diff --git a/rpython/rlib/rstrategies/rstrategies.py b/rpython/rlib/rstrategies/rstrategies.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rstrategies/rstrategies.py
@@ -0,0 +1,572 @@
+
+import weakref, sys
+from rpython.rlib.rstrategies import logger
+from rpython.rlib import jit, objectmodel, rerased
+from rpython.rlib.objectmodel import specialize
+
+def make_accessors(strategy='strategy', storage='storage'):
+    """
+    Instead of using this generator, the methods can be implemented manually.
+    A third way is to overwrite the getter/setter methods in StrategyFactory.
+    """
+    def make_getter(attr):
+        def getter(self): return getattr(self, attr)
+        return getter
+    def make_setter(attr):
+        def setter(self, val): setattr(self, attr, val)
+        return setter
+    classdef = sys._getframe(1).f_locals
+    classdef['_get_strategy'] = make_getter(strategy)
+    classdef['_set_strategy'] = make_setter(strategy)
+    classdef['_get_storage'] = make_getter(storage)
+    classdef['_set_storage'] = make_setter(storage)
+
+class StrategyMetaclass(type):
+    """
+    A metaclass is required, because we need certain attributes to be special
+    for every single strategy class.
+    """
+    def __new__(self, name, bases, attrs):
+        attrs['_is_strategy'] = False
+        attrs['_is_singleton'] = False
+        attrs['_specializations'] = []
+        # Not every strategy uses rerased-pairs, but they won't hurt
+        erase, unerase = rerased.new_erasing_pair(name)
+        def get_storage(self, w_self):
+            erased = self.strategy_factory().get_storage(w_self)
+            return unerase(erased)
+        def set_storage(self, w_self, storage):
+            erased = erase(storage)
+            self.strategy_factory().set_storage(w_self, erased)
+        attrs['get_storage'] = get_storage
+        attrs['set_storage'] = set_storage
+        return type.__new__(self, name, bases, attrs)
+    
+def strategy(generalize=None, singleton=True):
+    """
+    Strategy classes must be decorated with this.
+    generalize is a list of other strategies, that can be switched to from the decorated strategy.
+    If the singleton flag is set to False, new strategy instances will be created,
+    instead of always reusing the singleton object.
+    """
+    def decorator(strategy_class):
+        # Patch strategy class: Add generalized_strategy_for and mark as strategy class.
+        if generalize:
+            @jit.unroll_safe
+            def generalized_strategy_for(self, value):
+                # TODO - optimize this method
+                for strategy in generalize:
+                    if self.strategy_factory().strategy_singleton_instance(strategy)._check_can_handle(value):
+                        return strategy
+                raise Exception("Could not find generalized strategy for %s coming from %s" % (value, self))
+            strategy_class.generalized_strategy_for = generalized_strategy_for
+            for generalized in generalize:
+                generalized._specializations.append(strategy_class)
+        strategy_class._is_strategy = True
+        strategy_class._generalizations = generalize
+        strategy_class._is_singleton = singleton
+        return strategy_class
+    return decorator
+
+class StrategyFactory(object):
+    _immutable_fields_ = ["strategies[*]", "logger", "strategy_singleton_field"]
+    factory_instance_counter = 0
+    
+    def __init__(self, root_class, all_strategy_classes=None):
+        if all_strategy_classes is None:
+            all_strategy_classes = self._collect_subclasses(root_class)
+        self.strategies = []
+        self.logger = logger.Logger()
+        
+        # This is to avoid confusion between multiple factories existing simultaneously (e.g. in tests)
+        self.strategy_singleton_field = "__singleton_%i" % StrategyFactory.factory_instance_counter
+        StrategyFactory.factory_instance_counter += 1
+        
+        self._create_strategy_instances(root_class, all_strategy_classes)
+    
+    def _create_strategy_instances(self, root_class, all_strategy_classes):
+        for strategy_class in all_strategy_classes:
+            if strategy_class._is_strategy:
+                setattr(strategy_class, self.strategy_singleton_field, self.instantiate_strategy(strategy_class))
+                self.strategies.append(strategy_class)
+            self._patch_strategy_class(strategy_class, root_class)
+        self._order_strategies()
+    
+    # =============================
+    # API methods
+    # =============================
+    
+    def switch_strategy(self, w_self, new_strategy_type, new_element=None):
+        """
+        Switch the strategy of w_self to the new type.
+        new_element can be given as as hint, purely for logging purposes.
+        It should be the object that was added to w_self, causing the strategy switch.
+        """
+        old_strategy = self.get_strategy(w_self)
+        if new_strategy_type._is_singleton:
+            new_strategy = self.strategy_singleton_instance(new_strategy_type)
+        else:
+            size = old_strategy.size(w_self)
+            new_strategy = self.instantiate_strategy(new_strategy_type, w_self, size)
+        self.set_strategy(w_self, new_strategy)
+        old_strategy._convert_storage_to(w_self, new_strategy)
+        new_strategy.strategy_switched(w_self)
+        self.log(w_self, new_strategy, old_strategy, new_element)
+        return new_strategy
+    
+    def set_initial_strategy(self, w_self, strategy_type, size, elements=None):
+        """
+        Initialize the strategy and storage fields of w_self.
+        This must be called before switch_strategy or any strategy method can be used.
+        elements is an optional list of values initially stored in w_self.
+        If given, then len(elements) == size must hold.
+        """
+        assert self.get_strategy(w_self) is None, "Strategy should not be initialized yet!"
+        if strategy_type._is_singleton:
+            strategy = self.strategy_singleton_instance(strategy_type)
+        else:
+            strategy = self.instantiate_strategy(strategy_type, w_self, size)
+        self.set_strategy(w_self, strategy)
+        strategy._initialize_storage(w_self, size)
+        element = None
+        if elements:
+            strategy.store_all(w_self, elements)
+            if len(elements) > 0: element = elements[0]
+        strategy.strategy_switched(w_self)
+        self.log(w_self, strategy, None, element)
+        return strategy
+    
+    @jit.unroll_safe
+    def strategy_type_for(self, objects):
+        """
+        Return the best-fitting strategy to hold all given objects.
+        """
+        specialized_strategies = len(self.strategies)
+        can_handle = [True] * specialized_strategies
+        for obj in objects:
+            if specialized_strategies <= 1:
+                break
+            for i, strategy in enumerate(self.strategies):
+                if can_handle[i] and not self.strategy_singleton_instance(strategy)._check_can_handle(obj):
+                    can_handle[i] = False
+                    specialized_strategies -= 1
+        for i, strategy_type in enumerate(self.strategies):
+            if can_handle[i]:
+                return strategy_type
+        raise Exception("Could not find strategy to handle: %s" % objects)
+    
+    def decorate_strategies(self, transitions):
+        """
+        As an alternative to decorating all strategies with @strategy,
+        invoke this in the constructor of your StrategyFactory subclass, before
+        calling __init__. transitions is a dict mapping all strategy classes to
+        their 'generalize' list parameter (see @strategy decorator).
+        """
+        "NOT_RPYTHON"
+        for strategy_class, generalized in transitions.items():
+            strategy(generalized)(strategy_class)
+    
+    # =============================
+    # The following methods can be overwritten to customize certain aspects of the factory.
+    # =============================
+    
+    def instantiate_strategy(self, strategy_type, w_self=None, initial_size=0):
+        """
+        Return a functional instance of strategy_type.
+        Overwrite this if you need a non-default constructor.
+        The two additional parameters should be ignored for singleton-strategies.
+        """
+        return strategy_type()
+    
+    def log(self, w_self, new_strategy, old_strategy=None, new_element=None):
+        """
+        This can be overwritten into a more appropriate call to self.logger.log
+        """
+        if not self.logger.active: return
+        new_strategy_str = self.log_string_for_object(new_strategy)
+        old_strategy_str = self.log_string_for_object(old_strategy)
+        element_typename = self.log_string_for_object(new_element)
+        size = new_strategy.size(w_self)
+        typename = ""
+        cause = "Switched" if old_strategy else "Created"
+        self.logger.log(new_strategy_str, size, cause, old_strategy_str, typename, element_typename)
+    
+    @specialize.call_location()
+    def log_string_for_object(self, obj):
+        """
+        This can be overwritten instead of the entire log() method.
+        Keep the specialize-annotation in order to handle different kinds of objects here.
+        """
+        return obj.__class__.__name__ if obj else ""
+    
+    # These storage accessors are specialized because the storage field is 
+    # populated by erased-objects which seem to be incompatible sometimes.
+    @specialize.call_location()
+    def get_storage(self, obj):
+        return obj._get_storage()
+    @specialize.call_location()
+    def set_storage(self, obj, val):
+        return obj._set_storage(val)
+    
+    def get_strategy(self, obj):
+        return obj._get_strategy()
+    def set_strategy(self, obj, val):
+        return obj._set_strategy(val)
+    
+    # =============================
+    # Internal methods
+    # =============================
+    
+    def _patch_strategy_class(self, strategy_class, root_class):
+        "NOT_RPYTHON"
+        # Patch root class: Add default handler for visitor
+        def _convert_storage_from_OTHER(self, w_self, previous_strategy):
+            self._convert_storage_from(w_self, previous_strategy)
+        funcname = "_convert_storage_from_" + strategy_class.__name__
+        _convert_storage_from_OTHER.func_name = funcname
+        setattr(root_class, funcname, _convert_storage_from_OTHER)
+        
+        # Patch strategy class: Add polymorphic visitor function
+        def _convert_storage_to(self, w_self, new_strategy):
+            getattr(new_strategy, funcname)(w_self, self)
+        strategy_class._convert_storage_to = _convert_storage_to
+    
+    def _collect_subclasses(self, cls):
+        "NOT_RPYTHON"
+        subclasses = []
+        for subcls in cls.__subclasses__():
+            subclasses.append(subcls)
+            subclasses.extend(self._collect_subclasses(subcls))
+        return subclasses
+    
+    def _order_strategies(self):
+        "NOT_RPYTHON"
+        def get_generalization_depth(strategy, visited=None):
+            if visited is None:
+                visited = set()
+            if strategy._generalizations:
+                if strategy in visited:
+                    raise Exception("Cycle in generalization-tree of %s" % strategy)
+                visited.add(strategy)
+                depth = 0
+                for generalization in strategy._generalizations:
+                    other_depth = get_generalization_depth(generalization, set(visited))
+                    depth = max(depth, other_depth)
+                return depth + 1
+            else:
+                return 0
+        self.strategies.sort(key=get_generalization_depth, reverse=True)
+    
+    @jit.elidable
+    def strategy_singleton_instance(self, strategy_class):
+        return getattr(strategy_class, self.strategy_singleton_field)
+    
+    def _freeze_(self):
+        # Instance will be frozen at compile time, making accesses constant.
+        # The constructor does meta stuff which is not possible after translation.
+        return True
+
+class AbstractStrategy(object):
+    """
+    == Required:
+    strategy_factory(self) - Access to StorageFactory
+    """
+    
+    def strategy_switched(self, w_self):
+        # Overwrite this method for a hook whenever the strategy
+        # of w_self was switched to self.
+        pass
+    
+    # Main Fixedsize API
+    
+    def store(self, w_self, index0, value):
+        raise NotImplementedError("Abstract method")
+    
+    def fetch(self, w_self, index0):
+        raise NotImplementedError("Abstract method")
+    
+    def size(self, w_self):
+        raise NotImplementedError("Abstract method")
+    
+    # Fixedsize utility methods
+    
+    def slice(self, w_self, start, end):
+        return [ self.fetch(w_self, i) for i in range(start, end)]
+    
+    def fetch_all(self, w_self):
+        return self.slice(w_self, 0, self.size(w_self))
+    
+    def store_all(self, w_self, elements):
+        for i, e in enumerate(elements):
+            self.store(w_self, i, e)
+    
+    # Main Varsize API
+    
+    def insert(self, w_self, index0, list_w):
+        raise NotImplementedError("Abstract method")
+    
+    def delete(self, w_self, start, end):
+        raise NotImplementedError("Abstract method")
+    
+    # Varsize utility methods
+    
+    def append(self, w_self, list_w):
+        self.insert(w_self, self.size(w_self), list_w)        
+    
+    def pop(self, w_self, index0):
+        e = self.fetch(w_self, index0)
+        self.delete(w_self, index0, index0+1)
+        return e
+
+    # Internal methods
+    
+    def _initialize_storage(self, w_self, initial_size):
+        raise NotImplementedError("Abstract method")
+    
+    def _check_can_handle(self, value):
+        raise NotImplementedError("Abstract method")
+    
+    def _convert_storage_to(self, w_self, new_strategy):
+        # This will be overwritten in _patch_strategy_class
+        new_strategy._convert_storage_from(w_self, self)
+    
+    @jit.unroll_safe
+    def _convert_storage_from(self, w_self, previous_strategy):
+        # This is a very unefficient (but most generic) way to do this.
+        # Subclasses should specialize.
+        storage = previous_strategy.fetch_all(w_self)
+        self._initialize_storage(w_self, previous_strategy.size(w_self))
+        for i, field in enumerate(storage):
+            self.store(w_self, i, field)
+    
+    def _generalize_for_value(self, w_self, value):
+        strategy_type = self.generalized_strategy_for(value)
+        new_instance = self.strategy_factory().switch_strategy(w_self, strategy_type, new_element=value)
+        return new_instance
+        
+    def _cannot_handle_store(self, w_self, index0, value):
+        new_instance = self._generalize_for_value(w_self, value)
+        new_instance.store(w_self, index0, value)
+        
+    def _cannot_handle_insert(self, w_self, index0, list_w):
+        # TODO - optimize. Prevent multiple generalizations and slicing done by callers.
+        new_strategy = self._generalize_for_value(w_self, list_w[0])
+        new_strategy.insert(w_self, index0, list_w)
+
+# ============== Special Strategies with no storage array ==============
+
+class EmptyStrategy(AbstractStrategy):
+    # == Required:
+    # See AbstractStrategy
+    
+    def _initialize_storage(self, w_self, initial_size):
+        assert initial_size == 0
+        self.set_storage(w_self, None)
+    def _convert_storage_from(self, w_self, previous_strategy):
+        self.set_storage(w_self, None)
+    def _check_can_handle(self, value):
+        return False
+    
+    def fetch(self, w_self, index0):
+        raise IndexError
+    def store(self, w_self, index0, value):
+        self._cannot_handle_store(w_self, index0, [value])
+    def insert(self, w_self, index0, list_w):
+        self._cannot_handle_insert(w_self, index0, list_w)
+    def delete(self, w_self, start, end):
+        self.check_index_range(w_self, start, end)
+    def size(self, w_self):
+        return 0
+
+class SingleValueStrategyStorage(object):
+    """Small container object for a size value."""
+    _attrs_ = ['size']
+    def __init__(self, size=0):
+        self.size = size
+
+class SingleValueStrategy(AbstractStrategy):
+    # == Required:
+    # See AbstractStrategy
+    # check_index_*(...) - use mixin SafeIndexingMixin or UnsafeIndexingMixin
+    # value(self) - the single value contained in this strategy. Should be constant.
+    
+    def _initialize_storage(self, w_self, initial_size):
+        storage_obj = SingleValueStrategyStorage(initial_size)
+        self.set_storage(w_self, storage_obj)
+    def _convert_storage_from(self, w_self, previous_strategy):
+        self._initialize_storage(w_self, previous_strategy.size(w_self))
+    def _check_can_handle(self, value):
+        return value is self.value()
+    
+    def fetch(self, w_self, index0):
+        self.check_index_fetch(w_self, index0)
+        return self.value()
+    def store(self, w_self, index0, value):
+        self.check_index_store(w_self, index0)
+        if self._check_can_handle(value):
+            return
+        self._cannot_handle_store(w_self, index0, value)
+    def delete(self, w_self, start, end):
+        self.check_index_range(w_self, start, end)
+        self.get_storage(w_self).size -= (end - start)
+    def size(self, w_self):
+        return self.get_storage(w_self).size
+    
+    @jit.unroll_safe
+    def insert(self, w_self, index0, list_w):
+        storage_obj = self.get_storage(w_self)
+        for i in range(len(list_w)):
+            if self._check_can_handle(list_w[i]):
+                storage_obj.size += 1
+            else:
+                self._cannot_handle_insert(w_self, index0 + i, list_w[i:])
+                return
+
+# ============== Basic strategies with storage ==============
+
+class StrategyWithStorage(AbstractStrategy):
+    # == Required:
+    # See AbstractStrategy
+    # check_index_*(...) - use mixin SafeIndexingMixin or UnsafeIndexingMixin
+    # default_value(self) - The value to be initially contained in this strategy
+    
+    def _initialize_storage(self, w_self, initial_size):
+        default = self._unwrap(self.default_value())
+        self.set_storage(w_self, [default] * initial_size)
+    
+    @jit.unroll_safe
+    def _convert_storage_from(self, w_self, previous_strategy):
+        size = previous_strategy.size(w_self)
+        new_storage = [ self._unwrap(previous_strategy.fetch(w_self, i))
+                        for i in range(size) ]
+        self.set_storage(w_self, new_storage)
+    
+    def store(self, w_self, index0, wrapped_value):
+        self.check_index_store(w_self, index0)
+        if self._check_can_handle(wrapped_value):
+            unwrapped = self._unwrap(wrapped_value)
+            self.get_storage(w_self)[index0] = unwrapped
+        else:
+            self._cannot_handle_store(w_self, index0, wrapped_value)
+    
+    def fetch(self, w_self, index0):
+        self.check_index_fetch(w_self, index0)
+        unwrapped = self.get_storage(w_self)[index0]
+        return self._wrap(unwrapped)
+    
+    def _wrap(self, value):
+        raise NotImplementedError("Abstract method")
+    
+    def _unwrap(self, value):
+        raise NotImplementedError("Abstract method")
+    
+    def size(self, w_self):
+        return len(self.get_storage(w_self))
+    
+    @jit.unroll_safe
+    def insert(self, w_self, start, list_w):
+        # This is following Python's behaviour - insert automatically
+        # happens at the beginning of an array, even if index is larger
+        if start > self.size(w_self):
+            start = self.size(w_self)
+        for i in range(len(list_w)):
+            if self._check_can_handle(list_w[i]):
+                self.get_storage(w_self).insert(start + i, self._unwrap(list_w[i]))
+            else:
+                self._cannot_handle_insert(w_self, start + i, list_w[i:])
+                return
+    
+    def delete(self, w_self, start, end):
+        self.check_index_range(w_self, start, end)
+        assert start >= 0 and end >= 0
+        del self.get_storage(w_self)[start : end]
+        
+class GenericStrategy(StrategyWithStorage):
+    # == Required:
+    # See StrategyWithStorage
+    
+    def _wrap(self, value):
+        return value
+    def _unwrap(self, value):
+        return value
+    def _check_can_handle(self, wrapped_value):
+        return True
+    
+class WeakGenericStrategy(StrategyWithStorage):
+    # == Required:
+    # See StrategyWithStorage
+    
+    def _wrap(self, value):
+        return value() or self.default_value()
+    def _unwrap(self, value):
+        assert value is not None
+        return weakref.ref(value)
+    def _check_can_handle(self, wrapped_value):
+        return True
+    
+# ============== Mixins for index checking operations ==============
+
+class SafeIndexingMixin(object):
+    def check_index_store(self, w_self, index0):
+        self.check_index(w_self, index0)
+    def check_index_fetch(self, w_self, index0):
+        self.check_index(w_self, index0)
+    def check_index_range(self, w_self, start, end):
+        if end < start:
+            raise IndexError
+        self.check_index(w_self, start)
+        self.check_index(w_self, end)
+    def check_index(self, w_self, index0):
+        if index0 < 0 or index0 >= self.size(w_self):
+            raise IndexError
+
+class UnsafeIndexingMixin(object):
+    def check_index_store(self, w_self, index0):
+        pass
+    def check_index_fetch(self, w_self, index0):
+        pass
+    def check_index_range(self, w_self, start, end):
+        pass
+
+# ============== Specialized Storage Strategies ==============
+
+class SpecializedStrategy(StrategyWithStorage):
+    # == Required:
+    # See StrategyWithStorage
+    # wrap(self, value) - Return a boxed object for the primitive value
+    # unwrap(self, value) - Return the unboxed primitive value of value
+    
+    def _unwrap(self, value):
+        return self.unwrap(value)
+    def _wrap(self, value):
+        return self.wrap(value)
+    
+class SingleTypeStrategy(SpecializedStrategy):
+    # == Required Functions:
+    # See SpecializedStrategy
+    # contained_type - The wrapped type that can be stored in this strategy
+    
+    def _check_can_handle(self, value):
+        return isinstance(value, self.contained_type)
+    
+class TaggingStrategy(SingleTypeStrategy):
+    """This strategy uses a special tag value to represent a single additional object."""
+    # == Required:
+    # See SingleTypeStrategy
+    # wrapped_tagged_value(self) - The tagged object
+    # unwrapped_tagged_value(self) - The unwrapped tag value representing the tagged object
+    
+    def _check_can_handle(self, value):
+        return value is self.wrapped_tagged_value() or \
+                (isinstance(value, self.contained_type) and \
+                self.unwrap(value) != self.unwrapped_tagged_value())
+    
+    def _unwrap(self, value):
+        if value is self.wrapped_tagged_value():
+            return self.unwrapped_tagged_value()
+        return self.unwrap(value)
+    
+    def _wrap(self, value):
+        if value == self.unwrapped_tagged_value():
+            return self.wrapped_tagged_value()
+        return self.wrap(value)
diff --git a/rpython/rlib/rstrategies/test/test_rstrategies.py b/rpython/rlib/rstrategies/test/test_rstrategies.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rstrategies/test/test_rstrategies.py
@@ -0,0 +1,552 @@
+
+import py
+from rpython.rlib.rstrategies import rstrategies as rs
+from rpython.rlib.objectmodel import import_from_mixin
+
+# === Define small model tree
+
+class W_AbstractObject(object):
+    pass
+
+class W_Object(W_AbstractObject):
+    pass
+
+class W_Integer(W_AbstractObject):
+    def __init__(self, value):
+        self.value = value
+    def __eq__(self, other):
+        return isinstance(other, W_Integer) and self.value == other.value
+
+class W_List(W_AbstractObject):
+    rs.make_accessors()
+    def __init__(self, strategy=None, size=0, elements=None):
+        self.strategy = None
+        if strategy:
+            factory.set_initial_strategy(self, strategy, size, elements)
+    def fetch(self, i):
+        assert self.strategy
+        return self.strategy.fetch(self, i)
+    def store(self, i, value):
+        assert self.strategy
+        return self.strategy.store(self, i, value)
+    def size(self):
+        assert self.strategy
+        return self.strategy.size(self)
+    def insert(self, index0, list_w):
+        assert self.strategy
+        return self.strategy.insert(self, index0, list_w)
+    def delete(self, start, end):
+        assert self.strategy
+        return self.strategy.delete(self, start, end)
+    def append(self, list_w):
+        assert self.strategy
+        return self.strategy.append(self, list_w)
+    def pop(self, index0):
+        assert self.strategy
+        return self.strategy.pop(self, index0)
+    def slice(self, start, end):
+        assert self.strategy
+        return self.strategy.slice(self, start, end)
+    def fetch_all(self):
+        assert self.strategy
+        return self.strategy.fetch_all(self)
+    def store_all(self, elements):
+        assert self.strategy
+        return self.strategy.store_all(self, elements)
+
+w_nil = W_Object()
+
+# === Define concrete strategy classes
+
+class AbstractStrategy(object):
+    __metaclass__ = rs.StrategyMetaclass
+    import_from_mixin(rs.AbstractStrategy)
+    import_from_mixin(rs.SafeIndexingMixin)
+    def __init__(self, factory, w_self=None, size=0):
+        self.factory = factory
+    def strategy_factory(self):
+        return self.factory
+
+class Factory(rs.StrategyFactory):
+    switching_log = []
+    
+    def __init__(self, root_class):
+        self.decorate_strategies({
+            EmptyStrategy: [NilStrategy, IntegerStrategy, IntegerOrNilStrategy, GenericStrategy],
+            NilStrategy: [IntegerOrNilStrategy, GenericStrategy],
+            GenericStrategy: [],
+            IntegerStrategy: [IntegerOrNilStrategy, GenericStrategy],
+            IntegerOrNilStrategy: [GenericStrategy],
+        })
+        rs.StrategyFactory.__init__(self, root_class)
+    
+    def instantiate_strategy(self, strategy_type, w_self=None, size=0):
+        return strategy_type(self, w_self, size)
+    
+    def set_strategy(self, w_list, strategy): 
+        old_strategy = self.get_strategy(w_list)
+        self.switching_log.append((old_strategy, strategy))
+        super(Factory, self).set_strategy(w_list, strategy)
+    
+    def clear_log(self):
+        del self.switching_log[:]
+
+class EmptyStrategy(AbstractStrategy):
+    import_from_mixin(rs.EmptyStrategy)
+    # TODO - implement and test transition from Generic back to Empty
+
+class NilStrategy(AbstractStrategy):
+    import_from_mixin(rs.SingleValueStrategy)
+    def value(self): return w_nil
+
+class GenericStrategy(AbstractStrategy):
+    import_from_mixin(rs.GenericStrategy)
+    import_from_mixin(rs.UnsafeIndexingMixin)
+    def default_value(self): return w_nil
+
+class WeakGenericStrategy(AbstractStrategy):
+    import_from_mixin(rs.WeakGenericStrategy)
+    def default_value(self): return w_nil
+    
+class IntegerStrategy(AbstractStrategy):
+    import_from_mixin(rs.SingleTypeStrategy)
+    contained_type = W_Integer
+    def wrap(self, value): return W_Integer(value)
+    def unwrap(self, value): return value.value
+    def default_value(self): return W_Integer(0)
+
+class IntegerOrNilStrategy(AbstractStrategy):
+    import_from_mixin(rs.TaggingStrategy)
+    contained_type = W_Integer
+    def wrap(self, value): return W_Integer(value)
+    def unwrap(self, value): return value.value
+    def default_value(self): return w_nil
+    def wrapped_tagged_value(self): return w_nil
+    def unwrapped_tagged_value(self): import sys; return sys.maxint
+    
+ at rs.strategy(generalize=[], singleton=False)
+class NonSingletonStrategy(GenericStrategy):
+    def __init__(self, factory, w_list=None, size=0):
+        super(NonSingletonStrategy, self).__init__(factory, w_list, size)
+        self.w_list = w_list
+        self.the_size = size
+
+class NonStrategy(NonSingletonStrategy):
+    pass
+
+ at rs.strategy(generalize=[])
+class InefficientStrategy(GenericStrategy):
+    def _convert_storage_from(self, w_self, previous_strategy):
+        return AbstractStrategy._convert_storage_from(self, w_self, previous_strategy)
+
+factory = Factory(AbstractStrategy)
+
+def check_contents(list, expected):
+    assert list.size() == len(expected)
+    for i, val in enumerate(expected):
+        assert list.fetch(i) == val
+
+def teardown():
+    factory.clear_log()
+
+# === Test Initialization and fetch
+
+def test_setup():
+    pass
+
+def test_factory_setup():
+    expected_strategies = 7
+    assert len(factory.strategies) == expected_strategies
+    assert len(set(factory.strategies)) == len(factory.strategies)
+    for strategy in factory.strategies:
+        assert isinstance(factory.strategy_singleton_instance(strategy), strategy)
+
+def test_factory_setup_singleton_instances():
+    new_factory = Factory(AbstractStrategy)
+    s1 = factory.strategy_singleton_instance(GenericStrategy)
+    s2 = new_factory.strategy_singleton_instance(GenericStrategy)
+    assert s1 is not s2
+    assert s1.strategy_factory() is factory
+    assert s2.strategy_factory() is new_factory
+
+def test_metaclass():
+    assert NonStrategy._is_strategy == False
+    assert IntegerOrNilStrategy._is_strategy == True
+    assert IntegerOrNilStrategy._is_singleton == True
+    assert NonSingletonStrategy._is_singleton == False
+    assert NonStrategy._is_singleton == False
+    assert NonStrategy.get_storage is not NonSingletonStrategy.get_storage
+
+def test_singletons():
+    def do_test_singletons(cls, expected_true):
+        l1 = W_List(cls, 0)
+        l2 = W_List(cls, 0)
+        if expected_true:
+            assert l1.strategy is l2.strategy
+        else:
+            assert l1.strategy is not l2.strategy
+    do_test_singletons(EmptyStrategy, True)
+    do_test_singletons(NonSingletonStrategy, False)
+    do_test_singletons(NonStrategy, False)
+    do_test_singletons(GenericStrategy, True)
+
+def do_test_initialization(cls, default_value=w_nil, is_safe=True):
+    size = 10
+    l = W_List(cls, size)
+    s = l.strategy
+    assert s.size(l) == size
+    assert s.fetch(l,0) == default_value
+    assert s.fetch(l,size/2) == default_value
+    assert s.fetch(l,size-1) == default_value
+    py.test.raises(IndexError, s.fetch, l, size)
+    py.test.raises(IndexError, s.fetch, l, size+1)
+    py.test.raises(IndexError, s.fetch, l, size+5)
+    if is_safe:
+        py.test.raises(IndexError, s.fetch, l, -1)
+    else:
+        assert s.fetch(l, -1) == s.fetch(l, size - 1)
+
+def test_init_Empty():
+    l = W_List(EmptyStrategy, 0)
+    s = l.strategy
+    assert s.size(l) == 0
+    py.test.raises(IndexError, s.fetch, l, 0)
+    py.test.raises(IndexError, s.fetch, l, 10)
+    py.test.raises(IndexError, s.delete, l, 0, 1)
+    py.test.raises(AssertionError, W_List, EmptyStrategy, 2) # Only size 0 possible.
+    
+def test_init_Nil():
+    do_test_initialization(NilStrategy)
+
+def test_init_Generic():
+    do_test_initialization(GenericStrategy, is_safe=False)
+    
+def test_init_WeakGeneric():
+    do_test_initialization(WeakGenericStrategy)
+    
+def test_init_Integer():
+    do_test_initialization(IntegerStrategy, default_value=W_Integer(0))
+    
+def test_init_IntegerOrNil():
+    do_test_initialization(IntegerOrNilStrategy)
+    
+# === Test Simple store
+
+def do_test_store(cls, stored_value=W_Object(), is_safe=True, is_varsize=False):
+    size = 10
+    l = W_List(cls, size)
+    s = l.strategy
+    def store_test(index):
+        s.store(l, index, stored_value)
+        assert s.fetch(l, index) == stored_value
+    store_test(0)
+    store_test(size/2)
+    store_test(size-1)
+    if not is_varsize:
+        py.test.raises(IndexError, s.store, l, size, stored_value)
+        py.test.raises(IndexError, s.store, l, size+1, stored_value)
+        py.test.raises(IndexError, s.store, l, size+5, stored_value)
+    if is_safe:
+        py.test.raises(IndexError, s.store, l, -1, stored_value)
+    else:
+        store_test(-1)
+
+def test_store_Nil():
+    do_test_store(NilStrategy, stored_value=w_nil)
+
+def test_store_Generic():
+    do_test_store(GenericStrategy, is_safe=False)
+    
+def test_store_WeakGeneric():
+    do_test_store(WeakGenericStrategy, stored_value=w_nil)
+    
+def test_store_Integer():
+    do_test_store(IntegerStrategy, stored_value=W_Integer(100))
+    
+def test_store_IntegerOrNil():
+    do_test_store(IntegerOrNilStrategy, stored_value=W_Integer(100))
+    do_test_store(IntegerOrNilStrategy, stored_value=w_nil)
+
+# === Test Insert
+
+def do_test_insert(cls, values):
+    l = W_List(cls, 0)
+    assert len(values) >= 6
+    values0 = values[0:1]
+    values1 = values[1:2]
+    values2 = values[2:4]
+    values3 = values[4:6]
+    l.insert(3, values0) # Will still be inserted at the very beginning
+    check_contents(l, values0)
+    l.insert(1, values1+values3)
+    check_contents(l, values0+values1+values3)
+    l.insert(2, values2)
+    check_contents(l, values)
+    return l
+
+def test_insert_Nil():
+    do_test_insert(NilStrategy, [w_nil]*6)
+
+def test_insert_Generic():
+    do_test_insert(GenericStrategy, [W_Object() for _ in range(6)])
+    
+def test_insert_WeakGeneric():
+    do_test_insert(WeakGenericStrategy, [W_Object() for _ in range(6)])
+    
+def test_insert_Integer():
+    do_test_insert(IntegerStrategy, [W_Integer(x) for x in range(6)])
+    
+def test_insert_IntegerOrNil():
+    do_test_insert(IntegerOrNilStrategy, [w_nil]+[W_Integer(x) for x in range(4)]+[w_nil])
+    do_test_insert(IntegerOrNilStrategy, [w_nil]*6)
+    
+# === Test Delete
+
+def do_test_delete(cls, values, indexing_unsafe=False):
+    assert len(values) >= 6
+    l = W_List(cls, len(values), values)
+    if not indexing_unsafe:
+        py.test.raises(IndexError, l.delete, 2, 1)
+    l.delete(2, 4)
+    del values[2: 4]
+    check_contents(l, values)
+    l.delete(1, 2)
+    del values[1: 2]
+    check_contents(l, values)
+
+def test_delete_Nil():
+    do_test_delete(NilStrategy, [w_nil]*6)
+
+def test_delete_Generic():
+    do_test_delete(GenericStrategy, [W_Object() for _ in range(6)], indexing_unsafe=True)
+    
+def test_delete_WeakGeneric():
+    do_test_delete(WeakGenericStrategy, [W_Object() for _ in range(6)])
+    
+def test_delete_Integer():
+    do_test_delete(IntegerStrategy, [W_Integer(x) for x in range(6)])
+    
+def test_delete_IntegerOrNil():
+    do_test_delete(IntegerOrNilStrategy, [w_nil]+[W_Integer(x) for x in range(4)]+[w_nil])
+    do_test_delete(IntegerOrNilStrategy, [w_nil]*6)
+
+# === Test Transitions
+
+def test_CheckCanHandle():
+    def assert_handles(cls, good, bad):
+        s = cls(0)
+        for val in good:
+            assert s._check_can_handle(val)
+        for val in bad:
+            assert not s._check_can_handle(val)
+    obj = W_Object()
+    i = W_Integer(0)
+    nil = w_nil
+    
+    assert_handles(EmptyStrategy, [], [nil, obj, i])
+    assert_handles(NilStrategy, [nil], [obj, i])
+    assert_handles(GenericStrategy, [nil, obj, i], [])
+    assert_handles(WeakGenericStrategy, [nil, obj, i], [])
+    assert_handles(IntegerStrategy, [i], [nil, obj])
+    assert_handles(IntegerOrNilStrategy, [nil, i], [obj])
+
+def do_test_transition(OldStrategy, value, NewStrategy, initial_size=10):
+    w = W_List(OldStrategy, initial_size)
+    old = w.strategy
+    w.store(0, value)
+    assert isinstance(w.strategy, NewStrategy)
+    assert factory.switching_log == [(None, old), (old, w.strategy)]
+
+def test_AllNil_to_Generic():
+    do_test_transition(NilStrategy, W_Object(), GenericStrategy)
+
+def test_AllNil_to_IntegerOrNil():
+    do_test_transition(NilStrategy, W_Integer(0), IntegerOrNilStrategy)
+
+def test_IntegerOrNil_to_Generic():
+    do_test_transition(IntegerOrNilStrategy, W_Object(), GenericStrategy)
+
+def test_Integer_to_IntegerOrNil():
+    do_test_transition(IntegerStrategy, w_nil, IntegerOrNilStrategy)
+
+def test_Generic_to_AllNil():
+    w = W_List(GenericStrategy, 5)
+    old = w.strategy
+    factory.switch_strategy(w, NilStrategy)
+    assert isinstance(w.strategy, NilStrategy)
+    assert factory.switching_log == [(None, old), (old, w.strategy)]
+
+def test_Integer_Generic():
+    do_test_transition(IntegerStrategy, W_Object(), GenericStrategy)
+
+def test_TaggingValue_not_storable():
+    tag = IntegerOrNilStrategy(10).unwrapped_tagged_value() # sys.maxint
+    do_test_transition(IntegerOrNilStrategy, W_Integer(tag), GenericStrategy)
+
+def test_insert_StrategySwitch_IntOrNil():
+    o = W_Object()
+    l = do_test_insert(IntegerOrNilStrategy, [W_Integer(1), w_nil, o, o, w_nil, W_Integer(3)])
+    assert isinstance(l.strategy, GenericStrategy)
+
+def test_insert_StrategySwitch_AllNil():
+    o = W_Object()
+    l = do_test_insert(NilStrategy, [w_nil, w_nil, o, o, w_nil, w_nil])
+    assert isinstance(l.strategy, GenericStrategy)
+    
+def test_transition_to_nonSingleton():
+    l = W_List(NilStrategy, 5)
+    factory.switch_strategy(l, NonSingletonStrategy)
+    strategy1 = l.strategy
+    assert isinstance(strategy1, NonSingletonStrategy)
+    factory.switch_strategy(l, NonSingletonStrategy)
+    assert strategy1 != l.strategy


More information about the pypy-commit mailing list