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