[pypy-commit] lang-smalltalk default: added type hints for instance variables of non-varsized pointers objects

lwassermann noreply at buildbot.pypy.org
Fri Apr 12 00:12:36 CEST 2013


Author: Lars Wassermann <lars.wassermann at gmail.com>
Branch: 
Changeset: r257:720555f9465b
Date: 2013-04-11 20:36 +0200
http://bitbucket.org/pypy/lang-smalltalk/changeset/720555f9465b/

Log:	added type hints for instance variables of non-varsized pointers
	objects

diff --git a/spyvm/fieldtypes.py b/spyvm/fieldtypes.py
new file mode 100644
--- /dev/null
+++ b/spyvm/fieldtypes.py
@@ -0,0 +1,133 @@
+from spyvm import model, shadow
+
+from rpython.rlib import jit, signature
+
+LPI = object()
+int
+float
+
+object
+
+maps = dict()
+
+class VarSizedFieldTypes():
+    _immutable_fields_ = []
+    _attrs_ = []
+    _settled_ = True
+
+    @staticmethod
+    def of_length(s_class, n):
+        return nilTyper
+
+    def __init__(self):
+        pass
+
+    def fetch(self, w_obj, n0):
+        return w_obj._vars[n0]
+
+    def store(self, w_obj, n0, w_val):
+        w_obj._vars[n0] = w_val
+
+class FieldTypes(VarSizedFieldTypes):
+    _immutable_fields_ = ['types']
+    _attrs_ = ['types', 'parent', 'siblings', 'diff']
+    _settled_ = True
+
+    def __init__(self, types, parent=None, change=(-1, object)):
+        self.types = types
+        self.parent = parent
+        if parent is not None:
+            assert change != (-1, object)
+        self.diff = change
+
+        self.siblings = dict()
+
+    def fetch(self, w_object, n0):
+        w_result = w_object._vars[n0]
+        types = self.types
+        if types[n0] is int:
+            jit.record_known_class(w_result, model.W_SmallInteger)
+        elif types[n0] is LPI:
+            jit.record_known_class(w_result, model.W_LargePositiveInteger1Word)
+        elif types[n0] is float:
+            jit.record_known_class(w_result, model.W_Float)
+        return w_result
+
+    def store(self, w_object, n0, w_value):
+        types = self.types
+        changed_type = w_value.fieldtype()
+        if types[n0] is not changed_type:
+            w_object.fieldtypes = self.sibling(n0, changed_type)
+        w_object._vars[n0] = w_value
+
+
+    def sibling(self, n0, changed_type):
+        assert self.types[n0] is not changed_type
+        change = (n0, changed_type)
+        parent = self.parent
+        siblings = self.siblings
+        if change in siblings:
+            return siblings[change]
+        elif parent is None:
+            return self.descent([change])
+        else:
+            new_fieldtype = parent.ascent([change, self.diff])
+            assert new_fieldtype.types == self.types[0:n0] + [changed_type] + self.types[n0+1:]
+            siblings[change] = new_fieldtype
+            return new_fieldtype
+
+    def ascent(self, changes):
+        parent = self.parent
+        if parent is None:
+            return self.descent(sorted(changes))
+        else:
+            change = self.diff
+            if changes[0][0] != change[0]:
+                changes.append(change)
+            return parent.ascent(changes)
+
+    def descent(self, changes):
+        if changes == []:
+            return self
+
+        change = changes[0]
+        siblings = self.siblings
+        if change in siblings:
+            return siblings[change].descent(changes[1:])
+        else:
+            new_types = list(self.types)
+            new_types[change[0]] = change[1]
+            new_fieldtype = FieldTypes(new_types, self, change)
+            siblings[change] = new_fieldtype
+            return new_fieldtype.descent(changes[1:])
+
+
+    @staticmethod
+    def of_length(n):
+        if n not in maps:
+            maps[n] = FieldTypes([object] * n)
+        return maps[n]
+
+
+nilTyper = VarSizedFieldTypes()
+def fieldtypes_of_length(s_class, size):
+    if s_class is None or s_class.isvariable():
+        return nilTyper
+    else:
+        return FieldTypes.of_length(size)
+
+def fieldtypes_of(w_obj):
+    try:
+        if w_obj.s_class.isvariable():
+            return nilTyper
+        else:
+            vars = w_obj._vars
+            size = len(vars)
+            typer = FieldTypes.of_length(size)
+            for i, w_val in enumerate(vars):
+                changed_type = w_val.fieldtype()
+                if changed_type is not object:
+                    typer = typer.sibling(i, changed_type)
+            return typer
+    except AttributeError:
+        return nilTyper
\ No newline at end of file
diff --git a/spyvm/model.py b/spyvm/model.py
--- a/spyvm/model.py
+++ b/spyvm/model.py
@@ -130,6 +130,9 @@
     def unwrap_uint(self, space):
         raise error.UnwrappingError("Got unexpected class in unwrap_uint")
 
