Author: Tim Felgentreff <[email protected]>
Branch: bitblt
Changeset: r177:7a70b58eb720
Date: 2013-03-13 17:56 +0100
http://bitbucket.org/pypy/lang-smalltalk/changeset/7a70b58eb720/

Log:    merge default

diff --git a/spyvm/interpreter.py b/spyvm/interpreter.py
--- a/spyvm/interpreter.py
+++ b/spyvm/interpreter.py
@@ -290,12 +290,12 @@
         s_method = receiverclassshadow.lookup(w_selector)
         # XXX catch MethodNotFound here and send doesNotUnderstand:
         # AK shouln't that be done in lookup itself, please check what spec 
says about DNU in case of super sends.
-        if s_method.primitive:
+        code = s_method.primitive()
+        if code:
             # the primitive pushes the result (if any) onto the stack itself
-            code = s_method.primitive
             if interp.should_trace():
                 print "%sActually calling primitive %d" % 
(interp._last_indent, code,)
-            func = primitives.prim_table[code]
+            func = primitives.prim_holder.prim_table[code]
             try:
                 # note: argcount does not include rcvr
                 return func(interp, self, argcount)
diff --git a/spyvm/model.py b/spyvm/model.py
--- a/spyvm/model.py
+++ b/spyvm/model.py
@@ -156,6 +156,12 @@
     """Boxed float value."""
     _attrs_ = ['value']
 
+    def fillin_fromwords(self, space, high, low):
+        from rpython.rlib.rstruct.ieee import float_unpack
+        from rpython.rlib.rarithmetic import r_ulonglong
+        r = (r_ulonglong(high) << 32) | low
+        self.value = float_unpack(r, 8)
+
     def __init__(self, value):
         self.value = value
 
@@ -192,6 +198,35 @@
     def clone(self, space):
         return self
 
+    def at0(self, space, index0):
+        return self.fetch(space, index0)
+
+    def atput0(self, space, index0, w_value):
+        self.store(space, index0, w_value)
+
+    def fetch(self, space, n0):
+        from rpython.rlib.rstruct.ieee import float_pack
+        r = float_pack(self.value, 8) # C double
+        if n0 == 0:
+            return space.wrap_uint(r_uint(intmask(r >> 32)))
+        else:
+            assert n0 == 1
+            return space.wrap_uint(r_uint(intmask(r)))
+
+    def store(self, space, n0, w_obj):
+        from rpython.rlib.rstruct.ieee import float_unpack, float_pack
+        from rpython.rlib.rarithmetic import r_ulonglong
+
+        uint = r_ulonglong(space.unwrap_uint(w_obj))
+        r = float_pack(self.value, 8)
+        if n0 == 0:
+            r = ((r << 32) >> 32) | (uint << 32)
+        else:
+            assert n0 == 1
+            r = ((r >> 32) << 32) | uint
+        self.value = float_unpack(r, 8)
+
+
 class W_AbstractObjectWithIdentityHash(W_Object):
     """Object with explicit hash (ie all except small
     ints and floats)."""
@@ -221,11 +256,14 @@
 class W_AbstractObjectWithClassReference(W_AbstractObjectWithIdentityHash):
     """Objects with arbitrary class (ie not CompiledMethod, SmallInteger or
     Float)."""
-    _attrs_ = ['w_class']
+    _attrs_ = ['w_class', 's_class']
+    s_class = None
 
     def __init__(self, w_class):
         if w_class is not None:     # it's None only for testing and space 
generation
             assert isinstance(w_class, W_PointersObject)
+            if w_class.has_shadow():
+                self.s_class = 
w_class.as_class_get_shadow(w_class._shadow.space)
         self.w_class = w_class
 
     def getclass(self, space):
@@ -236,11 +274,11 @@
         return "<%s %s>" % (self.__class__.__name__, self)
 
     def __str__(self):
-        if isinstance(self, W_PointersObject) and self._shadow is not None:
+        if isinstance(self, W_PointersObject) and self.has_shadow():
             return self._shadow.getname()
         else:
             name = None
-            if self.w_class._shadow is not None:
+            if self.w_class.has_shadow():
                 name = self.w_class._shadow.name
             return "a %s" % (name or '?',)
 
@@ -250,18 +288,23 @@
 
     def _become(self, w_other):
         self.w_class, w_other.w_class = w_other.w_class, self.w_class
+        self.s_class, w_other.s_class = w_other.s_class, self.s_class
         W_AbstractObjectWithIdentityHash._become(self, w_other)
 
     def has_class(self):
         return self.w_class is not None
-        
+
+    def shadow_of_my_class(self, space):
+        if self.s_class is None:
+            self.s_class = self.w_class.as_class_get_shadow(space)
+        return self.s_class
 
 class W_PointersObject(W_AbstractObjectWithClassReference):
     """Common object."""
     _attrs_ = ['_shadow', '_vars']
+
+    _shadow = None # Default value
     
-    _shadow = None # Default value
-
     @jit.unroll_safe
     def __init__(self, w_class, size):
         """Create new object with size = fixed + variable size."""
@@ -269,6 +312,7 @@
         vars = self._vars = [None] * size
         for i in range(size): # do it by hand for the JIT's sake
             vars[i] = w_nil
+        self._shadow = None # Default value
 
     def at0(self, space, index0):
         # To test, at0 = in varsize part
@@ -279,7 +323,7 @@
         self.store(space, index0 + self.instsize(space), w_value)
 
     def fetch(self, space, n0):
-        if self._shadow is not None:
+        if self.has_shadow():
             return self._shadow.fetch(n0)
         return self._fetch(n0)
 
@@ -287,7 +331,7 @@
         return self._vars[n0]
         
     def store(self, space, n0, w_value):    
-        if self._shadow is not None:
+        if self.has_shadow():
             return self._shadow.store(n0, w_value)
         return self._store(n0, w_value)
 
@@ -305,7 +349,7 @@
         return self.varsize(space)
 
     def size(self):
-        if self._shadow is not None:
+        if self.has_shadow():
             return self._shadow.size()
         return self._size()
 
@@ -317,12 +361,13 @@
                 isinstance(self._vars, list))
 
     def store_shadow(self, shadow):
+        assert self._shadow is None or self._shadow is shadow
         self._shadow = shadow
 
     @objectmodel.specialize.arg(2)
     def attach_shadow_of_class(self, space, TheClass):
         shadow = TheClass(space, self)
-        self._shadow = shadow
+        self.store_shadow(shadow)
         shadow.attach_shadow()
         return shadow
 
@@ -331,9 +376,9 @@
         shadow = self._shadow
         if not isinstance(shadow, TheClass):
             if shadow is not None:
-                shadow.detach_shadow()
+                raise DetachingShadowError(shadow, TheClass)
             shadow = self.attach_shadow_of_class(space, TheClass)
-        shadow.sync_shadow()
+            shadow.update()
         return shadow
 
     def get_shadow(self, space):
@@ -369,6 +414,13 @@
         from spyvm.shadow import CachedObjectShadow
         return self.as_special_get_shadow(space, CachedObjectShadow)
 
