[pypy-commit] lang-smalltalk rstrategies: Split shadow.py into 3 storage*.py files.
anton_gulenko
noreply at buildbot.pypy.org
Tue Aug 5 18:06:37 CEST 2014
Author: Anton Gulenko <anton.gulenko at googlemail.com>
Branch: rstrategies
Changeset: r1008:a42e8c9ffe86
Date: 2014-07-28 15:52 +0200
http://bitbucket.org/pypy/lang-smalltalk/changeset/a42e8c9ffe86/
Log: Split shadow.py into 3 storage*.py files.
diff too long, truncating to 2000 out of 3376 lines
diff --git a/spyvm/interpreter.py b/spyvm/interpreter.py
--- a/spyvm/interpreter.py
+++ b/spyvm/interpreter.py
@@ -1,6 +1,6 @@
import os
-from spyvm.shadow import MethodContextShadow, ActiveContext, InactiveContext, DirtyContext
+from spyvm.storage_contexts import MethodContextShadow, ActiveContext, InactiveContext, DirtyContext
from spyvm import model, constants, wrapper, objspace, interpreter_bytecodes, error
from rpython.rlib import jit, rstackovf, unroll
diff --git a/spyvm/interpreter_bytecodes.py b/spyvm/interpreter_bytecodes.py
--- a/spyvm/interpreter_bytecodes.py
+++ b/spyvm/interpreter_bytecodes.py
@@ -1,5 +1,6 @@
-from spyvm.shadow import ContextPartShadow, ClassShadow
+from spyvm.storage_contexts import ContextPartShadow
+from spyvm.storage_classes import ClassShadow
from spyvm import model, primitives, wrapper, error
from spyvm.tool.bitmanipulation import splitter
from rpython.rlib import objectmodel, unroll, jit
diff --git a/spyvm/interpreter_debugging.py b/spyvm/interpreter_debugging.py
--- a/spyvm/interpreter_debugging.py
+++ b/spyvm/interpreter_debugging.py
@@ -1,6 +1,6 @@
import pdb
-from spyvm.shadow import ContextPartShadow
+from spyvm.storage_contexts import ContextPartShadow
from spyvm import model, constants, primitives
# This module patches up the interpreter and adds breakpoints at certain execution points.
diff --git a/spyvm/model.py b/spyvm/model.py
--- a/spyvm/model.py
+++ b/spyvm/model.py
@@ -544,9 +544,9 @@
return "? (no class)"
def invariant(self):
- from spyvm import shadow
+ from spyvm import storage_classes
return (W_AbstractObjectWithIdentityHash.invariant(self) and
- isinstance(self.w_class.shadow, shadow.ClassShadow))
+ isinstance(self.w_class.shadow, storage_classes.ClassShadow))
def _become(self, w_other):
assert isinstance(w_other, W_AbstractObjectWithClassReference)
@@ -578,7 +578,7 @@
self.initialize_storage(space, size, weak)
def initialize_storage(self, space, size, weak=False):
- from spyvm.shadow import empty_storage
+ from spyvm.storage import empty_storage
storage = empty_storage(space, self, size, weak)
self.store_shadow(storage)
self.log_storage("Initialized")
@@ -590,18 +590,18 @@
g_obj.fillin(space)
pointers = g_self.get_pointers()
# TODO -- Also handle weak objects loaded from images.
- from spyvm.shadow import find_storage_for_objects
+ from spyvm.storage import find_storage_for_objects
storage = find_storage_for_objects(space, pointers, g_self.isweak())(space, self, len(pointers))
self.store_shadow(storage)
self.store_all(space, pointers)
self.log_storage("Filledin", log_classname=False)
def is_weak(self):
- from shadow import WeakListStorageShadow
+ from storage import WeakListStorageShadow
return isinstance(self.shadow, WeakListStorageShadow)
def is_class(self, space):
- from spyvm.shadow import ClassShadow
+ from spyvm.storage_classes import ClassShadow
if isinstance(self.shadow, ClassShadow):
return True
return W_AbstractObjectWithClassReference.is_class(self, space)
@@ -701,23 +701,23 @@
return shadow
def get_shadow(self, space):
- from spyvm.shadow import AbstractShadow
+ from spyvm.storage import AbstractShadow
return self.as_special_get_shadow(space, AbstractShadow)
def as_class_get_shadow(self, space):
- from spyvm.shadow import ClassShadow
+ from spyvm.storage_classes import ClassShadow
return jit.promote(self.as_special_get_shadow(space, ClassShadow))
def as_blockcontext_get_shadow(self, space):
- from spyvm.shadow import BlockContextShadow
+ from spyvm.storage_contexts import BlockContextShadow
return self.as_special_get_shadow(space, BlockContextShadow)
def as_methodcontext_get_shadow(self, space):
- from spyvm.shadow import MethodContextShadow
+ from spyvm.storage_contexts import MethodContextShadow
return self.as_special_get_shadow(space, MethodContextShadow)
def as_context_get_shadow(self, space):
- from spyvm.shadow import ContextPartShadow
+ from spyvm.storage_contexts import ContextPartShadow
if not isinstance(self.shadow, ContextPartShadow):
if self.getclass(space).is_same_object(space.w_BlockContext):
return self.as_blockcontext_get_shadow(space)
@@ -727,15 +727,15 @@
return self.as_special_get_shadow(space, ContextPartShadow)
def as_methoddict_get_shadow(self, space):
- from spyvm.shadow import MethodDictionaryShadow
+ from spyvm.storage_classes import MethodDictionaryShadow
return self.as_special_get_shadow(space, MethodDictionaryShadow)
def as_cached_object_get_shadow(self, space):
- from spyvm.shadow import CachedObjectShadow
+ from spyvm.storage import CachedObjectShadow
return self.as_special_get_shadow(space, CachedObjectShadow)
def as_observed_get_shadow(self, space):
- from spyvm.shadow import ObserveeShadow
+ from spyvm.storage import ObserveeShadow
return self.as_special_get_shadow(space, ObserveeShadow)
def has_shadow(self):
@@ -1267,7 +1267,7 @@
return True
def create_frame(self, space, receiver, arguments=[]):
- from spyvm.shadow import MethodContextShadow
+ from spyvm.storage_contexts import MethodContextShadow
assert len(arguments) == self.argsize
return MethodContextShadow(space, w_method=self, w_receiver=receiver, arguments=arguments)
@@ -1313,7 +1313,7 @@
# methods in order to avoid side effects that prevent translation.
w_class = self.safe_compiled_in()
if isinstance(w_class, W_PointersObject):
- from spyvm.shadow import ClassShadow
+ from spyvm.storage_classes import ClassShadow
s_class = w_class.shadow
if isinstance(s_class, ClassShadow):
return "%s >> #%s" % (s_class.getname(), self.lookup_selector)
diff --git a/spyvm/objspace.py b/spyvm/objspace.py
--- a/spyvm/objspace.py
+++ b/spyvm/objspace.py
@@ -1,6 +1,6 @@
import os
-from spyvm import constants, model, model_display, shadow, wrapper, version, display
+from spyvm import constants, model, model_display, wrapper, version, display
from spyvm.error import UnwrappingError, WrappingError, PrimitiveFailedError
from rpython.rlib import jit, rpath
from rpython.rlib.objectmodel import instantiate, specialize, import_from_mixin
diff --git a/spyvm/plugins/bitblt.py b/spyvm/plugins/bitblt.py
--- a/spyvm/plugins/bitblt.py
+++ b/spyvm/plugins/bitblt.py
@@ -1,6 +1,6 @@
from spyvm import model_display, model
from spyvm.error import PrimitiveFailedError
-from spyvm.shadow import AbstractCachingShadow
+from spyvm.storage import AbstractCachingShadow
from spyvm.plugins.plugin import Plugin
from rpython.rlib import jit, objectmodel
diff --git a/spyvm/primitives.py b/spyvm/primitives.py
--- a/spyvm/primitives.py
+++ b/spyvm/primitives.py
@@ -2,7 +2,7 @@
import inspect
import math
import operator
-from spyvm import model, model_display, shadow, error, constants, display
+from spyvm import model, model_display, storage_contexts, error, constants, display
from spyvm.error import PrimitiveFailedError, PrimitiveNotYetWrittenError
from spyvm import wrapper
@@ -947,7 +947,7 @@
def func(interp, s_frame, w_rcvr):
# This takes a long time (at least in interpreted mode), and is not really necessary.
# We are monitoring changes to MethodDictionaries, so there is no need for the image to tell us.
- #walk_gc_objects_of_type(shadow.MethodDictionaryShadow, lambda s_dict: s_dict.flush_method_cache())
+ #walk_gc_objects_of_type(storage_contexts.MethodDictionaryShadow, lambda s_dict: s_dict.flush_method_cache())
return w_rcvr
# ___________________________________________________________________________
@@ -1292,7 +1292,7 @@
# The block bytecodes are stored inline: so we skip past the
# byteodes to invoke this primitive to find them (hence +2)
initialip = s_frame.pc() + 2
- s_new_context = shadow.BlockContextShadow(interp.space, None, 0, w_method_context, argcnt, initialip)
+ s_new_context = storage_contexts.BlockContextShadow(interp.space, None, 0, w_method_context, argcnt, initialip)
return s_new_context.w_self()
@expose_primitive(VALUE, result_is_new_frame=True)
diff --git a/spyvm/shadow.py b/spyvm/shadow.py
deleted file mode 100644
--- a/spyvm/shadow.py
+++ /dev/null
@@ -1,1312 +0,0 @@
-import sys, weakref
-from spyvm import model, constants, error, wrapper, version
-from spyvm.version import elidable_for_version, constant_for_version, constant_for_version_arg
-from rpython.tool.pairtype import extendabletype
-from rpython.rlib import rarithmetic, objectmodel, jit, longlong2float
-from rpython.rlib.objectmodel import import_from_mixin
-from rpython.rlib.debug import make_sure_not_resized
-from rpython.rlib.rstruct.runpack import runpack
-from rpython.rtyper.lltypesystem import rffi, lltype
-
-class AbstractShadow(object):
- """A shadow is an optional extra bit of information that
- can be attached at run-time to any Smalltalk object.
- """
- _attrs_ = ['_w_self', 'space']
- _immutable_fields_ = ['space']
- provides_getname = False
- repr_classname = "AbstractShadow"
-
- def __init__(self, space, w_self, size):
- self.space = space
- assert w_self is None or isinstance(w_self, model.W_PointersObject)
- self._w_self = w_self
- def w_self(self):
- return self._w_self
- def getname(self):
- raise NotImplementedError("Abstract class")
- def __repr__(self):
- if self.provides_getname:
- return "<%s %s>" % (self.repr_classname, self.getname())
- else:
- return "<%s>" % self.repr_classname
-
- def fetch(self, n0):
- raise NotImplementedError("Abstract class")
- def store(self, n0, w_value):
- raise NotImplementedError("Abstract class")
- def size(self):
- raise NotImplementedError("Abstract class")
-
- # This will invoke an appropriate copy_from_* method.
- # Overwriting this allows optimized transitions between certain storage types.
- def copy_into(self, other_shadow):
- other_shadow.copy_from(self)
-
- def attach_shadow(self): pass
-
- def copy_field_from(self, n0, other_shadow):
- self.store(n0, other_shadow.fetch(n0))
-
- def copy_from(self, other_shadow):
- assert self.size() == other_shadow.size()
- for i in range(self.size()):
- self.copy_field_from(i, other_shadow)
-
- def copy_from_AllNil(self, all_nil_storage):
- self.copy_from(all_nil_storage)
- def copy_from_SmallIntegerOrNil(self, small_int_storage):
- self.copy_from(small_int_storage)
- def copy_from_FloatOrNil(self, float_storage):
- self.copy_from(float_storage)
-
-class AbstractStorageShadow(AbstractShadow):
- _attrs_ = []
- repr_classname = "AbstractStorageShadow"
- def __init__(self, space, w_self, size):
- AbstractShadow.__init__(self, space, w_self, size)
- def store(self, n0, w_val):
- if self.can_contain(w_val):
- return self.do_store(n0, w_val)
- new_storage = self.generalized_strategy_for(w_val)
- return self._w_self.store_with_new_storage(new_storage, n0, w_val)
- def can_contain(self, w_val):
- return self.static_can_contain(self.space, w_val)
- @staticmethod
- def static_can_contain(space, w_val):
- raise NotImplementedError()
- def do_store(self, n0, w_val):
- raise NotImplementedError()
- def generalized_strategy_for(self, w_val):
- raise NotImplementedError()
-
- def copy_from_AllNil(self, all_nil_storage):
- pass # Already initialized
- def copy_from(self, other_shadow):
- assert self.size() == other_shadow.size()
- for i in range(self.size()):
- w_val = other_shadow.fetch(i)
- if not w_val.is_nil(self.space): # nil fields already initialized
- self.store(i, w_val)
-
-class AllNilStorageShadow(AbstractStorageShadow):
- repr_classname = "AllNilStorageShadow"
- _attrs_ = ['_size']
- _immutable_fields_ = ['_size']
- def __init__(self, space, w_self, size):
- AbstractStorageShadow.__init__(self, space, w_self, size)
- self._size = size
- def fetch(self, n0):
- if n0 >= self._size:
- raise IndexError
- return self.space.w_nil
- def copy_into(self, other_shadow):
- other_shadow.copy_from_AllNil(self)
- def do_store(self, n0, w_value):
- pass
- def size(self):
- return self._size
- def generalized_strategy_for(self, w_val):
- return find_storage_for_objects(self.space, [w_val])
- @staticmethod
- def static_can_contain(space, w_val):
- return isinstance(w_val, model.W_Object) and w_val.is_nil(space)
-
-class AbstractValueOrNilStorageMixin(object):
- # Class must provide: wrap, unwrap, nil_value, is_nil_value, wrapper_class
- _attrs_ = ['storage']
- _immutable_fields_ = ['storage']
-
- def __init__(self, space, w_self, size):
- AbstractStorageShadow.__init__(self, space, w_self, size)
- self.storage = [self.nil_value] * size
-
- def size(self):
- return len(self.storage)
-
- def generalized_strategy_for(self, w_val):
- return ListStorageShadow
-
- def fetch(self, n0):
- val = self.storage[n0]
- if self.is_nil_value(val):
- return self.space.w_nil
- else:
- return self.wrap(self.space, val)
-
- def do_store(self, n0, w_val):
- if w_val.is_nil(self.space):
- self.storage[n0] = self.nil_value
- else:
- self.storage[n0] = self.unwrap(self.space, w_val)
-
-# This is to avoid code duplication
- at objectmodel.specialize.arg(0)
-def _value_or_nil_can_handle(cls, space, w_val):
- return isinstance(w_val, model.W_Object) and w_val.is_nil(space) or \
- (isinstance(w_val, cls.wrapper_class) \
- and not cls.is_nil_value(cls.unwrap(space, w_val)))
-
-class SmallIntegerOrNilStorageShadow(AbstractStorageShadow):
- repr_classname = "SmallIntegerOrNilStorageShadow"
- nil_value = constants.MAXINT
- wrapper_class = model.W_SmallInteger
- import_from_mixin(AbstractValueOrNilStorageMixin)
-
- @staticmethod
- def static_can_contain(space, w_val):
- return _value_or_nil_can_handle(SmallIntegerOrNilStorageShadow, space, w_val)
- @staticmethod
- def is_nil_value(val):
- return val == SmallIntegerOrNilStorageShadow.nil_value
- @staticmethod
- def wrap(space, val):
- return space.wrap_int(val)
- @staticmethod
- def unwrap(space, w_val):
- return space.unwrap_int(w_val)
- def copy_into(self, other_shadow):
- other_shadow.copy_from_SmallIntegerOrNil(self)
-
-class FloatOrNilStorageShadow(AbstractStorageShadow):
- repr_classname = "FloatOrNilStorageShadow"
- nil_value = sys.float_info.max
- wrapper_class = model.W_Float
- import_from_mixin(AbstractValueOrNilStorageMixin)
-
- @staticmethod
- def static_can_contain(space, w_val):
- return _value_or_nil_can_handle(FloatOrNilStorageShadow, space, w_val)
- @staticmethod
- def is_nil_value(val):
- return val == FloatOrNilStorageShadow.nil_value
- @staticmethod
- def wrap(space, val):
- return space.wrap_float(val)
- @staticmethod
- def unwrap(space, w_val):
- return space.unwrap_float(w_val)
- def copy_into(self, other_shadow):
- other_shadow.copy_from_FloatOrNil(self)
-
-def empty_storage(space, w_self, size, weak=False):
- if weak:
- return WeakListStorageShadow(space, w_self, size)
- if space.no_specialized_storage.is_set():
- return ListStorageShadow(space, w_self, size)
- return AllNilStorageShadow(space, w_self, size)
-
- at jit.unroll_safe
-def find_storage_for_objects(space, vars, weak=False):
- if weak:
- return WeakListStorageShadow
- if space.no_specialized_storage.is_set():
- return ListStorageShadow
- specialized_strategies = 3
- all_nil_can_handle = True
- small_int_can_handle = True
- float_can_handle = True
- for w_obj in vars:
- if all_nil_can_handle and not AllNilStorageShadow.static_can_contain(space, w_obj):
- all_nil_can_handle = False
- specialized_strategies = specialized_strategies - 1
- if small_int_can_handle and not SmallIntegerOrNilStorageShadow.static_can_contain(space, w_obj):
- small_int_can_handle = False
- specialized_strategies = specialized_strategies - 1
- if float_can_handle and not FloatOrNilStorageShadow.static_can_contain(space, w_obj):
- float_can_handle = False
- specialized_strategies = specialized_strategies - 1
-
- if specialized_strategies <= 0:
- return ListStorageShadow
-
- if all_nil_can_handle:
- return AllNilStorageShadow
- if small_int_can_handle:
- return SmallIntegerOrNilStorageShadow
- if float_can_handle:
- return FloatOrNilStorageShadow
-
- # If this happens, please look for a bug in the code above.
- assert False, "No strategy could be found for list..."
-
-class ListStorageMixin(object):
- def __init__(self, space, w_self, size):
- AbstractStorageShadow.__init__(self, space, w_self, size)
- self.initialize_storage(size)
- def size(self):
- return len(self.storage)
-
-class ListStorageShadow(AbstractStorageShadow):
- _attrs_ = ['storage']
- _immutable_fields_ = ['storage']
- repr_classname = "ListStorageShadow"
- import_from_mixin(ListStorageMixin)
-
- def initialize_storage(self, size):
- self.storage = [self.space.w_nil] * size
- def fetch(self, n0):
- return self.storage[n0]
- def store(self, n0, w_value):
- self.storage[n0] = w_value
-
-class WeakListStorageShadow(AbstractStorageShadow):
- _attrs_ = ['storage']
- _immutable_fields_ = ['storage']
- repr_classname = "WeakListStorageShadow"
- import_from_mixin(ListStorageMixin)
-
- def initialize_storage(self, size):
- self.storage = [weakref.ref(self.space.w_nil)] * size
- def fetch(self, n0):
- weakobj = self.storage[n0]
- return weakobj() or self.space.w_nil
- def store(self, n0, w_value):
- assert w_value is not None
- self.storage[n0] = weakref.ref(w_value)
-
-class AbstractCachingShadow(ListStorageShadow):
- _immutable_fields_ = ['version?']
- _attrs_ = ['version']
- repr_classname = "AbstractCachingShadow"
- import_from_mixin(version.VersionMixin)
- version = None
-
- def __init__(self, space, w_self, size):
- ListStorageShadow.__init__(self, space, w_self, size)
- self.changed()
-
-# ____________________________________________________________
-
-POINTERS = 0
-BYTES = 1
-WORDS = 2
-WEAK_POINTERS = 3
-COMPILED_METHOD = 4
-FLOAT = 5
-LARGE_POSITIVE_INTEGER = 6
-
-class ClassShadowError(error.SmalltalkException):
- exception_type = "ClassShadowError"
-
-class ClassShadow(AbstractCachingShadow):
- """A shadow for Smalltalk objects that are classes
- (i.e. used as the class of another Smalltalk object).
- """
-
- _attrs_ = ["name", "_instance_size", "instance_varsized", "instance_kind",
- "_s_methoddict", "_s_superclass", "subclass_s"]
- name = '??? (incomplete class info)'
- _s_superclass = _s_methoddict = None
- provides_getname = True
- repr_classname = "ClassShadow"
-
- def __init__(self, space, w_self, size):
- self.subclass_s = {}
- AbstractCachingShadow.__init__(self, space, w_self, size)
-
- def store(self, n0, w_val):
- AbstractCachingShadow.store(self, n0, w_val)
- if n0 == constants.CLASS_SUPERCLASS_INDEX:
- self.store_w_superclass(w_val)
- elif n0 == constants.CLASS_METHODDICT_INDEX:
- self.store_w_methoddict(w_val)
- elif n0 == constants.CLASS_FORMAT_INDEX:
- # read and painfully decode the format
- assert isinstance(w_val, model.W_SmallInteger)
- classformat = self.space.unwrap_int(w_val)
- # 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
-
- # In case of raised exception below.
- self.changed()
-
- if format < 4:
- self.instance_kind = POINTERS
- elif format == 4:
- self.instance_kind = WEAK_POINTERS
- elif format == 6:
- if self.space.w_Float.is_same_object(self.w_self()):
- self.instance_kind = FLOAT
- else:
- 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:
- if self.space.w_LargePositiveInteger.is_same_object(self.w_self()):
- self.instance_kind = LARGE_POSITIVE_INTEGER
- else:
- 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,))
- else:
- if self._w_self.w_class == self.space.classtable["w_Metaclass"]:
- # In case of Metaclasses, the "instance" class is stored in the last field.
- if n0 == self.size() - 1 and isinstance(w_val, model.W_PointersObject):
- cl_shadow = w_val.as_class_get_shadow(self.space)
- self.name = "%s class" % cl_shadow.getname()
- else:
- return
- elif n0 == constants.CLASS_NAME_INDEX:
- # In case of regular classes, the name is stored here.
- self.store_w_name(w_val)
- else:
- return
- # Some of the special info has changed -> Switch version.
- self.changed()
-
- def store_w_superclass(self, w_class):
- superclass = self._s_superclass
- if w_class is None or w_class.is_nil(self.space):
- if superclass: superclass.detach_s_class(self)
- self._s_superclass = None
- else:
- assert isinstance(w_class, model.W_PointersObject)
- s_new_superclass = w_class.as_class_get_shadow(self.space)
- if superclass is s_new_superclass:
- return
- if superclass: superclass.detach_s_class(self)
- self._s_superclass = s_new_superclass
- s_new_superclass.attach_s_class(self)
-
- def store_w_methoddict(self, w_methoddict):
- methoddict = self._s_methoddict
- if w_methoddict is None or w_methoddict.is_nil(self.space):
- if methoddict: methoddict.s_class = None
- self._s_methoddict = None
- else:
- assert isinstance(w_methoddict, model.W_PointersObject)
- s_new_methoddict = w_methoddict.as_methoddict_get_shadow(self.space)
- if methoddict is s_new_methoddict:
- return
- if methoddict: methoddict.s_class = None
- self.store_s_methoddict(s_new_methoddict)
-
- def store_s_methoddict(self, s_methoddict):
- s_methoddict.s_class = self
- s_methoddict.sync_method_cache()
- self._s_methoddict = s_methoddict
-
- 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 store_w_name(self, w_name):
- if isinstance(w_name, model.W_BytesObject):
- self.name = w_name.as_string()
- else:
- self.name = None
-
- @jit.unroll_safe
- def flush_method_caches(self):
- look_in_shadow = self
- while look_in_shadow is not None:
- look_in_shadow.s_methoddict().flush_method_cache()
- look_in_shadow = look_in_shadow._s_superclass
-
- def new(self, extrasize=0):
- w_cls = self.w_self()
- if self.instance_kind == POINTERS:
- size = self.instsize() + extrasize
- w_new = model.W_PointersObject(self.space, w_cls, size)
- elif self.instance_kind == WORDS:
- w_new = model.W_WordsObject(self.space, w_cls, extrasize)
- elif self.instance_kind == BYTES:
- w_new = model.W_BytesObject(self.space, w_cls, extrasize)
- elif self.instance_kind == COMPILED_METHOD:
- w_new = model.W_CompiledMethod(self.space, extrasize)
- elif self.instance_kind == FLOAT:
- w_new = model.W_Float(0) # Squeak gives a random piece of memory
- elif self.instance_kind == LARGE_POSITIVE_INTEGER:
- if extrasize <= 4:
- w_new = model.W_LargePositiveInteger1Word(0, extrasize)
- else:
- w_new = model.W_BytesObject(self.space, w_cls, extrasize)
- elif self.instance_kind == WEAK_POINTERS:
- size = self.instsize() + extrasize
- w_new = model.W_PointersObject(self.space, w_cls, size, weak=True)
- else:
- raise NotImplementedError(self.instance_kind)
- return w_new
-
- def w_methoddict(self):
- return self._s_methoddict.w_self()
-
- def s_methoddict(self):
- return self._s_methoddict
-
- def s_superclass(self):
- return self._s_superclass
-
- def getname(self):
- return self.name
-
- # _______________________________________________________________
- # Methods for querying the format word, taken from the blue book:
- #
- # included so that we can reproduce code from the reference impl
- # more easily
-
- def ispointers(self):
- " True if instances of this class have data stored as pointers "
- XXX # what about weak pointers?
- return self.format == POINTERS
-
- def iswords(self):
- " True if instances of this class have data stored as numerical words "
- XXX # what about weak pointers?
- return self.format in (POINTERS, WORDS)
-
- def isbytes(self):
- " True if instances of this class have data stored as numerical bytes "
- return self.format == BYTES
-
- @constant_for_version
- def isvariable(self):
- " True if instances of this class have indexed inst variables "
- return self.instance_varsized
-
- @constant_for_version
- def instsize(self):
- " Number of named instance variables for each instance of this class "
- return self._instance_size
-
- # _______________________________________________________________
- # Other Methods
-
- @constant_for_version_arg
- def lookup(self, w_selector):
- look_in_shadow = self
- 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
- look_in_shadow = look_in_shadow._s_superclass
- raise error.MethodNotFound()
-
- def changed(self):
- self.superclass_changed(version.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 used only in testing
-
- def inherits_from(self, s_superclass):
- "NOT_RPYTHON" # this is only for testing.
- classshadow = self
- while classshadow is not None:
- if classshadow is s_superclass:
- return True
- classshadow = classshadow.s_superclass()
- else:
- return False
-
- def initialize_methoddict(self):
- "NOT_RPYTHON" # this is only for testing.
- if self._s_methoddict is None:
- w_methoddict = model.W_PointersObject(self.space, None, 2)
- w_methoddict.store(self.space, constants.METHODDICT_VALUES_INDEX, model.W_PointersObject(self.space, None, 0))
- self.store_s_methoddict(w_methoddict.as_methoddict_get_shadow(self.space))
- 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
- if isinstance(w_method, model.W_CompiledMethod):
- w_method.compiledin_class = self.w_self()
-
-class MethodDictionaryShadow(ListStorageShadow):
-
- _immutable_fields_ = ['invalid?', 's_class']
- _attrs_ = ['methoddict', 'invalid', 's_class']
- repr_classname = "MethodDictionaryShadow"
-
- def __init__(self, space, w_self, size):
- self.invalid = True
- self.s_class = None
- self.methoddict = {}
- ListStorageShadow.__init__(self, space, w_self, size)
-
- def update(self):
- self.sync_method_cache()
-
- def find_selector(self, w_selector):
- if self.invalid:
- return None # we may be invalid if Smalltalk code did not call flushCache
- return self.methoddict.get(w_selector, None)
-
- # We do not call update() after 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_method_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):
- ListStorageShadow.store(self, n0, w_value)
- if n0 == constants.METHODDICT_VALUES_INDEX:
- self.setup_notification()
- if n0 >= constants.METHODDICT_NAMES_INDEX:
- self.invalid = True
-
- def setup_notification(self):
- self.w_values().as_observed_get_shadow(self.space).notify(self)
-
- def w_values(self):
- w_values = self.fetch(constants.METHODDICT_VALUES_INDEX)
- assert isinstance(w_values, model.W_PointersObject)
- return w_values
-
- def flush_method_cache(self):
- # Lazy synchronization: Only flush the cache, if we are already synchronized.
- if self.invalid:
- self.sync_method_cache()
-
- def sync_method_cache(self):
- if self.size() == 0:
- return
- self.methoddict = {}
- size = self.size() - constants.METHODDICT_NAMES_INDEX
- w_values = self.w_values()
- for i in range(size):
- w_selector = self.w_self().fetch(self.space, constants.METHODDICT_NAMES_INDEX+i)
- if not w_selector.is_nil(self.space):
- if isinstance(w_selector, model.W_BytesObject):
- selector = w_selector.as_string()
- else:
- selector = "? (non-byteobject selector)"
- pass
- # TODO: Check if there's more assumptions about this.
- # Putting any key in the methodDict and running with
- # perform is actually supported in Squeak
- # raise ClassShadowError("bogus selector in method dict")
- w_compiledmethod = w_values.fetch(self.space, i)
- if not isinstance(w_compiledmethod, model.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
- w_compiledmethod.set_lookup_class_and_name(self.s_class.w_self(), selector)
- if self.s_class:
- self.s_class.changed()
- self.invalid = False
-
-class AbstractRedirectingShadow(AbstractShadow):
- _attrs_ = ['_w_self_size']
- repr_classname = "AbstractRedirectingShadow"
-
- def __init__(self, space, w_self, size):
- if w_self is not None:
- self._w_self_size = w_self.size()
- else:
- self._w_self_size = size
- AbstractShadow.__init__(self, space, w_self, self._w_self_size)
-
- def size(self):
- return self._w_self_size
-
-class ContextState(object):
- def __init__(self, name):
- self.name = name
- def __str__(self):
- return self.name
- def __repr__(self):
- return self.name
-InactiveContext = ContextState("InactiveContext")
-ActiveContext = ContextState("ActiveContext")
-DirtyContext = ContextState("DirtyContext")
-
-class ContextPartShadow(AbstractRedirectingShadow):
-
- __metaclass__ = extendabletype
- _attrs_ = ['_s_sender',
- '_pc', '_temps_and_stack',
- '_stack_ptr', 'instances_w', 'state']
- repr_classname = "ContextPartShadow"
-
- _virtualizable_ = [
- '_s_sender',
- "_pc", "_temps_and_stack[*]", "_stack_ptr",
- "_w_self", "_w_self_size", 'state'
- ]
-
- # ______________________________________________________________________
- # Initialization
-
- def __init__(self, space, w_self, size=0):
- self._s_sender = None
- AbstractRedirectingShadow.__init__(self, space, w_self, size)
- self.instances_w = {}
- self.state = InactiveContext
-
- def copy_from(self, other_shadow):
- # Some fields have to be initialized before the rest, to ensure correct initialization.
- privileged_fields = self.fields_to_copy_first()
- for n0 in privileged_fields:
- self.copy_field_from(n0, other_shadow)
-
- # Now the temp size will be known.
- self.init_stack_and_temps()
-
- for n0 in range(self.size()):
- if n0 not in privileged_fields:
- self.copy_field_from(n0, other_shadow)
-
- def fields_to_copy_first(self):
- return []
-
- # ______________________________________________________________________
- # Accessing object fields
-
- def fetch(self, n0):
- if n0 == constants.CTXPART_SENDER_INDEX:
- return self.w_sender()
- if n0 == constants.CTXPART_PC_INDEX:
- return self.wrap_pc()
- if n0 == constants.CTXPART_STACKP_INDEX:
- return self.wrap_stackpointer()
- if self.stackstart() <= n0 < self.external_stackpointer():
- temp_i = self.stackdepth() - (n0-self.stackstart()) - 1
- assert temp_i >= 0
- return self.peek(temp_i)
- if self.external_stackpointer() <= n0 < self.stackend():
- return self.space.w_nil
- else:
- # XXX later should store tail out of known context part as well
- raise error.WrapperException("Index in context out of bounds")
-
- def store(self, n0, w_value):
- if n0 == constants.CTXPART_SENDER_INDEX:
- assert isinstance(w_value, model.W_PointersObject)
- if w_value.is_nil(self.space):
- self.store_s_sender(None)
- else:
- self.store_s_sender(w_value.as_context_get_shadow(self.space))
- return
- if n0 == constants.CTXPART_PC_INDEX:
- return self.store_unwrap_pc(w_value)
- if n0 == constants.CTXPART_STACKP_INDEX:
- return self.unwrap_store_stackpointer(w_value)
- if self.stackstart() <= n0 < self.external_stackpointer(): # XXX can be simplified?
- temp_i = self.stackdepth() - (n0-self.stackstart()) - 1
- assert temp_i >= 0
- return self.set_top(w_value, temp_i)
- if self.external_stackpointer() <= n0 < self.stackend():
- return
- else:
- # XXX later should store tail out of known context part as well
- raise error.WrapperException("Index in context out of bounds")
-
- # === Sender ===
-
- def store_s_sender(self, s_sender):
- if s_sender is not self._s_sender:
- self._s_sender = s_sender
- # If new sender is None, we are just being marked as returned.
- if s_sender is not None and self.state is ActiveContext:
- self.state = DirtyContext
-
- def w_sender(self):
- sender = self.s_sender()
- if sender is None:
- return self.space.w_nil
- return sender.w_self()
-
- def s_sender(self):
- return self._s_sender
-
- # === Stack Pointer ===
-
- def unwrap_store_stackpointer(self, w_sp1):
- # the stackpointer in the W_PointersObject starts counting at the
- # tempframe start
- # Stackpointer from smalltalk world == stacksize in python world
- self.store_stackpointer(self.space.unwrap_int(w_sp1))
-
- def store_stackpointer(self, size):
- depth = self.stackdepth()
- if size < depth:
- # TODO Warn back to user
- assert size >= 0
- self.pop_n(depth - size)
- else:
- for i in range(depth, size):
- self.push(self.space.w_nil)
-
- def stackdepth(self):
- return rarithmetic.intmask(self._stack_ptr)
-
- def wrap_stackpointer(self):
- return self.space.wrap_int(self.stackdepth())
-
- # === Program Counter ===
-
- def store_unwrap_pc(self, w_pc):
- if w_pc.is_nil(self.space):
- self.store_pc(-1)
- else:
- pc = self.space.unwrap_int(w_pc)
- pc -= self.w_method().bytecodeoffset()
- pc -= 1
- self.store_pc(pc)
-
- def wrap_pc(self):
- pc = self.pc()
- if pc == -1:
- return self.space.w_nil
- else:
- pc += 1
- pc += self.w_method().bytecodeoffset()
- return self.space.wrap_int(pc)
-
- def pc(self):
- return self._pc
-
- def store_pc(self, newpc):
- assert newpc >= -1
- self._pc = newpc
-
- # === Subclassed accessors ===
-
- def s_home(self):
- raise NotImplementedError()
-
- def stackstart(self):
- raise NotImplementedError()
-
- def w_receiver(self):
- raise NotImplementedError()
-
- def w_method(self):
- raise NotImplementedError()
-
- def tempsize(self):
- raise NotImplementedError()
-
- def is_closure_context(self):
- raise NotImplementedError()
-
- def is_BlockClosure_ensure(self):
- raise NotImplementedError()
-
- def home_is_self(self):
- raise NotImplementedError()
-
- # === Other properties of Contexts ===
-
- def mark_returned(self):
- self.store_pc(-1)
- self.store_s_sender(None)
-
- def is_returned(self):
- return self.pc() == -1 and self.w_sender().is_nil(self.space)
-
- def external_stackpointer(self):
- return self.stackdepth() + self.stackstart()
-
- def stackend(self):
- # XXX this is incorrect when there is subclassing
- return self._w_self_size
-
- def fetch_next_bytecode(self):
- pc = jit.promote(self._pc)
- assert pc >= 0
- self._pc += 1
- return self.fetch_bytecode(pc)
-
- def fetch_bytecode(self, pc):
- bytecode = self.w_method().fetch_bytecode(pc)
- return ord(bytecode)
-
- # ______________________________________________________________________
- # Temporary Variables
- #
- # Every context has it's own stack. BlockContexts share their temps with
- # their home contexts. MethodContexts created from a BlockClosure get their
- # temps copied from the closure upon activation. Changes are not propagated back;
- # this is handled by the compiler by allocating an extra Array for temps.
-
- def gettemp(self, index):
- raise NotImplementedError()
-
- def settemp(self, index, w_value):
- raise NotImplementedError()
-
- # ______________________________________________________________________
- # Stack Manipulation
-
- @jit.unroll_safe
- def init_stack_and_temps(self):
- stacksize = self.stackend() - self.stackstart()
- tempsize = self.tempsize()
- temps_and_stack = [None] * (stacksize + tempsize)
- self._temps_and_stack = temps_and_stack
- make_sure_not_resized(temps_and_stack)
- for i in range(tempsize):
- temps_and_stack[i] = self.space.w_nil
- self._stack_ptr = rarithmetic.r_uint(tempsize) # we point after the last element
-
- def stack_get(self, index0):
- return self._temps_and_stack[index0]
-
- def stack_put(self, index0, w_val):
- self._temps_and_stack[index0] = w_val
-
- def stack(self):
- """NOT_RPYTHON""" # purely for testing
- return self._temps_and_stack[self.tempsize():self._stack_ptr]
-
- def pop(self):
- #assert self._stack_ptr > self.tempsize()
- ptr = jit.promote(self._stack_ptr) - 1
- ret = self.stack_get(ptr) # you get OverflowError if the stack is empty
- self.stack_put(ptr, None)
- self._stack_ptr = ptr
- return ret
-
- def push(self, w_v):
- #assert self._stack_ptr >= self.tempsize()
- #assert self._stack_ptr < self.stackend() - self.stackstart() + self.tempsize()
- ptr = jit.promote(self._stack_ptr)
- self.stack_put(ptr, w_v)
- self._stack_ptr = ptr + 1
-
- @jit.unroll_safe
- def push_all(self, lst):
- for elt in lst:
- self.push(elt)
-
- def top(self):
- return self.peek(0)
-
- def set_top(self, value, position=0):
- rpos = rarithmetic.r_uint(position)
- ptr = self._stack_ptr + ~rpos
- self.stack_put(ptr, value)
-
- def peek(self, idx):
- rpos = rarithmetic.r_uint(idx)
- ptr = jit.promote(self._stack_ptr) + ~rpos
- return self.stack_get(ptr)
-
- @jit.unroll_safe
- def pop_n(self, n):
- #assert n == 0 or self._stack_ptr - n >= self.tempsize()
- jit.promote(self._stack_ptr)
- while n > 0:
- n -= 1
- self._stack_ptr -= 1
- self.stack_put(self._stack_ptr, None)
-
- @jit.unroll_safe
- def pop_and_return_n(self, n):
- result = [self.peek(i) for i in range(n - 1, -1, -1)]
- self.pop_n(n)
- return result
-
- # ______________________________________________________________________
- # Primitive support
-
- def store_instances_array(self, w_class, match_w):
- # used for primitives 77 & 78
- self.instances_w[w_class] = match_w
-
- @jit.elidable
- def instances_array(self, w_class):
- return self.instances_w.get(w_class, None)
-
- # ______________________________________________________________________
- # Printing
-
- def argument_strings(self):
- return [ w_arg.as_repr_string() for w_arg in self.w_arguments() ]
-
- def __str__(self):
- retval = self.short_str()
- retval += "\n%s" % self.w_method().bytecode_string(markBytecode=self.pc() + 1)
- retval += "\nArgs:----------------"
- argcount = self.w_method().argsize
- j = 0
- for w_obj in self._temps_and_stack[:self._stack_ptr]:
- if j == argcount:
- retval += "\nTemps:---------------"
- if j == self.tempsize():
- retval += "\nStack:---------------"
- retval += "\n %0.2i: %s" % (j, w_obj.as_repr_string())
- j += 1
- retval += "\n---------------------"
- return retval
-
- def short_str(self):
- arg_strings = self.argument_strings()
- if len(arg_strings) > 0:
- args = " , ".join(arg_strings)
- args = " (%d arg(s): %s)" % (len(arg_strings), args)
- else:
- args = ""
- return '%s [pc: %d] (rcvr: %s)%s' % (
- self.method_str(),
- self.pc() + 1,
- self.w_receiver().as_repr_string(),
- args
- )
-
- def print_stack(self, method=True):
- return self.print_padded_stack(method)[1]
-
- def print_padded_stack(self, method):
- padding = ret_str = ''
- if self.s_sender() is not None:
- padding, ret_str = self.s_sender().print_padded_stack(method)
- if method:
- desc = self.method_str()
- else:
- desc = self.short_str()
- return padding + ' ', '%s\n%s%s' % (ret_str, padding, desc)
-
-class BlockContextShadow(ContextPartShadow):
- _attrs_ = ['_w_home', '_initialip', '_eargc']
- repr_classname = "BlockContextShadow"
-
- # === Initialization ===
-
- def __init__(self, space, w_self=None, size=0, w_home=None, argcnt=0, initialip=0):
- self = jit.hint(self, access_directly=True, fresh_virtualizable=True)
- creating_w_self = w_self is None
- if creating_w_self:
- s_home = w_home.as_methodcontext_get_shadow(space)
- contextsize = s_home.size() - s_home.tempsize()
- w_self = model.W_PointersObject(space, space.w_BlockContext, contextsize)
- ContextPartShadow.__init__(self, space, w_self, size)
- if creating_w_self:
- w_self.store_shadow(self)
- self.store_expected_argument_count(argcnt)
- self.store_initialip(initialip)
- if w_home:
- self.store_w_home(w_home)
- self.store_pc(initialip)
- self.init_stack_and_temps()
-
- def fields_to_copy_first(self):
- return [ constants.BLKCTX_HOME_INDEX ]
-
- # === Implemented accessors ===
-
- def s_home(self):
- return self._w_home.as_methodcontext_get_shadow(self.space)
-
- def stackstart(self):
- return constants.BLKCTX_STACK_START
-
- def tempsize(self):
- # A blockcontext doesn't have any temps
- return 0
-
- def w_receiver(self):
- return self.s_home().w_receiver()
-
- def w_method(self):
- retval = self.s_home().w_method()
- assert isinstance(retval, model.W_CompiledMethod)
- return retval
-
- def is_closure_context(self):
- return True
-
- def is_BlockClosure_ensure(self):
- return False
-
- def home_is_self(self):
- return False
-
- # === Temporary variables ===
-
- def gettemp(self, index):
- return self.s_home().gettemp(index)
-
- def settemp(self, index, w_value):
- self.s_home().settemp(index, w_value)
-
- # === Accessing object fields ===
-
- def fetch(self, n0):
- if n0 == constants.BLKCTX_HOME_INDEX:
- return self._w_home
- if n0 == constants.BLKCTX_INITIAL_IP_INDEX:
- return self.wrap_initialip()
- if n0 == constants.BLKCTX_BLOCK_ARGUMENT_COUNT_INDEX:
- return self.wrap_eargc()
- else:
- return ContextPartShadow.fetch(self, n0)
-
- def store(self, n0, w_value):
- if n0 == constants.BLKCTX_HOME_INDEX:
- return self.store_w_home(w_value)
- if n0 == constants.BLKCTX_INITIAL_IP_INDEX:
- return self.unwrap_store_initialip(w_value)
- if n0 == constants.BLKCTX_BLOCK_ARGUMENT_COUNT_INDEX:
- return self.unwrap_store_eargc(w_value)
- else:
- return ContextPartShadow.store(self, n0, w_value)
-
- def store_w_home(self, w_home):
- assert isinstance(w_home, model.W_PointersObject)
- self._w_home = w_home
-
- def unwrap_store_initialip(self, w_value):
- initialip = self.space.unwrap_int(w_value)
- initialip -= 1 + self.w_method().literalsize
- self.store_initialip(initialip)
-
- def store_initialip(self, initialip):
- self._initialip = initialip
-
- def wrap_initialip(self):
- initialip = self.initialip()
- initialip += 1 + self.w_method().literalsize
- return self.space.wrap_int(initialip)
-
- def reset_pc(self):
- self.store_pc(self.initialip())
-
- def initialip(self):
- return self._initialip
-
- def unwrap_store_eargc(self, w_value):
- self.store_expected_argument_count(self.space.unwrap_int(w_value))
-
- def wrap_eargc(self):
- return self.space.wrap_int(self.expected_argument_count())
-
- def expected_argument_count(self):
- return self._eargc
-
- def store_expected_argument_count(self, argc):
- self._eargc = argc
-
- # === Stack Manipulation ===
-
- def reset_stack(self):
- self.pop_n(self.stackdepth())
-
- # === Printing ===
-
- def w_arguments(self):
- return []
-
- def method_str(self):
- return '[] in %s' % self.w_method().get_identifier_string()
-
-class MethodContextShadow(ContextPartShadow):
- _attrs_ = ['closure', '_w_receiver', '_w_method', '_is_BlockClosure_ensure']
- repr_classname = "MethodContextShadow"
-
- # === Initialization ===
-
- @jit.unroll_safe
- def __init__(self, space, w_self=None, size=0, w_method=None, w_receiver=None,
- arguments=[], closure=None, pc=0):
- self = jit.hint(self, access_directly=True, fresh_virtualizable=True)
- ContextPartShadow.__init__(self, space, w_self, size)
- self.store_w_receiver(w_receiver)
- self.store_pc(pc)
- self.closure = closure
-
- if w_method:
- self.store_w_method(w_method)
- # The summand is needed, because we calculate i.a. our stackdepth relative of the size of w_self.
- size = w_method.compute_frame_size() + self.space.w_MethodContext.as_class_get_shadow(self.space).instsize()
- self._w_self_size = size
- self.init_stack_and_temps()
- else:
- self._w_method = None
- self._is_BlockClosure_ensure = False
-
- argc = len(arguments)
- for i0 in range(argc):
- self.settemp(i0, arguments[i0])
-
- if closure:
- for i0 in range(closure.size()):
- self.settemp(i0+argc, closure.at0(i0))
-
- def fields_to_copy_first(self):
- return [ constants.MTHDCTX_METHOD, constants.MTHDCTX_CLOSURE_OR_NIL ]
-
- # === Accessing object fields ===
-
- def fetch(self, n0):
- if n0 == constants.MTHDCTX_METHOD:
- return self.w_method()
- if n0 == constants.MTHDCTX_CLOSURE_OR_NIL:
- if self.closure:
- return self.closure._w_self
- else:
- return self.space.w_nil
- if n0 == constants.MTHDCTX_RECEIVER:
- return self.w_receiver()
- temp_i = n0-constants.MTHDCTX_TEMP_FRAME_START
- if (0 <= temp_i < self.tempsize()):
- return self.gettemp(temp_i)
- else:
- return ContextPartShadow.fetch(self, n0)
-
- def store(self, n0, w_value):
- if n0 == constants.MTHDCTX_METHOD:
- return self.store_w_method(w_value)
- if n0 == constants.MTHDCTX_CLOSURE_OR_NIL:
- if w_value.is_nil(self.space):
- self.closure = None
- else:
- self.closure = wrapper.BlockClosureWrapper(self.space, w_value)
- return
- if n0 == constants.MTHDCTX_RECEIVER:
- self.store_w_receiver(w_value)
- return
- temp_i = n0-constants.MTHDCTX_TEMP_FRAME_START
- if (0 <= temp_i < self.tempsize()):
- return self.settemp(temp_i, w_value)
- else:
- return ContextPartShadow.store(self, n0, w_value)
-
- def store_w_receiver(self, w_receiver):
- self._w_receiver = w_receiver
-
- # === Implemented Accessors ===
-
- def s_home(self):
- if self.is_closure_context():
- # this is a context for a blockClosure
- w_outerContext = self.closure.outerContext()
- assert isinstance(w_outerContext, model.W_PointersObject)
- s_outerContext = w_outerContext.as_context_get_shadow(self.space)
- # XXX check whether we can actually return from that context
- if s_outerContext.is_returned():
- raise error.BlockCannotReturnError()
- return s_outerContext.s_home()
- else:
- return self
-
- def stackstart(self):
- return constants.MTHDCTX_TEMP_FRAME_START
-
- def store_w_method(self, w_method):
- assert isinstance(w_method, model.W_CompiledMethod)
- self._w_method = w_method
- if w_method:
- # Primitive 198 is used in BlockClosure >> ensure:
- self._is_BlockClosure_ensure = (w_method.primitive() == 198)
-
- def w_receiver(self):
- return self._w_receiver
-
- def w_method(self):
- retval = self._w_method
- assert isinstance(retval, model.W_CompiledMethod)
- return retval
-
- def tempsize(self):
- if not self.is_closure_context():
- return self.w_method().tempsize()
- else:
- return self.closure.tempsize()
-
- def is_closure_context(self):
- return self.closure is not None
-
- def is_BlockClosure_ensure(self):
- return self._is_BlockClosure_ensure
-
- def home_is_self(self):
- return not self.is_closure_context()
-
- # ______________________________________________________________________
- # Marriage of MethodContextShadows with PointerObjects only when required
-
- def w_self(self):
- if self._w_self is not None:
- return self._w_self
- else:
- s_MethodContext = self.space.w_MethodContext.as_class_get_shadow(self.space)
- size = self.size() - s_MethodContext.instsize()
- space = self.space
- w_self = s_MethodContext.new(size)
- assert isinstance(w_self, model.W_PointersObject)
- w_self.store_shadow(self)
- self._w_self = w_self
- self._w_self_size = w_self.size()
- return w_self
-
- # === Temporary variables ===
-
- def gettemp(self, index0):
- return self.stack_get(index0)
-
- def settemp(self, index0, w_value):
- self.stack_put(index0, w_value)
-
- # === Printing ===
-
- def w_arguments(self):
- argcount = self.w_method().argsize
- return [ self.stack_get(i) for i in range(argcount) ]
-
- def method_str(self):
- block = '[] in ' if self.is_closure_context() else ''
- return '%s%s' % (block, self.w_method().get_identifier_string())
-
-class CachedObjectShadow(AbstractCachingShadow):
- repr_classname = "CachedObjectShadow"
-
- @elidable_for_version
- def fetch(self, n0):
- return AbstractCachingShadow.fetch(self, n0)
-
- def store(self, n0, w_value):
- AbstractCachingShadow.store(self, n0, w_value)
- self.changed()
-
-class ObserveeShadow(ListStorageShadow):
- _attrs_ = ['dependent']
- repr_classname = "ObserveeShadow"
- def __init__(self, space, w_self, size):
- ListStorageShadow.__init__(self, space, w_self, size)
- self.dependent = None
-
- def store(self, n0, w_value):
- ListStorageShadow.store(self, n0, w_value)
- if self.dependent:
- 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
diff --git a/spyvm/storage.py b/spyvm/storage.py
new file mode 100644
--- /dev/null
+++ b/spyvm/storage.py
@@ -0,0 +1,319 @@
+
+import sys, weakref
+from spyvm import model, version, constants
+from spyvm.version import elidable_for_version
+from rpython.rlib import objectmodel, jit
+from rpython.rlib.objectmodel import import_from_mixin
+
+class AbstractShadow(object):
+ """A shadow is an optional extra bit of information that
+ can be attached at run-time to any Smalltalk object.
+ """
+ _attrs_ = ['_w_self', 'space']
+ _immutable_fields_ = ['space']
+ provides_getname = False
+ repr_classname = "AbstractShadow"
+
+ def __init__(self, space, w_self, size):
+ self.space = space
+ assert w_self is None or isinstance(w_self, model.W_PointersObject)
+ self._w_self = w_self
+ def w_self(self):
+ return self._w_self
+ def getname(self):
+ raise NotImplementedError("Abstract class")
+ def __repr__(self):
+ if self.provides_getname:
+ return "<%s %s>" % (self.repr_classname, self.getname())
+ else:
+ return "<%s>" % self.repr_classname
+
+ def fetch(self, n0):
+ raise NotImplementedError("Abstract class")
+ def store(self, n0, w_value):
+ raise NotImplementedError("Abstract class")
+ def size(self):
+ raise NotImplementedError("Abstract class")
+
+ # This will invoke an appropriate copy_from_* method.
+ # Overwriting this allows optimized transitions between certain storage types.
+ def copy_into(self, other_shadow):
+ other_shadow.copy_from(self)
+
+ def attach_shadow(self): pass
+
+ def copy_field_from(self, n0, other_shadow):
+ self.store(n0, other_shadow.fetch(n0))
+
+ def copy_from(self, other_shadow):
+ assert self.size() == other_shadow.size()
+ for i in range(self.size()):
+ self.copy_field_from(i, other_shadow)
+
+ def copy_from_AllNil(self, all_nil_storage):
+ self.copy_from(all_nil_storage)
+ def copy_from_SmallIntegerOrNil(self, small_int_storage):
+ self.copy_from(small_int_storage)
+ def copy_from_FloatOrNil(self, float_storage):
+ self.copy_from(float_storage)
+
+# ========== Storage classes implementing storage strategies ==========
+
+class AbstractStorageShadow(AbstractShadow):
+ _attrs_ = []
+ repr_classname = "AbstractStorageShadow"
+
+ def store(self, n0, w_val):
+ if self.can_contain(w_val):
+ return self.do_store(n0, w_val)
+ new_storage = self.generalized_strategy_for(w_val)
+ return self._w_self.store_with_new_storage(new_storage, n0, w_val)
+ def can_contain(self, w_val):
+ return self.static_can_contain(self.space, w_val)
+ @staticmethod
+ def static_can_contain(space, w_val):
+ raise NotImplementedError()
+ def do_store(self, n0, w_val):
+ raise NotImplementedError()
+ def generalized_strategy_for(self, w_val):
+ raise NotImplementedError()
+
+ def copy_from_AllNil(self, all_nil_storage):
+ pass # Already initialized
+ def copy_from(self, other_shadow):
+ assert self.size() == other_shadow.size()
+ for i in range(self.size()):
+ w_val = other_shadow.fetch(i)
+ if not w_val.is_nil(self.space): # nil fields already initialized
+ self.store(i, w_val)
+
+class AllNilStorageShadow(AbstractStorageShadow):
+ repr_classname = "AllNilStorageShadow"
+ _attrs_ = ['_size']
+ _immutable_fields_ = ['_size']
+ def __init__(self, space, w_self, size):
+ AbstractStorageShadow.__init__(self, space, w_self, size)
+ self._size = size
+ def fetch(self, n0):
+ if n0 >= self._size:
+ raise IndexError
+ return self.space.w_nil
+ def copy_into(self, other_shadow):
+ other_shadow.copy_from_AllNil(self)
+ def do_store(self, n0, w_value):
+ pass
+ def size(self):
+ return self._size
+ def generalized_strategy_for(self, w_val):
+ return find_storage_for_objects(self.space, [w_val])
+ @staticmethod
+ def static_can_contain(space, w_val):
+ return isinstance(w_val, model.W_Object) and w_val.is_nil(space)
+
+class AbstractValueOrNilStorageMixin(object):
+ # Class must provide: wrap, unwrap, nil_value, is_nil_value, wrapper_class
+ _attrs_ = ['storage']
+ _immutable_fields_ = ['storage']
+
+ def __init__(self, space, w_self, size):
+ AbstractStorageShadow.__init__(self, space, w_self, size)
+ self.storage = [self.nil_value] * size
+
+ def size(self):
+ return len(self.storage)
+
+ def generalized_strategy_for(self, w_val):
+ return ListStorageShadow
+
+ def fetch(self, n0):
+ val = self.storage[n0]
+ if self.is_nil_value(val):
+ return self.space.w_nil
+ else:
+ return self.wrap(self.space, val)
+
+ def do_store(self, n0, w_val):
+ if w_val.is_nil(self.space):
+ self.storage[n0] = self.nil_value
+ else:
+ self.storage[n0] = self.unwrap(self.space, w_val)
+
+# This is to avoid code duplication
+ at objectmodel.specialize.arg(0)
+def _value_or_nil_can_handle(cls, space, w_val):
+ return isinstance(w_val, model.W_Object) and w_val.is_nil(space) or \
+ (isinstance(w_val, cls.wrapper_class) \
+ and not cls.is_nil_value(cls.unwrap(space, w_val)))
+
+class SmallIntegerOrNilStorageShadow(AbstractStorageShadow):
+ repr_classname = "SmallIntegerOrNilStorageShadow"
+ nil_value = constants.MAXINT
+ wrapper_class = model.W_SmallInteger
+ import_from_mixin(AbstractValueOrNilStorageMixin)
+
+ @staticmethod
+ def static_can_contain(space, w_val):
+ return _value_or_nil_can_handle(SmallIntegerOrNilStorageShadow, space, w_val)
+ @staticmethod
+ def is_nil_value(val):
+ return val == SmallIntegerOrNilStorageShadow.nil_value
+ @staticmethod
+ def wrap(space, val):
+ return space.wrap_int(val)
+ @staticmethod
+ def unwrap(space, w_val):
+ return space.unwrap_int(w_val)
+ def copy_into(self, other_shadow):
+ other_shadow.copy_from_SmallIntegerOrNil(self)
+
+class FloatOrNilStorageShadow(AbstractStorageShadow):
+ repr_classname = "FloatOrNilStorageShadow"
+ nil_value = sys.float_info.max
+ wrapper_class = model.W_Float
+ import_from_mixin(AbstractValueOrNilStorageMixin)
+
+ @staticmethod
+ def static_can_contain(space, w_val):
+ return _value_or_nil_can_handle(FloatOrNilStorageShadow, space, w_val)
+ @staticmethod
+ def is_nil_value(val):
+ return val == FloatOrNilStorageShadow.nil_value
+ @staticmethod
+ def wrap(space, val):
+ return space.wrap_float(val)
+ @staticmethod
+ def unwrap(space, w_val):
+ return space.unwrap_float(w_val)
+ def copy_into(self, other_shadow):
+ other_shadow.copy_from_FloatOrNil(self)
+
+def empty_storage(space, w_self, size, weak=False):
+ if weak:
+ return WeakListStorageShadow(space, w_self, size)
+ if space.no_specialized_storage.is_set():
+ return ListStorageShadow(space, w_self, size)
+ return AllNilStorageShadow(space, w_self, size)
+
+ at jit.unroll_safe
+def find_storage_for_objects(space, vars, weak=False):
+ if weak:
+ return WeakListStorageShadow
+ if space.no_specialized_storage.is_set():
+ return ListStorageShadow
+ specialized_strategies = 3
+ all_nil_can_handle = True
+ small_int_can_handle = True
+ float_can_handle = True
+ for w_obj in vars:
+ if all_nil_can_handle and not AllNilStorageShadow.static_can_contain(space, w_obj):
+ all_nil_can_handle = False
+ specialized_strategies = specialized_strategies - 1
+ if small_int_can_handle and not SmallIntegerOrNilStorageShadow.static_can_contain(space, w_obj):
+ small_int_can_handle = False
+ specialized_strategies = specialized_strategies - 1
+ if float_can_handle and not FloatOrNilStorageShadow.static_can_contain(space, w_obj):
+ float_can_handle = False
+ specialized_strategies = specialized_strategies - 1
+
+ if specialized_strategies <= 0:
+ return ListStorageShadow
+
+ if all_nil_can_handle:
+ return AllNilStorageShadow
+ if small_int_can_handle:
+ return SmallIntegerOrNilStorageShadow
+ if float_can_handle:
+ return FloatOrNilStorageShadow
+
+ # If this happens, please look for a bug in the code above.
+ assert False, "No strategy could be found for list..."
+
+class ListStorageMixin(object):
+ def __init__(self, space, w_self, size):
+ AbstractStorageShadow.__init__(self, space, w_self, size)
+ self.initialize_storage(size)
+ def size(self):
+ return len(self.storage)
+
+class ListStorageShadow(AbstractStorageShadow):
+ _attrs_ = ['storage']
+ _immutable_fields_ = ['storage']
+ repr_classname = "ListStorageShadow"
+ import_from_mixin(ListStorageMixin)
+
+ def initialize_storage(self, size):
+ self.storage = [self.space.w_nil] * size
+ def fetch(self, n0):
+ return self.storage[n0]
+ def store(self, n0, w_value):
+ self.storage[n0] = w_value
+
+class WeakListStorageShadow(AbstractStorageShadow):
+ _attrs_ = ['storage']
+ _immutable_fields_ = ['storage']
+ repr_classname = "WeakListStorageShadow"
+ import_from_mixin(ListStorageMixin)
+
+ def initialize_storage(self, size):
+ self.storage = [weakref.ref(self.space.w_nil)] * size
+ def fetch(self, n0):
+ weakobj = self.storage[n0]
+ return weakobj() or self.space.w_nil
+ def store(self, n0, w_value):
+ assert w_value is not None
+ self.storage[n0] = weakref.ref(w_value)
+
+# ========== Other storage classes, non-strategies ==========
+
+class AbstractRedirectingShadow(AbstractShadow):
+ _attrs_ = ['_w_self_size']
+ repr_classname = "AbstractRedirectingShadow"
+
+ def __init__(self, space, w_self, size):
+ if w_self is not None:
+ self._w_self_size = w_self.size()
+ else:
+ self._w_self_size = size
+ AbstractShadow.__init__(self, space, w_self, self._w_self_size)
+
+ def size(self):
+ return self._w_self_size
+
+class AbstractCachingShadow(ListStorageShadow):
+ _immutable_fields_ = ['version?']
+ _attrs_ = ['version']
+ repr_classname = "AbstractCachingShadow"
+ import_from_mixin(version.VersionMixin)
+ version = None
+
+ def __init__(self, space, w_self, size):
+ ListStorageShadow.__init__(self, space, w_self, size)
+ self.changed()
+
+class CachedObjectShadow(AbstractCachingShadow):
+ repr_classname = "CachedObjectShadow"
+
+ @elidable_for_version
+ def fetch(self, n0):
+ return AbstractCachingShadow.fetch(self, n0)
+
+ def store(self, n0, w_value):
+ AbstractCachingShadow.store(self, n0, w_value)
+ self.changed()
+
+class ObserveeShadow(ListStorageShadow):
+ _attrs_ = ['dependent']
+ repr_classname = "ObserveeShadow"
+ def __init__(self, space, w_self, size):
+ ListStorageShadow.__init__(self, space, w_self, size)
+ self.dependent = None
+
+ def store(self, n0, w_value):
+ ListStorageShadow.store(self, n0, w_value)
+ if self.dependent:
+ 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
diff --git a/spyvm/storage_classes.py b/spyvm/storage_classes.py
new file mode 100644
--- /dev/null
+++ b/spyvm/storage_classes.py
@@ -0,0 +1,346 @@
+
+from spyvm import model, constants, error, wrapper, version
+from spyvm.storage import AbstractCachingShadow, ListStorageShadow
+from spyvm.version import constant_for_version, constant_for_version_arg
+from rpython.rlib import jit
+
+POINTERS = 0
+BYTES = 1
+WORDS = 2
+WEAK_POINTERS = 3
+COMPILED_METHOD = 4
+FLOAT = 5
+LARGE_POSITIVE_INTEGER = 6
+
+class ClassShadowError(error.SmalltalkException):
+ exception_type = "ClassShadowError"
+
+class ClassShadow(AbstractCachingShadow):
+ """A shadow for Smalltalk objects that are classes
+ (i.e. used as the class of another Smalltalk object).
+ """
+
+ _attrs_ = ["name", "_instance_size", "instance_varsized", "instance_kind",
+ "_s_methoddict", "_s_superclass", "subclass_s"]
+ name = '??? (incomplete class info)'
+ _s_superclass = _s_methoddict = None
+ provides_getname = True
+ repr_classname = "ClassShadow"
+
+ def __init__(self, space, w_self, size):
+ self.subclass_s = {}
+ AbstractCachingShadow.__init__(self, space, w_self, size)
+
+ def store(self, n0, w_val):
+ AbstractCachingShadow.store(self, n0, w_val)
+ if n0 == constants.CLASS_SUPERCLASS_INDEX:
+ self.store_w_superclass(w_val)
+ elif n0 == constants.CLASS_METHODDICT_INDEX:
+ self.store_w_methoddict(w_val)
+ elif n0 == constants.CLASS_FORMAT_INDEX:
+ # read and painfully decode the format
+ assert isinstance(w_val, model.W_SmallInteger)
+ classformat = self.space.unwrap_int(w_val)
+ # 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
+
+ # In case of raised exception below.
+ self.changed()
+
+ if format < 4:
+ self.instance_kind = POINTERS
+ elif format == 4:
+ self.instance_kind = WEAK_POINTERS
+ elif format == 6:
+ if self.space.w_Float.is_same_object(self.w_self()):
+ self.instance_kind = FLOAT
+ else:
+ 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:
+ if self.space.w_LargePositiveInteger.is_same_object(self.w_self()):
+ self.instance_kind = LARGE_POSITIVE_INTEGER
+ else:
+ 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,))
+ else:
+ if self._w_self.w_class == self.space.classtable["w_Metaclass"]:
+ # In case of Metaclasses, the "instance" class is stored in the last field.
+ if n0 == self.size() - 1 and isinstance(w_val, model.W_PointersObject):
+ cl_shadow = w_val.as_class_get_shadow(self.space)
+ self.name = "%s class" % cl_shadow.getname()
+ else:
+ return
+ elif n0 == constants.CLASS_NAME_INDEX:
+ # In case of regular classes, the name is stored here.
+ self.store_w_name(w_val)
+ else:
+ return
+ # Some of the special info has changed -> Switch version.
+ self.changed()
+
+ def store_w_superclass(self, w_class):
+ superclass = self._s_superclass
+ if w_class is None or w_class.is_nil(self.space):
+ if superclass: superclass.detach_s_class(self)
+ self._s_superclass = None
+ else:
+ assert isinstance(w_class, model.W_PointersObject)
+ s_new_superclass = w_class.as_class_get_shadow(self.space)
+ if superclass is s_new_superclass:
+ return
+ if superclass: superclass.detach_s_class(self)
+ self._s_superclass = s_new_superclass
+ s_new_superclass.attach_s_class(self)
+
+ def store_w_methoddict(self, w_methoddict):
+ methoddict = self._s_methoddict
+ if w_methoddict is None or w_methoddict.is_nil(self.space):
+ if methoddict: methoddict.s_class = None
+ self._s_methoddict = None
+ else:
+ assert isinstance(w_methoddict, model.W_PointersObject)
+ s_new_methoddict = w_methoddict.as_methoddict_get_shadow(self.space)
+ if methoddict is s_new_methoddict:
+ return
+ if methoddict: methoddict.s_class = None
+ self.store_s_methoddict(s_new_methoddict)
+
+ def store_s_methoddict(self, s_methoddict):
+ s_methoddict.s_class = self
+ s_methoddict.sync_method_cache()
+ self._s_methoddict = s_methoddict
+
+ 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 store_w_name(self, w_name):
+ if isinstance(w_name, model.W_BytesObject):
+ self.name = w_name.as_string()
+ else:
+ self.name = None
+
+ @jit.unroll_safe
+ def flush_method_caches(self):
+ look_in_shadow = self
+ while look_in_shadow is not None:
+ look_in_shadow.s_methoddict().flush_method_cache()
+ look_in_shadow = look_in_shadow._s_superclass
+
+ def new(self, extrasize=0):
+ w_cls = self.w_self()
+ if self.instance_kind == POINTERS:
+ size = self.instsize() + extrasize
+ w_new = model.W_PointersObject(self.space, w_cls, size)
More information about the pypy-commit
mailing list