+    def fieldtype(self):
+        return object
+
 class W_SmallInteger(W_Object):
     """Boxed integer value"""
     # TODO can we tell pypy that its never larger then 31-bit?
@@ -200,6 +203,9 @@
     def clone(self, space):
         return self
 
+    def fieldtype(self):
+        return int
+
 class W_AbstractObjectWithIdentityHash(W_Object):
     """Object with explicit hash (ie all except small
     ints and floats)."""
@@ -303,6 +309,10 @@
     def invariant(self):
         return isinstance(self.value, int)
 
+    def fieldtype(self):
+        from spyvm.fieldtype import LPI
+        return LPI
+
 class W_Float(W_AbstractObjectWithIdentityHash):
     """Boxed float value."""
     _attrs_ = ['value']
@@ -388,6 +398,9 @@
     def size(self):
         return 2
 
+    def fieldtype(self):
+        return float
+
 @signature.finishsigs
 class W_AbstractObjectWithClassReference(W_AbstractObjectWithIdentityHash):
     """Objects with arbitrary class (ie not CompiledMethod, SmallInteger or
@@ -447,24 +460,30 @@
 
 class W_PointersObject(W_AbstractObjectWithClassReference):
     """Common object."""
-    _attrs_ = ['_shadow', '_vars']
+    _attrs_ = ['_shadow', '_vars', 'fieldtypes']
 
     _shadow = None # Default value
 
     @jit.unroll_safe
     def __init__(self, space, w_class, size):
+        from spyvm.fieldtypes import fieldtypes_of_length
         """Create new object with size = fixed + variable size."""
         W_AbstractObjectWithClassReference.__init__(self, space, w_class)
+
         vars = self._vars = [None] * size
+        self.fieldtypes = fieldtypes_of_length(self.s_class, 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 fillin(self, space, g_self):
+        from spyvm.fieldtypes import fieldtypes_of
         self._vars = g_self.get_pointers()
         self.s_class = g_self.get_class().as_class_get_penumbra(space)
         self.hash = g_self.get_hash()
         self.space = space
+        self.fieldtypes = fieldtypes_of(self)
 
     def at0(self, space, index0):
         # To test, at0 = in varsize part
@@ -480,7 +499,9 @@
         return self._fetch(n0)
 
     def _fetch(self, n0):
-        return self._vars[n0]
+        # return self._vars[n0]
+        fieldtypes = jit.promote(self.fieldtypes)
+        return fieldtypes.fetch(self, n0)
 
     def store(self, space, n0, w_value):
         if self.has_shadow():
@@ -488,8 +509,9 @@
         return self._store(n0, w_value)
 
     def _store(self, n0, w_value):
-        self._vars[n0] = w_value
-
+        # self._vars[n0] = w_value
+        fieldtypes = jit.promote(self.fieldtypes)
+        return fieldtypes.store(self, n0, w_value)
 
     def varsize(self, space):
         return self.size() - self.instsize(space)
@@ -597,7 +619,8 @@
         return True
 
     def clone(self, space):
-        w_result = W_PointersObject(self.space, self.getclass(space), len(self._vars))
+        w_result = W_PointersObject(self.space, self.getclass(space),
+                                    len(self._vars))
         w_result._vars = [self.fetch(space, i) for i in range(len(self._vars))]
         return w_result
 
@@ -607,6 +630,10 @@
                                 className='W_PointersObject', 
                                 additionalInformation='len=%d' % self.size())
 
+    def fieldtype(self):
+        # from spyvm.fieldtype import
+        return object
+
 class W_BytesObject(W_AbstractObjectWithClassReference):
     _attrs_ = ['bytes']
 
diff --git a/spyvm/shadow.py b/spyvm/shadow.py
--- a/spyvm/shadow.py
+++ b/spyvm/shadow.py
@@ -201,7 +201,8 @@
     def new(self, extrasize=0):
         w_cls = self.w_self()
         if self.instance_kind == POINTERS:
-            w_new = model.W_PointersObject(self.space, w_cls, self.instsize()+extrasize)
+            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:
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
@@ -2,7 +2,10 @@
 from spyvm import model, interpreter, primitives, shadow
 from spyvm import objspace, wrapper, constants
 
-mockclass = objspace.bootstrap_class
+def mockclass(space, instsize, w_superclass=None, w_metaclass=None,
+                    name='?', format=shadow.POINTERS, varsized=True):
+    return objspace.bootstrap_class(space, instsize, w_superclass, w_metaclass,
+                    name, format, varsized)
 
 space = objspace.ObjSpace()
 interp = interpreter.Interpreter(space)
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
@@ -92,9 +92,9 @@
     w_object.store(space, constants.MTHDCTX_METHOD, method)
     # XXX
     w_object.store(space, constants.MTHDCTX_CLOSURE_OR_NIL, space.w_nil)
-    w_object.store(space, constants.MTHDCTX_RECEIVER, 'receiver')
+    w_object.store(space, constants.MTHDCTX_RECEIVER, space.wrap_string('receiver'))
 
-    w_object.store(space, constants.MTHDCTX_TEMP_FRAME_START, 'el')
+    w_object.store(space, constants.MTHDCTX_TEMP_FRAME_START, space.wrap_string('el'))
     return w_object
 
 def blockcontext(w_sender=space.w_nil, pc=1, stackpointer=1, stacksize=5,
@@ -106,7 +106,7 @@
     w_object.store(space, constants.BLKCTX_BLOCK_ARGUMENT_COUNT_INDEX, space.wrap_int(54))
     w_object.store(space, constants.BLKCTX_INITIAL_IP_INDEX, space.wrap_int(17))
     w_object.store(space, constants.BLKCTX_HOME_INDEX, home)
-    w_object.store(space, constants.BLKCTX_STACK_START, 'el')
+    w_object.store(space, constants.BLKCTX_STACK_START, space.wrap_string('el'))
     return w_object
 
 def test_context():
@@ -121,24 +121,24 @@
     assert s_object2.w_self() == w_object2
     assert s_object.s_sender() == None
     assert s_object2.s_sender() == s_object
-    assert s_object.w_receiver() == 'receiver'
+    assert s_object.w_receiver().as_string() == 'receiver'
     s_object2.settemp(0, 'a')
     s_object2.settemp(1, 'b')
     assert s_object2.gettemp(1) == 'b'
     assert s_object2.gettemp(0) == 'a'
     assert s_object.w_method() == w_m
     idx = s_object.stackstart()
-    w_object.store(space, idx, 'f')
-    w_object.store(space, idx + 1, 'g')
-    w_object.store(space, idx + 2, 'h')
-    assert s_object.stack() == ['f', 'g', 'h' ]
-    assert s_object.top() == 'h'
+    w_object.store(space, idx, space.wrap_string('f'))
+    w_object.store(space, idx + 1, space.wrap_string('g'))
+    w_object.store(space, idx + 2, space.wrap_string('h'))
+    assert map(lambda x: x.as_string(), s_object.stack()) == ['f', 'g', 'h' ]
+    assert s_object.top().as_string() == 'h'
     s_object.push('i')
     assert s_object.top() == 'i'
-    assert s_object.peek(1) == 'h'
+    assert s_object.peek(1).as_string() == 'h'
     assert s_object.pop() == 'i'
-    assert s_object.pop_and_return_n(2) == ['g', 'h']
-    assert s_object.pop() == 'f'
+    assert map(lambda x: x.as_string(), s_object.pop_and_return_n(2)) == ['g', 'h']
+    assert s_object.pop().as_string() == 'f'
     assert s_object.external_stackpointer() == s_object.stackstart()
 
 def test_methodcontext():


More information about the pypy-commit mailing list