+    def as_observed_get_shadow(self, space):
+        from spyvm.shadow import ObserveeShadow
+        return self.as_special_get_shadow(space, ObserveeShadow)
+
+    def has_shadow(self):
+        return self._shadow is not None
+
     def as_bitblt_get_shadow(self, space):
         from spyvm.shadow import BitBltShadow
         return self.as_special_get_shadow(space, BitBltShadow)
@@ -377,11 +429,16 @@
         from spyvm.shadow import FormShadow
         return self.as_special_get_shadow(space, FormShadow)
 
+
     def become(self, w_other):
         if not isinstance(w_other, W_PointersObject):
             return False
         self._vars, w_other._vars = w_other._vars, self._vars
+        # switching means also switching shadows
         self._shadow, w_other._shadow = w_other._shadow, self._shadow
+        # shadow links are in both directions -> also update shadows
+        if    self.has_shadow():    self._shadow._w_self = self
+        if w_other.has_shadow(): w_other._shadow._w_self = w_other
         W_AbstractObjectWithClassReference._become(self, w_other)
         return True
         
@@ -498,10 +555,11 @@
 ###    variables.  The number of bytes used for this purpose is the value of
 ###    the last byte in the method.
 
-    _shadow = None
+    _shadow = None # Default value
     _likely_methodname = "<unknown>"
 
     def __init__(self, bytecount=0, header=0):
+        self._shadow = None
         self.setheader(header)
         self.bytes = ["\x00"] * bytecount
 
@@ -516,7 +574,7 @@
         self.header, w_other.header = w_other.header, self.header
         self.literalsize, w_other.literalsize = w_other.literalsize, 
self.literalsize
         self.islarge, w_other.islarge = w_other.islarge, self.islarge
-        self._shadow = w_other._shadow = None
+        self._shadow, w_other._shadow = w_other._shadow, self._shadow
         W_AbstractObjectWithIdentityHash._become(self, w_other)
         return True
 
@@ -602,12 +660,13 @@
         """NOT RPYTHON
            Only for testing"""
         self.literals = literals
-        self._shadow = None
+        if self.has_shadow():
+            self._shadow.update()
 
     def setbytes(self, bytes):
         self.bytes = bytes
 
-    def as_compiledmethod_get_shadow(self, space):
+    def as_compiledmethod_get_shadow(self, space=None):
         from shadow import CompiledMethodShadow
         if self._shadow is None:
             self._shadow = CompiledMethodShadow(self)
@@ -625,7 +684,8 @@
             self.setheader(header)
         else:
             self.literals[index0-1] = w_value
-        self._shadow = None
+        if self.has_shadow():
+            self._shadow.update()
 
     def store(self, space, index0, w_v):
         self.atput0(space, index0, w_v)
@@ -658,7 +718,16 @@
     def setchar(self, index0, character):
         assert index0 >= 0
         self.bytes[index0] = character
-        self._shadow = None
+        if self.has_shadow():
+            self._shadow.update()
+
+    def has_shadow(self):
+        return self._shadow is not None
+
+class DetachingShadowError(Exception):
+    def __init__(self, old_shadow, new_shadow_class):
+        self.old_shadow = old_shadow
+        self.new_shadow_class = new_shadow_class
 
 # Use black magic to create w_nil without running the constructor,
 # thus allowing it to be used even in the constructor of its own
diff --git a/spyvm/objspace.py b/spyvm/objspace.py
--- a/spyvm/objspace.py
+++ b/spyvm/objspace.py
@@ -53,11 +53,8 @@
         w_Class = self.classtable["w_Class"]
         w_Metaclass = self.classtable["w_Metaclass"]
         # XXX
-        proto_shadow = instantiate(shadow.ClassShadow)
-        proto_shadow.space = self
-        proto_shadow.invalid = False
-        proto_shadow.w_superclass = w_Class
-        w_ProtoObjectClass.store_shadow(proto_shadow)
+        proto_shadow = w_ProtoObjectClass._shadow
+        proto_shadow.store_w_superclass(w_Class)
         # at this point, all classes that still lack a w_class are themselves
         # metaclasses
         for nm, w_cls_obj in self.classtable.items():
@@ -181,7 +178,7 @@
                 return self.wrap_int(intmask(val))
             except WrappingError:
                 pass
-        # XXX is math allowed here?
+        # XXX this code sucks
         import math
         bytes_len = int(math.log(val) / math.log(0xff)) + 1
         bytes_len = 4 if 4 > bytes_len else bytes_len
@@ -296,13 +293,15 @@
     # XXX
     s = instantiate(shadow.ClassShadow)
     s.space = space
+    s.version = shadow.Version()
     s._w_self = w_class
-    s.w_superclass = w_superclass
+    s.subclass_s = {}
+    s._s_superclass = None
+    s.store_w_superclass(w_superclass)
     s.name = name
-    s.instance_size = instsize
+    s._instance_size = instsize
     s.instance_kind = format
-    s.w_methoddict = None
+    s._s_methoddict = None
     s.instance_varsized = varsized or format != shadow.POINTERS
-    s.invalid = False
     w_class.store_shadow(s)
     return w_class
diff --git a/spyvm/primitives.py b/spyvm/primitives.py
--- a/spyvm/primitives.py
+++ b/spyvm/primitives.py
@@ -35,6 +35,12 @@
 # Squeak has primitives all the way up to 575
 # So all optional primitives will default to the bytecode implementation
 prim_table = [make_failing(i) for i in range(576)]
+
+class PrimitiveHolder(object):
+    _immutable_fields_ = ["prim_table[*]"]
+
+prim_holder = PrimitiveHolder()
+prim_holder.prim_table = prim_table
 # clean up namespace:
 del i
 prim_table_implemented_only = []
@@ -243,6 +249,10 @@
 FLOAT_LOG_N = 58
 FLOAT_EXP = 59
 
+@expose_primitive(SMALLINT_AS_FLOAT, unwrap_spec=[int])
+def func(interp, s_frame, i):
+    return interp.space.wrap_float(float(i))
+
 math_ops = {
     FLOAT_ADD: operator.add,
     FLOAT_SUBTRACT: operator.sub,
@@ -653,6 +663,7 @@
         raise PrimitiveFailedError()
 
     w_rcvr.w_class = w_arg.w_class
+    w_rcvr.s_class = w_arg.s_class
 
 # ___________________________________________________________________________
 # Miscellaneous Primitives (120-127)
diff --git a/spyvm/shadow.py b/spyvm/shadow.py
--- a/spyvm/shadow.py
+++ b/spyvm/shadow.py
@@ -3,6 +3,16 @@
 from rpython.tool.pairtype import extendabletype
 from rpython.rlib import rarithmetic, jit
 
+def make_elidable_after_versioning(func):
+    @jit.elidable
+    def elidable_func(self, version, *args):
+        return func(self, *args)
+    def meth(self, *args):
+        jit.promote(self)
+        version = jit.promote(self.version)
+        return elidable_func(self, version, *args)
+    return meth
+
 class AbstractShadow(object):
     """A shadow is an optional extra bit of information that
     can be attached at run-time to any Smalltalk object.
@@ -23,40 +33,37 @@
     def getname(self):
         return repr(self)
     def attach_shadow(self): pass
-    def detach_shadow(self): pass
-    def sync_shadow(self): pass
-   
+    def update(self): pass
+
 class AbstractCachingShadow(AbstractShadow):
+    _immutable_fields_ = ['version?']
     _attr_ = []
 
     def __init__(self, space, w_self):
         AbstractShadow.__init__(self, space, w_self)
+        self.version = Version()
 
-    def detach_shadow(self):
-        self.invalidate_shadow()
+    def attach_shadow(self):
+        self.w_self().store_shadow(self)
+        self.update()
 
-    def invalidate_shadow(self):
+    def update(self):
         """This should get called whenever the base Smalltalk
         object changes."""
-        self._w_self.store_shadow(None)
-
-    def attach_shadow(self):
-        self.update_shadow()
-
-    def sync_shadow(self):
-        pass
-
-    def update_shadow(self):
-        self.w_self().store_shadow(self)
         self.sync_cache()
 
     def sync_cache(self):
         raise NotImplementedError()
 
     def store(self, n0, w_value):
-        self.invalidate_shadow()
         AbstractShadow.store(self, n0, w_value)
+        self.update()
 
+    def change(self):
+        self.version = Version()
+
+class Version:
+    pass
 # ____________________________________________________________ 
 
 POINTERS = 0
@@ -77,71 +84,87 @@
     (i.e. used as the class of another Smalltalk object).
     """
 
-    _immutable_fields_ = ["name", "instance_size", "instance_varsized", 
"instance_kind", "w_methoddict", "s_methoddict", "w_superclass"]
+    _attr_ = ["name", "_instance_size", "instance_varsized", "instance_kind",
+                "_s_methoddict", "_s_superclass", "subclass_s"]
 
-    name = None
     def __init__(self, space, w_self):
-        self.name = ""
+        # fields added here should also be in objspace.py:56ff, 300ff
+        self.name = ''
+        self._s_superclass = None
+        self.subclass_s = {}
         AbstractCachingShadow.__init__(self, space, w_self)
 
     def getname(self):
         return "%s class" % (self.name or '?',)
 
     def sync_cache(self):
+        from spyvm.objspace import UnwrappingError
         "Update the ClassShadow with data from the w_self class."
 
         w_self = self.w_self()
+        if w_self.size() == 0:
+            return
+
         # read and painfully decode the format
-        classformat = self.space.unwrap_int(
-            w_self._fetch(constants.CLASS_FORMAT_INDEX))
-        # The classformat in Squeak, as an integer value, is:
-        #    <2 bits=instSize//64><5 bits=cClass><4 bits=instSpec>
-        #                                    <6 bits=instSize\\64><1 bit=0>
-        # In Slang the value is read directly as a boxed integer, so that
-        # the code gets a "pointer" whose bits are set as above, but
-        # shifted one bit to the left and with the lowest bit set to 1.
+        try:
+            classformat = self.space.unwrap_int(
+                w_self._fetch(constants.CLASS_FORMAT_INDEX))
+            # The classformat in Squeak, as an integer value, is:
+            #    <2 bits=instSize//64><5 bits=cClass><4 bits=instSpec>
+            #                                    <6 bits=instSize\\64><1 bit=0>
+            # In Slang the value is read directly as a boxed integer, so that
+            # the code gets a "pointer" whose bits are set as above, but
+            # shifted one bit to the left and with the lowest bit set to 1.
 
-        # compute the instance size (really the size, not the number of bytes)
-        instsize_lo = (classformat >> 1) & 0x3F
-        instsize_hi = (classformat >> (9 + 1)) & 0xC0
-        self.instance_size = (instsize_lo | instsize_hi) - 1  # subtract hdr
-        # decode the instSpec
-        format = (classformat >> 7) & 15
-        self.instance_varsized = format >= 2
-        if format < 4:
-            self.instance_kind = POINTERS
-        elif format == 4:
-            self.instance_kind = WEAK_POINTERS
-        elif format == 6:
-            self.instance_kind = WORDS
-            if self.instance_size != 0:
-                raise ClassShadowError("can't have both words and a non-zero "
-                                       "base instance size")
-        elif 8 <= format <= 11:
-            self.instance_kind = BYTES
-            if self.instance_size != 0:
-                raise ClassShadowError("can't have both bytes and a non-zero "
-                                       "base instance size")
-        elif 12 <= format <= 15:
-            self.instance_kind = COMPILED_METHOD
-        else:
-            raise ClassShadowError("unknown format %d" % (format,))
+            # compute the instance size (really the size, not the number of 
bytes)
+            instsize_lo = (classformat >> 1) & 0x3F
+            instsize_hi = (classformat >> (9 + 1)) & 0xC0
+            self._instance_size = (instsize_lo | instsize_hi) - 1  # subtract 
hdr
+            # decode the instSpec
+            format = (classformat >> 7) & 15
+            self.instance_varsized = format >= 2
+            if format < 4:
+                self.instance_kind = POINTERS
+            elif format == 4:
+                self.instance_kind = WEAK_POINTERS
+            elif format == 6:
+                self.instance_kind = WORDS
+                if self.instsize() != 0:
+                    raise ClassShadowError("can't have both words and a 
non-zero "
+                                           "base instance size")
+            elif 8 <= format <= 11:
+                self.instance_kind = BYTES
+                if self.instsize() != 0:
+                    raise ClassShadowError("can't have both bytes and a 
non-zero "
+                                           "base instance size")
+            elif 12 <= format <= 15:
+                self.instance_kind = COMPILED_METHOD
+            else:
+                raise ClassShadowError("unknown format %d" % (format,))
+        except UnwrappingError:
+            assert w_self._fetch(constants.CLASS_FORMAT_INDEX) is 
self.space.w_nil
+            pass # not enough information stored in w_self, yet
 
         self.guess_class_name()
 
         # read the methoddict
         w_methoddict = w_self._fetch(constants.CLASS_METHODDICT_INDEX)
         assert isinstance(w_methoddict, model.W_PointersObject)
-        self.w_methoddict = w_methoddict
+        if not w_methoddict.is_same_object(self.space.w_nil):
+            self._s_methoddict = 
w_methoddict.as_methoddict_get_shadow(self.space)
+            self._s_methoddict.s_class = self
 
         w_superclass = w_self._fetch(constants.CLASS_SUPERCLASS_INDEX)
         if w_superclass.is_same_object(self.space.w_nil):
-            self.w_superclass = None
+            self._s_superclass = None
         else:
             assert isinstance(w_superclass, model.W_PointersObject)
-            self.w_superclass = w_superclass
+            self.store_w_superclass(w_superclass)
+        self.changed()
 
     def guess_class_name(self):
+        if self.name != '':
+            return self.name
         w_self = self.w_self()
         w_name = None
 
@@ -164,11 +187,14 @@
 
         if isinstance(w_name, model.W_BytesObject):
             self.name = w_name.as_string()
+        else:
+            self.name = None
+        self.changed()
 
     def new(self, extrasize=0):
         w_cls = self.w_self()
         if self.instance_kind == POINTERS:
-            w_new = model.W_PointersObject(w_cls, self.instance_size+extrasize)
+            w_new = model.W_PointersObject(w_cls, self.instsize()+extrasize)
         elif self.instance_kind == WORDS:
             w_new = model.W_WordsObject(w_cls, extrasize)
         elif self.instance_kind == BYTES:
@@ -179,13 +205,16 @@
             raise NotImplementedError(self.instance_kind)
         return w_new
 
+    def w_methoddict(self):
+        return self.w_self()._fetch(constants.CLASS_METHODDICT_INDEX)
+
     def s_methoddict(self):
-        return 
jit.promote(self.w_methoddict.as_methoddict_get_shadow(self.space))
+        return self._s_methoddict
 
     def s_superclass(self):
-        if self.w_superclass is None:
+        if self._s_superclass is None:
             return None
-        return self.w_superclass.as_class_get_shadow(self.space)
+        return self._s_superclass
 
     # _______________________________________________________________
     # Methods for querying the format word, taken from the blue book:
@@ -207,13 +236,64 @@
         " True if instances of this class have data stored as numerical bytes "
         return self.format == BYTES
 
+    @make_elidable_after_versioning
     def isvariable(self):
         " True if instances of this class have indexed inst variables "
         return self.instance_varsized
 
+    @make_elidable_after_versioning
     def instsize(self):
         " Number of named instance variables for each instance of this class "
-        return self.instance_size
+        return self._instance_size
+
+    def store_w_superclass(self, w_class):
+        if w_class is None:
+            self._s_superclass = None
+        else:
+            s_scls = w_class.as_class_get_shadow(self.space)
+            if self._s_superclass is s_scls: 
+                return
+            elif (self._s_superclass is not None 
+                and self._s_superclass is not s_scls):
+                self._s_superclass.detach_s_class(self)
+            self._s_superclass = s_scls
+            self._s_superclass.attach_s_class(self)
+
+    def attach_s_class(self, s_other):
+        self.subclass_s[s_other] = None
+
+    def detach_s_class(self, s_other):
+        del self.subclass_s[s_other]
+
+    def changed(self):
+        self.superclass_changed(Version())
+
+    # this is done, because the class-hierarchy contains cycles
+    def superclass_changed(self, version):
+        if self.version is not version:
+            self.version = version
+            for s_class in self.subclass_s:
+                s_class.superclass_changed(version)
+
+    # _______________________________________________________________
+    # Methods for querying the format word, taken from the blue book:
+
+    def __repr__(self):
+        return "<ClassShadow %s>" % (self.name or '?',)
+
+    @make_elidable_after_versioning
+    def lookup(self, w_selector):
+        look_in_shadow = self
+        while look_in_shadow is not None:
+            s_method = look_in_shadow.s_methoddict().find_selector(w_selector)
+            if s_method is not None:
+                return s_method
+            look_in_shadow = look_in_shadow._s_superclass
+        raise MethodNotFound(self, w_selector)
+
+
+    # _______________________________________________________________
+    # Methods used only in testing
 
     def inherits_from(self, s_superclass):
         classshadow = self
@@ -224,51 +304,58 @@
         else:
             return False
 
-    # _______________________________________________________________
-    # Methods for querying the format word, taken from the blue book:
-
-    def __repr__(self):
-        return "<ClassShadow %s>" % (self.name or '?',)
-
-    @jit.unroll_safe
-    def lookup(self, w_selector):
-        look_in_shadow = self
-        jit.promote(w_selector)
-        while look_in_shadow is not None:
-            w_method = look_in_shadow.s_methoddict().find_selector(w_selector)
-            if w_method is not None:
-                return w_method.as_compiledmethod_get_shadow(self.space)
-            look_in_shadow = look_in_shadow.s_superclass()
-        raise MethodNotFound(self, w_selector)
-
     def initialize_methoddict(self):
         "NOT_RPYTHON"     # this is only for testing.
-        if self.w_methoddict is None:
-            self.w_methoddict = model.W_PointersObject(None, 2)
-            self.w_methoddict._store(1, model.W_PointersObject(None, 0))
-            self.s_methoddict().invalid = False
+        if self._s_methoddict is None:
+            w_methoddict = model.W_PointersObject(None, 2)
+            w_methoddict._store(1, model.W_PointersObject(None, 0))
+            self._s_methoddict = 
w_methoddict.as_methoddict_get_shadow(self.space)
+            self.s_methoddict().sync_cache()
+        self.s_methoddict().invalid = False
 
     def installmethod(self, w_selector, w_method):
         "NOT_RPYTHON"     # this is only for testing.
         assert not isinstance(w_selector, str)
         self.initialize_methoddict()
-        self.s_methoddict().methoddict[w_selector] = w_method
+        s_method = w_method.as_compiledmethod_get_shadow(self.space)
+        self.s_methoddict().methoddict[w_selector] = s_method
         if isinstance(w_method, model.W_CompiledMethod):
-            method = w_method.as_compiledmethod_get_shadow(self.space)
-            method.w_compiledin = self.w_self()
+            s_method.w_compiledin = self.w_self()
 
-class MethodDictionaryShadow(AbstractCachingShadow):
+class MethodDictionaryShadow(AbstractShadow):
 
-    @jit.elidable
+    _immutable_fields_ = ['invalid?', 's_class']
+    _attr_ = ['methoddict']
+
+    def __init__(self, space, w_self):
+        self.invalid = True
+        self.s_class = None
+        self.methoddict = {}
+        AbstractShadow.__init__(self, space, w_self)
+
     def find_selector(self, w_selector):
+        assert not self.invalid
         return self.methoddict.get(w_selector, None)
 
+    def update(self): return self.sync_cache()
+
+    # Remove update call for changes to ourselves:
+    # Whenever a method is added, it's keyword is added to w_self, then the
+    # w_compiled_method is added to our observee.
+        # Sync_cache at this point would not have the desired effect, because 
in
+        # the Smalltalk Implementation, the dictionary changes first. 
Afterwards
+        # its contents array is filled with the value belonging to the new key.
+    def store(self, n0, w_value):
+        AbstractShadow.store(self, n0, w_value)
+        self.invalid = True
+
     def sync_cache(self):
+        if self.w_self().size() == 0:
+            return
         w_values = self.w_self()._fetch(constants.METHODDICT_VALUES_INDEX)
         assert isinstance(w_values, model.W_PointersObject)
-        s_values = w_values.get_shadow(self.space)
-        # XXX Should add!
-        # s_values.notifyinvalid(self)
+        s_values = w_values.as_observed_get_shadow(self.space)
+        s_values.notify(self)
         size = self.w_self().size() - constants.METHODDICT_NAMES_INDEX
         self.methoddict = {}
         for i in range(size):
@@ -278,11 +365,16 @@
                     raise ClassShadowError("bogus selector in method dict")
                 w_compiledmethod = w_values._fetch(i)
                 if not isinstance(w_compiledmethod, model.W_CompiledMethod):
-                    raise ClassShadowError("the methoddict must contain "
-                                           "CompiledMethods only for now")
-                self.methoddict[w_selector] = w_compiledmethod
+                    raise ClassShadowError("The methoddict must contain "
+                                       "CompiledMethods only, for now. "
+                                       "If the value observed is nil, our "
+                                       "invalidating mechanism may be broken.")
+                self.methoddict[w_selector] = 
w_compiledmethod.as_compiledmethod_get_shadow(self.space)
                 selector = w_selector.as_string()
                 w_compiledmethod._likely_methodname = selector
+        if self.s_class:
+            self.s_class.changed()
+        self.invalid = False
 
 
 class AbstractRedirectingShadow(AbstractShadow):
@@ -310,12 +402,12 @@
             self.copy_from_w_self(i)
         w_self._vars = None
 
-    def detach_shadow(self):
-        w_self = self.w_self()
-        assert isinstance(w_self, model.W_PointersObject)
-        w_self._vars = [self.space.w_nil] * self._w_self_size
-        for i in range(self._w_self_size):
-            self.copy_to_w_self(i)
+    # def detach_shadow(self):
+    #     w_self = self.w_self()
+    #     assert isinstance(w_self, model.W_PointersObject)
+    #     w_self._vars = [self.space.w_nil] * self._w_self_size
+    #     for i in range(self._w_self_size):
+    #         self.copy_to_w_self(i)
 
     def copy_from_w_self(self, n0):
         self.store(n0, self.w_self()._fetch(n0))
@@ -485,7 +577,7 @@
     def getbytecode(self):
         jit.promote(self._pc)
         assert self._pc >= 0
-        bytecode = self.s_method().bytecode[self._pc]
+        bytecode = self.s_method().getbytecode(self._pc)
         currentBytecode = ord(bytecode)
         self._pc += 1
         return currentBytecode
@@ -579,7 +671,7 @@
         if self._w_self is not None:
             return self._w_self
         else:
-            size = self.size() - 
self.space.w_MethodContext.as_class_get_shadow(self.space).instance_size
+            size = self.size() - 
self.space.w_MethodContext.as_class_get_shadow(self.space).instsize()
             space = self.space
             w_self = space.w_MethodContext.as_class_get_shadow(space).new(size)
             w_self.store_shadow(self)
@@ -711,12 +803,8 @@
     @jit.unroll_safe
     def make_context(space, s_method, w_receiver,
                      arguments, s_sender=None, closure=None, pc=0):
-        # From blue book: normal mc have place for 12 temps+maxstack
-        # mc for methods with islarge flag turned on 32
-        size = (12 + s_method.islarge * 20 + s_method.argsize 
-            + space.w_MethodContext.as_class_get_shadow(space).instance_size)
-        # The last summand is needed, because we calculate i.a. our stackdepth 
relative of the size of w_self.
-
+        # The summand is needed, because we calculate i.a. our stackdepth 
relative of the size of w_self.
+        size = s_method.compute_frame_size() + 
space.w_MethodContext.as_class_get_shadow(space).instsize()
         s_new_context = MethodContextShadow(space, None)
         s_new_context._w_self_size = size
         s_new_context_non_fresh = s_new_context # XXX: find a better solution 
to translation err
@@ -777,7 +865,7 @@
 
     def tempsize(self):
         if not self.is_closure_context():
-            return self.s_method().tempsize
+            return self.s_method().tempsize()
         else:
             return wrapper.BlockClosureWrapper(self.space, 
                                 self.w_closure_or_nil).tempsize()
@@ -854,20 +942,44 @@
         )
 
 class CompiledMethodShadow(object):
-    _immutable_fields_ = ["_w_self", "bytecode",
-                          "literals[*]", "bytecodeoffset",
-                          "literalsize", "tempsize", "primitive",
-                          "argsize", "islarge",
-                          "w_compiledin"]
+    _attr_ = ["_w_self", "bytecode",
+              "literals[*]", "bytecodeoffset",
+              "literalsize", "tempsize", "primitive",
+              "argsize", "islarge",
+              "w_compiledin"]
+    _immutable_fields_ = ["version?", "_w_self"]
 
     def __init__(self, w_compiledmethod):
         self._w_self = w_compiledmethod
+        self.update()
+
+    def w_self(self):
+        return self._w_self
+
+    @make_elidable_after_versioning
+    def getliteral(self, index):
+        return self.literals[index]
+
+    @make_elidable_after_versioning
+    def compute_frame_size(self):
+        # From blue book: normal mc have place for 12 temps+maxstack
+        # mc for methods with islarge flag turned on 32
+        return 12 + self.islarge * 20 + self.argsize
+
+    def getliteralsymbol(self, index):
+        w_literal = self.getliteral(index)
+        assert isinstance(w_literal, model.W_BytesObject)
+        return w_literal.as_string()    # XXX performance issue here
+
+    def update(self):
+        w_compiledmethod = self._w_self
+        self.version = Version()
         self.bytecode = "".join(w_compiledmethod.bytes)
         self.literals = w_compiledmethod.literals
         self.bytecodeoffset = w_compiledmethod.bytecodeoffset()
         self.literalsize = w_compiledmethod.getliteralsize()
-        self.tempsize = w_compiledmethod.gettempsize()
-        self.primitive = w_compiledmethod.primitive
+        self._tempsize = w_compiledmethod.gettempsize()
+        self._primitive = w_compiledmethod.primitive
         self.argsize = w_compiledmethod.argsize
         self.islarge = w_compiledmethod.islarge
 
@@ -884,16 +996,13 @@
                 association = wrapper.AssociationWrapper(None, w_association)
                 self.w_compiledin = association.value()
 
-    def w_self(self):
-        return self._w_self
+    @make_elidable_after_versioning
+    def tempsize(self):
+        return self._tempsize
 
-    def getliteral(self, index):
-        return self.literals[index]
-
-    def getliteralsymbol(self, index):
-        w_literal = self.getliteral(index)
-        assert isinstance(w_literal, model.W_BytesObject)
-        return w_literal.as_string()    # XXX performance issue here
+    @make_elidable_after_versioning
+    def primitive(self):
+        return self._primitive
 
     def create_frame(self, space, receiver, arguments, sender = None):
         assert len(arguments) == self.argsize
@@ -901,15 +1010,11 @@
                 space, self, receiver, arguments, sender)
         return s_new
 
-class Version:
-    pass
+    @make_elidable_after_versioning
+    def getbytecode(self, pc):
+        return self.bytecode[pc]
 
 class CachedObjectShadow(AbstractCachingShadow):
-    _immutable_fields_ = ['version?']
-
-    def __init__(self, space, w_self):
-        AbstractCachingShadow.__init__(self, space, w_self)
-        self.version = Version()
 
     def fetch(self, n0):
         jit.promote(self)
@@ -926,8 +1031,25 @@
         self.version = Version()
         return self._w_self._store(n0, w_value)
 
-    def update_shadow(self):
-        self.version = Version()
+    def update(self): pass
+
+
+class ObserveeShadow(AbstractShadow):
+    _attr_ = ['dependent']
+    def __init__(self, space, w_self):
+        AbstractShadow.__init__(self, space, w_self)
+        self.dependent = None
+
+    def store(self, n0, w_value):
+        AbstractShadow.store(self, n0, w_value)
+        self.dependent.update()
+
+    def notify(self, dependent):
+        if self.dependent is not None and dependent is not self.dependent:
+            raise RuntimeError('Meant to be observed by only one value, so 
far')
+        self.dependent = dependent
+
+    def update(self): pass
 
 
 class BitBltShadow(AbstractCachingShadow):
diff --git a/spyvm/squeakimage.py b/spyvm/squeakimage.py
--- a/spyvm/squeakimage.py
+++ b/spyvm/squeakimage.py
@@ -227,6 +227,7 @@
         self.init_g_objects()
         self.init_w_objects()
         self.fillin_w_objects()
+        self.synchronize_shadows()
 
     def read_version(self):
         # 1 word version
@@ -300,6 +301,12 @@
         for chunk in self.chunks.itervalues():
             chunk.g_object.fillin_w_object()
 
+    def synchronize_shadows(self):
+        for chunk in self.chunks.itervalues():
+            casted = chunk.g_object.w_object
+            if isinstance(casted, model.W_PointersObject) and 
casted.has_shadow():
+                casted._shadow.update()
+
     def init_compactclassesarray(self):
         """ from the blue book (CompiledMethod Symbol Array PseudoContext 
LargePositiveInteger nil MethodDictionary Association Point Rectangle nil 
TranslatedMethod BlockContext MethodContext nil nil nil nil nil nil nil nil nil 
nil nil nil nil nil nil nil nil ) """
         special = self.chunks[self.specialobjectspointer]
@@ -454,9 +461,15 @@
     def iswords(self):
         return self.format == 6
 
+    def isfloat(self):
+        return self.iswords() and 
self.space.w_Float.is_same_object(self.g_class.w_object)
+
     def ispointers(self):
         return self.format < 5 #TODO, what about compiled methods?
 
+    def iscompiledmethod(self):
+        return 12 <= self.format <= 15
+
     def init_w_object(self):
         """ 0      no fields
             1      fixed fields only (all containing pointers)
@@ -476,24 +489,23 @@
         if self.w_object is None:
             # the instantiate call circumvents the constructors
             # and makes empty objects
-            if self.format < 5:
+            if self.ispointers():
                 # XXX self.format == 4 is weak
                 self.w_object = objectmodel.instantiate(model.W_PointersObject)
             elif self.format == 5:
                 raise CorruptImageError("Unknown format 5")
-            elif self.format == 6:
+            elif self.isfloat():
+                self.w_object = objectmodel.instantiate(model.W_Float)
+            elif self.iswords():
                 self.w_object = objectmodel.instantiate(model.W_WordsObject)
             elif self.format == 7:
                 raise CorruptImageError("Unknown format 7, no 64-bit support 
yet :-)")
-            elif 8 <= self.format <= 11:
+            elif self.isbytes():
                 self.w_object = objectmodel.instantiate(model.W_BytesObject)
-            elif 12 <= self.format <= 15:
+            elif self.iscompiledmethod():
                 self.w_object = objectmodel.instantiate(model.W_CompiledMethod)
             else:
                 assert 0, "not reachable"
-        else:
-            #XXX invalidate shadow here
-            pass
         return self.w_object
 
     def fillin_w_object(self):
@@ -502,6 +514,8 @@
         casted = self.w_object
         if isinstance(casted, model.W_PointersObject):
             self.fillin_pointersobject(casted)
+        elif isinstance(casted, model.W_Float):
+            self.fillin_floatobject(casted)
         elif isinstance(casted, model.W_WordsObject):
             self.fillin_wordsobject(casted)
         elif isinstance(casted, model.W_BytesObject):
@@ -515,15 +529,22 @@
 
     def fillin_pointersobject(self, w_pointersobject):
         assert self.pointers is not None
-        # XXX is the following needed?
-        if w_pointersobject._shadow is not None:
-            w_pointersobject._shadow.detach_shadow()
         w_pointersobject._vars = [g_object.w_object for g_object in 
self.pointers]
         w_class = self.g_class.w_object
         assert isinstance(w_class, model.W_PointersObject)
         w_pointersobject.w_class = w_class
+        w_pointersobject.s_class = None
         w_pointersobject.hash = self.chunk.hash12
 
+    def fillin_floatobject(self, w_floatobject):
+        from rpython.rlib.rarithmetic import r_uint
+        words = [r_uint(x) for x in self.chunk.data]
+        if len(words) != 2:
+            raise CorruptImageError("Expected 2 words in Float, got %d" % 
len(words))
+        w_class = self.g_class.w_object
+        assert isinstance(w_class, model.W_PointersObject)
+        w_floatobject.fillin_fromwords(self.space, words[0], words[1])
+
     def fillin_wordsobject(self, w_wordsobject):
         from rpython.rlib.rarithmetic import r_uint
         w_wordsobject.words = [r_uint(x) for x in self.chunk.data]
diff --git a/spyvm/test/jit.py b/spyvm/test/jit.py
--- a/spyvm/test/jit.py
+++ b/spyvm/test/jit.py
@@ -58,7 +58,7 @@
         
         image = create_testimage(space)
         interp = interpreter.Interpreter(space, image)
-        w_selector = interp.perform(space.wrap_string('loopTest3'), "asSymbol")
+        w_selector = interp.perform(space.wrap_string('loopTest2'), "asSymbol")
         assert isinstance(w_selector, model.W_BytesObject)
         def interp_w():
             interp.perform(model.W_SmallInteger(1000), w_selector)
diff --git a/spyvm/test/test_bootstrappedimage.py 
b/spyvm/test/test_bootstrappedimage.py
--- a/spyvm/test/test_bootstrappedimage.py
+++ b/spyvm/test/test_bootstrappedimage.py
@@ -7,7 +7,9 @@
 tools.setup_module(tools, filename='bootstrapped.image')
 
 def find_symbol_in_methoddict_of(string, s_class):
-    methoddict_w = s_class.s_methoddict().methoddict
+    s_methoddict = s_class.s_methoddict()
+    s_methoddict.sync_cache()
+    methoddict_w = s_methoddict.methoddict
     for each in methoddict_w.keys():
         if each.as_string() == string:
             return each
@@ -55,4 +57,4 @@
     sends = perform(w(5), 'benchFib')
     t1 = time.time()
     t = t1 - t0
-    print str(tools.space.unwrap_int(sends)/t) + " sends per second"
\ No newline at end of file
+    print str(tools.space.unwrap_int(sends)/t) + " sends per second"
diff --git a/spyvm/test/test_interpreter.py b/spyvm/test/test_interpreter.py
--- a/spyvm/test/test_interpreter.py
+++ b/spyvm/test/test_interpreter.py
@@ -62,7 +62,8 @@
         assert space.w_nil._shadow is None
         for (w_class, _, _, methname) in methods:
             s_class = w_class.as_class_get_shadow(space)
-            del s_class.s_methoddict().methoddict[fakesymbol(methname)]
+            s_class.update()
+            s_class.s_methoddict().update()
 
 def fakesymbol(s, _cache={}):
     try:
@@ -427,7 +428,7 @@
         assert s_active_context.w_sender() == w_frame
         assert s_active_context.stack() == []
         assert 
w_active_context.as_methodcontext_get_shadow(space).w_receiver().is_same_object(w_object)
-        assert 
w_active_context.as_methodcontext_get_shadow(space).w_method().is_same_object(shadow.s_methoddict().methoddict[w_foo])
+        assert 
w_active_context.as_methodcontext_get_shadow(space).w_method().is_same_object(shadow.s_methoddict().methoddict[w_foo].w_self())
         assert s_frame.stack() == []
         step_in_interp(s_active_context)
         w_active_context = step_in_interp(s_active_context)
@@ -598,7 +599,7 @@
     s_frame.push(space.w_one)
     w_active_context = step_in_interp(s_frame)
     s_active_context = w_active_context.as_context_get_shadow(space)
-    assert w_active_context.as_methodcontext_get_shadow(space).w_method() == 
shadow.s_methoddict().methoddict[w_symbol]
+    assert w_active_context.as_methodcontext_get_shadow(space).s_method() == 
shadow.s_methoddict().methoddict[w_symbol]
     assert s_active_context.w_receiver() is w_object
     assert 
w_active_context.as_methodcontext_get_shadow(space).gettemp(0).is_same_object(space.w_one)
     assert s_active_context.stack() == []
@@ -659,7 +660,7 @@
         assert s_active_context.stack() == []
         assert 
w_active_context.as_methodcontext_get_shadow(space).w_receiver() == w_object
         meth = 
w_specificclass.as_class_get_shadow(space).s_methoddict().methoddict[foo]
-        assert s_active_context.w_method() == meth
+        assert s_active_context.w_method() == meth.w_self()
         assert s_caller_context.stack() == []
 
 def test_secondExtendedSendBytecode():
diff --git a/spyvm/test/test_miniimage.py b/spyvm/test/test_miniimage.py
--- a/spyvm/test/test_miniimage.py
+++ b/spyvm/test/test_miniimage.py
@@ -270,7 +270,7 @@
     perform(w(10).getclass(space), "compile:classified:notifying:", 
w(sourcecode), w('pypy'), w(None))
     w_result = perform(w(10), "testBecome")
     assert space.unwrap_int(w_result) == 42
-       
+
 def perform(w_receiver, selector, *arguments_w):
     return interp.perform(w_receiver, selector, *arguments_w)
 
@@ -282,6 +282,22 @@
     assert isinstance(s_ctx, shadow.MethodContextShadow)
     assert s_ctx.top().is_same_object(space.w_true)
 
+def test_cached_methoddict():
+    py.test.skip('Should test the same as test_shadow.test_cached_methoddict, 
as long '
+                'as the implementation of MethodDictionary>>#at:put does not 
change.')
+    sourcecode = """fib
+                        ^self < 2
+                            ifTrue: [ 1 ]
+                            ifFalse: [ ((self - 1) fib + (self - 2) fib) + 1 
]"""
+    perform(w(10).getclass(space), "compile:classified:notifying:", 
w(sourcecode), w('pypy'), w(None))
+    assert perform(w(5), "fib").is_same_object(w(15))
+    sourcecode = """fib
+                        ^self < 2
+                            ifTrue: [ 1 ]
+                            ifFalse: [ (self - 1) fib + (self - 2) fib ]"""
+    perform(w(10).getclass(space), "compile:classified:notifying:", 
w(sourcecode), w('pypy'), w(None))
+    assert perform(w(10), "fib").is_same_object(w(89))
+
 def test_step_run_something():
     from spyvm.test import test_miniimage
     setup_module(test_miniimage, filename='running-something-mini.image')
@@ -306,3 +322,10 @@
     w_result = perform(w("someString"), "asSymbol")
     assert w_result is not None
     assert w_result.as_string() == "someString"
+
+def test_pi_as_w_float():
+    import math
+    w_result = perform(interp.space.w_Float, "pi")
+    assert w_result is not None
+    assert isinstance(w_result, model.W_Float)
+    assert w_result.value == math.pi
diff --git a/spyvm/test/test_model.py b/spyvm/test/test_model.py
--- a/spyvm/test/test_model.py
+++ b/spyvm/test/test_model.py
@@ -1,4 +1,5 @@
 import py
+import math
 from spyvm import model, shadow
 from spyvm.shadow import MethodNotFound
 from spyvm import objspace, error
@@ -213,7 +214,9 @@
     res = w_clsa.become(w_clsb)
     assert res
     assert w_clsa.as_class_get_shadow(space) is s_clsb
+    assert s_clsa._w_self is w_clsb
     assert w_clsb.as_class_get_shadow(space) is s_clsa
+    assert s_clsb._w_self is w_clsa
 
 def test_word_atput():
     i = model.W_SmallInteger(100)
@@ -236,3 +239,24 @@
     r = b.at0(space, 0)
     assert isinstance(r, model.W_BytesObject)
     assert r.size() == 4
+
+def test_float_at():
+    b = model.W_Float(64.0)
+    r = b.fetch(space, 0)
+    assert isinstance(r, model.W_BytesObject)
+    assert r.size() == 4
+    assert r.bytes == [chr(0), chr(0), chr(80), chr(64)]
+    r = b.fetch(space, 1)
+    assert isinstance(r, model.W_SmallInteger)
+    assert r.value == 0
+
+def test_float_at_put():
+    target = model.W_Float(1.0)
+    for f in [1.0, -1.0, 1.1, 64.4, -0.0, float('nan'), float('inf')]:
+        source = model.W_Float(f)
+        target.store(space, 0, source.fetch(space, 0))
+        target.store(space, 1, source.fetch(space, 1))
+        if math.isnan(f):
+            assert math.isnan(target.value)
+        else:
+            assert target.value == f
diff --git a/spyvm/test/test_primitives.py b/spyvm/test/test_primitives.py
--- a/spyvm/test/test_primitives.py
+++ b/spyvm/test/test_primitives.py
@@ -167,6 +167,9 @@
     prim_fails(primitives.BIT_SHIFT, [4, 29])
     prim_fails(primitives.BIT_SHIFT, [4, 28])
 
+def test_smallint_as_float():
+    assert prim(primitives.SMALLINT_AS_FLOAT, [12]).value == 12.0
+
 def test_float_add():
     assert prim(primitives.FLOAT_ADD, [1.0,2.0]).value == 3.0
     assert prim(primitives.FLOAT_ADD, [3.0,4.5]).value == 7.5
diff --git a/spyvm/test/test_shadow.py b/spyvm/test/test_shadow.py
--- a/spyvm/test/test_shadow.py
+++ b/spyvm/test/test_shadow.py
@@ -1,5 +1,5 @@
 import random
-from spyvm import model, shadow, constants
+from spyvm import model, shadow, constants, interpreter
 from spyvm import objspace
 
 space = objspace.ObjSpace()
@@ -42,6 +42,7 @@
     w_class.store(space, constants.CLASS_FORMAT_INDEX, space.wrap_int(format))
     if name is not None:
         w_class.store(space, constants.CLASS_NAME_INDEX, 
space.wrap_string(name))
+    w_class.as_class_get_shadow(space).s_methoddict().sync_cache()
     return w_class
 
 def basicshape(name, format, kind, varsized, instsize):
@@ -72,7 +73,7 @@
     methoddict = classshadow.s_methoddict().methoddict
     assert len(methods) == len(methoddict)
     for w_key, value in methoddict.items():
-        assert methods[w_key.as_string()] is value
+        assert methods[w_key.as_string()].as_compiledmethod_get_shadow(space) 
is value
 
 def method(tempsize=3,argsize=2, bytes="abcde"):
     w_m = model.W_CompiledMethod()
@@ -152,32 +153,24 @@
     assert s_object.getbytecode() == 101
     assert s_object.s_home() == s_object
 
-def test_attach_detach_mc():
+def test_attach_mc():
     w_m = method()
     w_object = methodcontext(pc=13, method=w_m)
     old_vars = w_object._vars
     s_object = w_object.as_methodcontext_get_shadow(space)
     assert w_object._vars is None
-    s_object.detach_shadow()
-    assert w_object._vars == old_vars
-    assert w_object._vars is not old_vars
 
-def test_attach_detach_bc():
+def test_attach_bc():
     w_object = blockcontext(pc=13)
     old_vars = w_object._vars
     s_object = w_object.as_blockcontext_get_shadow(space)
     assert w_object._vars is None
-    s_object.detach_shadow()
-    assert w_object._vars == old_vars
-    assert w_object._vars is not old_vars
 
 def test_replace_to_bc():
     w_object = blockcontext(pc=13)
     old_vars = w_object._vars
     s_object = w_object.as_blockcontext_get_shadow(space)
-    s_object.detach_shadow()
-    s_classshadow = shadow.ClassShadow(space, w_object)
-    w_object._shadow = s_classshadow
+    s_object._shadow = None
     s_newobject = w_object.as_blockcontext_get_shadow(space)
     assert ([s_newobject.fetch(i) for i in range(s_newobject.size())] ==
             [s_object.fetch(i) for i in range(s_newobject.size())])
@@ -193,20 +186,17 @@
     assert shadow.bytecode == "abc"
     assert shadow.bytecodeoffset == 12
     assert shadow.literalsize == 8 # 12 - 4byte header
-    assert shadow.tempsize == 1
+    assert shadow.tempsize() == 1
 
     w_compiledmethod.literalatput0(space, 1, 17)
     w_compiledmethod.literalatput0(space, 2, 41)
-    assert w_compiledmethod._shadow is None
-
-    shadow = w_compiledmethod.as_compiledmethod_get_shadow(space)
+    assert w_compiledmethod._shadow is not None
     assert shadow.literals == [17, 41]
 
     w_compiledmethod.atput0(space, 14, space.wrap_int(ord("x")))
-    assert w_compiledmethod._shadow is None
 
-    shadow = w_compiledmethod.as_compiledmethod_get_shadow(space)
     assert shadow.bytecode == "abx"
+    assert shadow is w_compiledmethod.as_compiledmethod_get_shadow(space)
 
 def test_cached_object_shadow():
     w_o = space.wrap_list([0, 1, 2, 3, 4, 5, 6, 7])
@@ -216,4 +206,71 @@
         assert w_o.at0(space, i) == i
     w_o.atput0(space, 0, 8)
     assert version is not s_o.version
-    assert w_o.at0(space, 0) == 8
\ No newline at end of file
+    assert w_o.at0(space, 0) == 8
+
+def test_observee_shadow():
+    notified = False
+    class Observer():
+        def __init__(self): self.notified = False
+        def update(self): self.notified = True
+    o = Observer()
+    w_o = w_Array.as_class_get_shadow(space).new(1)
+    w_o.as_observed_get_shadow(space).notify(o)
+    assert not o.notified
+    w_o.store(space, 0, 1)
+    assert o.notified
+    assert w_o.fetch(space, 0) == 1
+    try:
+        w_o._shadow.notify(Observer())
+    except RuntimeError:
+        pass
+    else:
+        assert False
+
+def test_cached_methoddict():
+    # create a methoddict
+    foo = model.W_CompiledMethod(0)
+    bar = model.W_CompiledMethod(0)
+    baz = model.W_CompiledMethod(0)
+    methods = {'foo': foo,
+               'bar': bar}
+    w_class = build_smalltalk_class("Demo", 0x90, methods=methods)
+    s_class = w_class.as_class_get_shadow(space)
+    s_methoddict = s_class.s_methoddict()
+    s_methoddict.sync_cache()
+    i = 0
+    key = s_methoddict.w_self()._fetch(constants.METHODDICT_NAMES_INDEX+i)
+    while key is space.w_nil:
+        key = s_methoddict.w_self()._fetch(constants.METHODDICT_NAMES_INDEX+i)
+        i = i + 1
+
+    assert (s_class.lookup(key) is foo.as_compiledmethod_get_shadow(space)
+            or s_class.lookup(key) is bar.as_compiledmethod_get_shadow(space))
+    # change that entry
+    w_array = s_class.w_methoddict()._fetch(constants.METHODDICT_VALUES_INDEX)
+    version = s_class.version
+    w_array.atput0(space, i, baz)
+
+    assert s_class.lookup(key) is baz.as_compiledmethod_get_shadow(space)
+    assert version is not s_class.version
+
+def test_updating_class_changes_subclasses():
+    w_parent = build_smalltalk_class("Demo", 0x90,
+            methods={'bar': model.W_CompiledMethod(0)})
+    w_class = build_smalltalk_class("Demo", 0x90,
+            methods={'foo': model.W_CompiledMethod(0)}, w_superclass=w_parent)
+    s_class = w_class.as_class_get_shadow(space)
+    version = s_class.version
+
+    w_method = model.W_CompiledMethod(0)
+    key = space.wrap_string('foo')
+
+    s_md = w_parent.as_class_get_shadow(space).s_methoddict()
+    s_md.sync_cache()
+    w_ary = s_md._w_self._fetch(constants.METHODDICT_VALUES_INDEX)
+    s_md._w_self.atput0(space, 0, key)
+    w_ary.atput0(space, 0, w_method)
+
+    assert s_class.lookup(key) is w_method.as_compiledmethod_get_shadow(space)
+    assert s_class.version is not version
+    assert s_class.version is w_parent.as_class_get_shadow(space).version
diff --git a/spyvm/todo.txt b/spyvm/todo.txt
--- a/spyvm/todo.txt
+++ b/spyvm/todo.txt
@@ -53,9 +53,3 @@
             return model.W_SmallInteger(rerased.erase_int(val))
         except OverflowError:
             raise WrappingError("integer too large to fit into a tagged 
pointer")
-
-make classes
-
-
-Unclarities:
-[ ] should image loading invalidate the shadows of the precreated objects?
\ No newline at end of file
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to