[pypy-commit] pypy py3k: hg merge default
mjacob
pypy.commits at gmail.com
Mon Feb 22 17:36:54 EST 2016
Author: Manuel Jacob <me at manueljacob.de>
Branch: py3k
Changeset: r82412:be90c2e77207
Date: 2016-02-22 23:36 +0100
http://bitbucket.org/pypy/pypy/changeset/be90c2e77207/
Log: hg merge default
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -128,6 +128,7 @@
Fix SSL tests by importing cpython's patch
+
.. branch: remove-getfield-pure
Remove pure variants of ``getfield_gc_*`` operations from the JIT. Relevant
@@ -163,3 +164,10 @@
.. branch: windows-vmprof-support
vmprof should work on Windows.
+
+
+.. branch: reorder-map-attributes
+
+When creating instances and adding attributes in several different orders
+depending on some condition, the JIT would create too much code. This is now
+fixed.
\ No newline at end of file
diff --git a/pypy/module/imp/test/test_import.py b/pypy/module/imp/test/test_import.py
--- a/pypy/module/imp/test/test_import.py
+++ b/pypy/module/imp/test/test_import.py
@@ -1216,12 +1216,12 @@
py.test.skip("unresolved issues with win32 shell quoting rules")
from pypy.interpreter.test.test_zpy import pypypath
extrapath = udir.ensure("pythonpath", dir=1)
- extrapath.join("urllib.py").write("print(42)\n")
+ extrapath.join("sched.py").write("print(42)\n")
old = os.environ.get('PYTHONPATH', None)
oldlang = os.environ.pop('LANG', None)
try:
os.environ['PYTHONPATH'] = str(extrapath)
- output = py.process.cmdexec('''"%s" "%s" -c "import urllib"''' %
+ output = py.process.cmdexec('''"%s" "%s" -c "import sched"''' %
(sys.executable, pypypath))
assert output.strip() == '42'
finally:
diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py
--- a/pypy/module/pypyjit/test_pypy_c/test_string.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_string.py
@@ -28,7 +28,6 @@
guard_true(i14, descr=...)
guard_not_invalidated(descr=...)
i16 = int_eq(i6, %d)
- guard_false(i16, descr=...)
i15 = int_mod(i6, i10)
i17 = int_rshift(i15, %d)
i18 = int_and(i10, i17)
@@ -68,7 +67,6 @@
guard_true(i11, descr=...)
guard_not_invalidated(descr=...)
i13 = int_eq(i6, %d) # value provided below
- guard_false(i13, descr=...)
i15 = int_mod(i6, 10)
i17 = int_rshift(i15, %d) # value provided below
i18 = int_and(10, i17)
@@ -144,43 +142,6 @@
jump(..., descr=...)
""")
- def test_getattr_promote(self):
- def main(n):
- class A(object):
- def meth_a(self):
- return 1
- def meth_b(self):
- return 2
- a = A()
-
- l = ['a', 'b']
- s = 0
- for i in range(n):
- name = 'meth_' + l[i & 1]
- meth = getattr(a, name) # ID: getattr
- s += meth()
- return s
-
- log = self.run(main, [1000])
- assert log.result == main(1000)
- loops = log.loops_by_filename(self.filepath)
- assert len(loops) == 1
- for loop in loops:
- assert loop.match_by_id('getattr','''
- guard_not_invalidated?
- i32 = strlen(p31)
- i34 = int_add(5, i32)
- p35 = newstr(i34)
- strsetitem(p35, 0, 109)
- strsetitem(p35, 1, 101)
- strsetitem(p35, 2, 116)
- strsetitem(p35, 3, 104)
- strsetitem(p35, 4, 95)
- copystrcontent(p31, p35, 0, 5, i32)
- i49 = call_i(ConstClass(_ll_2_str_eq_nonnull__rpy_stringPtr_rpy_stringPtr), p35, ConstPtr(ptr48), descr=<Calli [48] rr EF=0 OS=28>)
- guard_value(i49, 1, descr=...)
- ''')
-
def test_remove_duplicate_method_calls(self):
def main(n):
lst = []
diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py
--- a/pypy/objspace/std/mapdict.py
+++ b/pypy/objspace/std/mapdict.py
@@ -1,4 +1,4 @@
-import weakref
+import weakref, sys
from rpython.rlib import jit, objectmodel, debug, rerased
from rpython.rlib.rarithmetic import intmask, r_uint
@@ -12,6 +12,11 @@
from pypy.objspace.std.typeobject import MutableCell
+erase_item, unerase_item = rerased.new_erasing_pair("mapdict storage item")
+erase_map, unerase_map = rerased.new_erasing_pair("map")
+erase_list, unerase_list = rerased.new_erasing_pair("mapdict storage list")
+
+
# ____________________________________________________________
# attribute shapes
@@ -20,6 +25,7 @@
# note: we use "x * NUM_DIGITS_POW2" instead of "x << NUM_DIGITS" because
# we want to propagate knowledge that the result cannot be negative
+
class AbstractAttribute(object):
_immutable_fields_ = ['terminator']
cache_attrs = None
@@ -151,29 +157,124 @@
cache[name, index] = attr
return attr
+ @jit.elidable
+ def _get_cache_attr(self, name, index):
+ key = name, index
+ # this method is not actually elidable, but it's fine anyway
+ if self.cache_attrs is not None:
+ return self.cache_attrs.get(key, None)
+ return None
+
+ def add_attr(self, obj, name, index, w_value):
+ self._reorder_and_add(obj, name, index, w_value)
+ if not jit.we_are_jitted():
+ oldattr = self
+ attr = obj._get_mapdict_map()
+ size_est = (oldattr._size_estimate + attr.size_estimate()
+ - oldattr.size_estimate())
+ assert size_est >= (oldattr.length() * NUM_DIGITS_POW2)
+ oldattr._size_estimate = size_est
+
+ def _add_attr_without_reordering(self, obj, name, index, w_value):
+ attr = self._get_new_attr(name, index)
+ attr._switch_map_and_write_storage(obj, w_value)
+
+ @jit.unroll_safe
+ def _switch_map_and_write_storage(self, obj, w_value):
+ if self.length() > obj._mapdict_storage_length():
+ # note that self.size_estimate() is always at least self.length()
+ new_storage = [None] * self.size_estimate()
+ for i in range(obj._mapdict_storage_length()):
+ new_storage[i] = obj._mapdict_read_storage(i)
+ obj._set_mapdict_storage_and_map(new_storage, self)
+
+ # the order is important here: first change the map, then the storage,
+ # for the benefit of the special subclasses
+ obj._set_mapdict_map(self)
+ obj._mapdict_write_storage(self.storageindex, w_value)
+
+
+ @jit.elidable
+ def _find_branch_to_move_into(self, name, index):
+ # walk up the map chain to find an ancestor with lower order that
+ # already has the current name as a child inserted
+ current_order = sys.maxint
+ number_to_readd = 0
+ current = self
+ key = (name, index)
+ while True:
+ attr = None
+ if current.cache_attrs is not None:
+ attr = current.cache_attrs.get(key, None)
+ if attr is None or attr.order > current_order:
+ # we reached the top, so we didn't find it anywhere,
+ # just add it to the top attribute
+ if not isinstance(current, PlainAttribute):
+ return 0, self._get_new_attr(name, index)
+
+ else:
+ return number_to_readd, attr
+ # if not found try parent
+ number_to_readd += 1
+ current_order = current.order
+ current = current.back
+
@jit.look_inside_iff(lambda self, obj, name, index, w_value:
jit.isconstant(self) and
jit.isconstant(name) and
jit.isconstant(index))
- def add_attr(self, obj, name, index, w_value):
- attr = self._get_new_attr(name, index)
- oldattr = obj._get_mapdict_map()
- if not jit.we_are_jitted():
- size_est = (oldattr._size_estimate + attr.size_estimate()
- - oldattr.size_estimate())
- assert size_est >= (oldattr.length() * NUM_DIGITS_POW2)
- oldattr._size_estimate = size_est
- if attr.length() > obj._mapdict_storage_length():
- # note that attr.size_estimate() is always at least attr.length()
- new_storage = [None] * attr.size_estimate()
- for i in range(obj._mapdict_storage_length()):
- new_storage[i] = obj._mapdict_read_storage(i)
- obj._set_mapdict_storage_and_map(new_storage, attr)
+ def _reorder_and_add(self, obj, name, index, w_value):
+ # the idea is as follows: the subtrees of any map are ordered by
+ # insertion. the invariant is that subtrees that are inserted later
+ # must not contain the name of the attribute of any earlier inserted
+ # attribute anywhere
+ # m______
+ # inserted first / \ ... \ further attributes
+ # attrname a 0/ 1\ n\
+ # m a must not appear here anywhere
+ #
+ # when inserting a new attribute in an object we check whether any
+ # parent of lower order has seen that attribute yet. if yes, we follow
+ # that branch. if not, we normally append that attribute. When we
+ # follow a prior branch, we necessarily remove some attributes to be
+ # able to do that. They need to be re-added, which has to follow the
+ # reordering procedure recusively.
- # the order is important here: first change the map, then the storage,
- # for the benefit of the special subclasses
- obj._set_mapdict_map(attr)
- obj._mapdict_write_storage(attr.storageindex, w_value)
+ # we store the to-be-readded attribute in the stack, with the map and
+ # the value paired up those are lazily initialized to a list large
+ # enough to store all current attributes
+ stack = None
+ stack_index = 0
+ while True:
+ current = self
+ number_to_readd = 0
+ number_to_readd, attr = self._find_branch_to_move_into(name, index)
+ # we found the attributes further up, need to save the
+ # previous values of the attributes we passed
+ if number_to_readd:
+ if stack is None:
+ stack = [erase_map(None)] * (self.length() * 2)
+ current = self
+ for i in range(number_to_readd):
+ assert isinstance(current, PlainAttribute)
+ w_self_value = obj._mapdict_read_storage(
+ current.storageindex)
+ stack[stack_index] = erase_map(current)
+ stack[stack_index + 1] = erase_item(w_self_value)
+ stack_index += 2
+ current = current.back
+ attr._switch_map_and_write_storage(obj, w_value)
+
+ if not stack_index:
+ return
+
+ # readd the current top of the stack
+ stack_index -= 2
+ next_map = unerase_map(stack[stack_index])
+ w_value = unerase_item(stack[stack_index + 1])
+ name = next_map.name
+ index = next_map.index
+ self = obj._get_mapdict_map()
def materialize_r_dict(self, space, obj, dict_w):
raise NotImplementedError("abstract base class")
@@ -279,7 +380,7 @@
return Terminator.set_terminator(self, obj, terminator)
class PlainAttribute(AbstractAttribute):
- _immutable_fields_ = ['name', 'index', 'storageindex', 'back', 'ever_mutated?']
+ _immutable_fields_ = ['name', 'index', 'storageindex', 'back', 'ever_mutated?', 'order']
def __init__(self, name, index, back):
AbstractAttribute.__init__(self, back.space, back.terminator)
@@ -289,6 +390,7 @@
self.back = back
self._size_estimate = self.length() * NUM_DIGITS_POW2
self.ever_mutated = False
+ self.order = len(back.cache_attrs) if back.cache_attrs else 0
def _copy_attr(self, obj, new_obj):
w_value = self.read(obj, self.name, self.index)
@@ -540,9 +642,6 @@
memo_get_subclass_of_correct_size._annspecialcase_ = "specialize:memo"
_subclass_cache = {}
-erase_item, unerase_item = rerased.new_erasing_pair("mapdict storage item")
-erase_list, unerase_list = rerased.new_erasing_pair("mapdict storage list")
-
def _make_subclass_size_n(supercls, n):
from rpython.rlib import unroll
rangen = unroll.unrolling_iterable(range(n))
diff --git a/pypy/objspace/std/test/test_mapdict.py b/pypy/objspace/std/test/test_mapdict.py
--- a/pypy/objspace/std/test/test_mapdict.py
+++ b/pypy/objspace/std/test/test_mapdict.py
@@ -107,6 +107,153 @@
assert obj2.getdictvalue(space, "b") == 60
assert obj2.map is obj.map
+def test_insert_different_orders():
+ cls = Class()
+ obj = cls.instantiate()
+ obj.setdictvalue(space, "a", 10)
+ obj.setdictvalue(space, "b", 20)
+
+ obj2 = cls.instantiate()
+ obj2.setdictvalue(space, "b", 30)
+ obj2.setdictvalue(space, "a", 40)
+
+ assert obj.map is obj2.map
+
+def test_insert_different_orders_2():
+ cls = Class()
+ obj = cls.instantiate()
+ obj2 = cls.instantiate()
+
+ obj.setdictvalue(space, "a", 10)
+
+ obj2.setdictvalue(space, "b", 20)
+ obj2.setdictvalue(space, "a", 30)
+
+ obj.setdictvalue(space, "b", 40)
+ assert obj.map is obj2.map
+
+def test_insert_different_orders_3():
+ cls = Class()
+ obj = cls.instantiate()
+ obj2 = cls.instantiate()
+ obj3 = cls.instantiate()
+ obj4 = cls.instantiate()
+ obj5 = cls.instantiate()
+ obj6 = cls.instantiate()
+
+ obj.setdictvalue(space, "a", 10)
+ obj.setdictvalue(space, "b", 20)
+ obj.setdictvalue(space, "c", 30)
+
+ obj2.setdictvalue(space, "a", 30)
+ obj2.setdictvalue(space, "c", 40)
+ obj2.setdictvalue(space, "b", 50)
+
+ obj3.setdictvalue(space, "c", 30)
+ obj3.setdictvalue(space, "a", 40)
+ obj3.setdictvalue(space, "b", 50)
+
+ obj4.setdictvalue(space, "c", 30)
+ obj4.setdictvalue(space, "b", 40)
+ obj4.setdictvalue(space, "a", 50)
+
+ obj5.setdictvalue(space, "b", 30)
+ obj5.setdictvalue(space, "a", 40)
+ obj5.setdictvalue(space, "c", 50)
+
+ obj6.setdictvalue(space, "b", 30)
+ obj6.setdictvalue(space, "c", 40)
+ obj6.setdictvalue(space, "a", 50)
+
+ assert obj.map is obj2.map
+ assert obj.map is obj3.map
+ assert obj.map is obj4.map
+ assert obj.map is obj5.map
+ assert obj.map is obj6.map
+
+
+def test_insert_different_orders_4():
+ cls = Class()
+ obj = cls.instantiate()
+ obj2 = cls.instantiate()
+
+ obj.setdictvalue(space, "a", 10)
+ obj.setdictvalue(space, "b", 20)
+ obj.setdictvalue(space, "c", 30)
+ obj.setdictvalue(space, "d", 40)
+
+ obj2.setdictvalue(space, "d", 50)
+ obj2.setdictvalue(space, "c", 50)
+ obj2.setdictvalue(space, "b", 50)
+ obj2.setdictvalue(space, "a", 50)
+
+ assert obj.map is obj2.map
+
+def test_insert_different_orders_5():
+ cls = Class()
+ obj = cls.instantiate()
+ obj2 = cls.instantiate()
+
+ obj.setdictvalue(space, "a", 10)
+ obj.setdictvalue(space, "b", 20)
+ obj.setdictvalue(space, "c", 30)
+ obj.setdictvalue(space, "d", 40)
+
+ obj2.setdictvalue(space, "d", 50)
+ obj2.setdictvalue(space, "c", 50)
+ obj2.setdictvalue(space, "b", 50)
+ obj2.setdictvalue(space, "a", 50)
+
+ obj3 = cls.instantiate()
+ obj3.setdictvalue(space, "d", 50)
+ obj3.setdictvalue(space, "c", 50)
+ obj3.setdictvalue(space, "b", 50)
+ obj3.setdictvalue(space, "a", 50)
+
+ assert obj.map is obj3.map
+
+
+def test_bug_stack_overflow_insert_attributes():
+ cls = Class()
+ obj = cls.instantiate()
+
+ for i in range(1000):
+ obj.setdictvalue(space, str(i), i)
+
+
+def test_insert_different_orders_perm():
+ from itertools import permutations
+ cls = Class()
+ seen_maps = {}
+ for preexisting in ['', 'x', 'xy']:
+ for i, attributes in enumerate(permutations("abcdef")):
+ obj = cls.instantiate()
+ for i, attr in enumerate(preexisting):
+ obj.setdictvalue(space, attr, i*1000)
+ key = preexisting
+ for j, attr in enumerate(attributes):
+ obj.setdictvalue(space, attr, i*10+j)
+ key = "".join(sorted(key+attr))
+ if key in seen_maps:
+ assert obj.map is seen_maps[key]
+ else:
+ seen_maps[key] = obj.map
+
+ print len(seen_maps)
+
+
+def test_bug_infinite_loop():
+ cls = Class()
+ obj = cls.instantiate()
+ obj.setdictvalue(space, "e", 1)
+ obj2 = cls.instantiate()
+ obj2.setdictvalue(space, "f", 2)
+ obj3 = cls.instantiate()
+ obj3.setdictvalue(space, "a", 3)
+ obj3.setdictvalue(space, "e", 4)
+ obj3.setdictvalue(space, "f", 5)
+
+
def test_attr_immutability(monkeypatch):
cls = Class()
obj = cls.instantiate()
@@ -359,9 +506,15 @@
class TestMapDictImplementation(BaseTestRDictImplementation):
StrategyClass = MapDictStrategy
get_impl = get_impl
+ def test_setdefault_fast(self):
+ # mapdict can't pass this, which is fine
+ pass
class TestDevolvedMapDictImplementation(BaseTestDevolvedDictImplementation):
get_impl = get_impl
StrategyClass = MapDictStrategy
+ def test_setdefault_fast(self):
+ # mapdict can't pass this, which is fine
+ pass
# ___________________________________________________________
# tests that check the obj interface after the dict has devolved
@@ -1143,3 +1296,7 @@
class TestMapDictImplementationUsingnewdict(BaseTestRDictImplementation):
StrategyClass = MapDictStrategy
# NB: the get_impl method is not overwritten here, as opposed to above
+
+ def test_setdefault_fast(self):
+ # mapdict can't pass this, which is fine
+ pass
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,3 @@
+# hypothesis is used for test generation on untranslated jit tests
+hypothesis
+
diff --git a/rpython/jit/metainterp/resume.py b/rpython/jit/metainterp/resume.py
--- a/rpython/jit/metainterp/resume.py
+++ b/rpython/jit/metainterp/resume.py
@@ -27,6 +27,13 @@
self.prev = prev
self.boxes = boxes
+class TopSnapshot(Snapshot):
+ __slots__ = ('vable_boxes',)
+
+ def __init__(self, prev, boxes, vable_boxes):
+ Snapshot.__init__(self, prev, boxes)
+ self.vable_boxes = vable_boxes
+
def combine_uint(index1, index2):
assert 0 <= index1 < 65536
assert 0 <= index2 < 65536
@@ -127,9 +134,11 @@
snapshot_storage):
n = len(framestack) - 1
if virtualizable_boxes is not None:
- boxes = virtualref_boxes + virtualizable_boxes
+ virtualizable_boxes = ([virtualizable_boxes[-1]] +
+ virtualizable_boxes[:-1])
else:
- boxes = virtualref_boxes[:]
+ virtualizable_boxes = []
+ virtualref_boxes = virtualref_boxes[:]
if n >= 0:
top = framestack[n]
_ensure_parent_resumedata(framestack, n)
@@ -138,11 +147,12 @@
snapshot_storage.rd_frame_info_list = frame_info_list
snapshot = Snapshot(top.parent_resumedata_snapshot,
top.get_list_of_active_boxes(False))
- snapshot = Snapshot(snapshot, boxes)
+ snapshot = TopSnapshot(snapshot, virtualref_boxes, virtualizable_boxes)
snapshot_storage.rd_snapshot = snapshot
else:
snapshot_storage.rd_frame_info_list = None
- snapshot_storage.rd_snapshot = Snapshot(None, boxes)
+ snapshot_storage.rd_snapshot = TopSnapshot(None, virtualref_boxes,
+ virtualizable_boxes)
PENDINGFIELDSTRUCT = lltype.Struct('PendingField',
('lldescr', OBJECTPTR),
@@ -200,10 +210,12 @@
self.v = 0
def count_boxes(self, lst):
- c = 0
+ snapshot = lst[0]
+ assert isinstance(snapshot, TopSnapshot)
+ c = len(snapshot.vable_boxes)
for snapshot in lst:
c += len(snapshot.boxes)
- c += 2 * (len(lst) - 1)
+ c += 2 * (len(lst) - 1) + 1 + 1
return c
def append(self, item):
@@ -294,13 +306,11 @@
state.append(tagged)
state.n = n
state.v = v
- state.position -= length + 2
- def number(self, optimizer, snapshot, frameinfo):
+ def number(self, optimizer, topsnapshot, frameinfo):
# flatten the list
- vref_snapshot = snapshot
- cur = snapshot.prev
- snapshot_list = [vref_snapshot]
+ cur = topsnapshot.prev
+ snapshot_list = [topsnapshot]
framestack_list = []
while cur:
framestack_list.append(frameinfo)
@@ -311,19 +321,30 @@
# we want to number snapshots starting from the back, but ending
# with a forward list
- for i in range(len(snapshot_list) - 1, -1, -1):
- state.position -= len(snapshot_list[i].boxes)
- if i != 0:
- frameinfo = framestack_list[i - 1]
- jitcode_pos, pc = unpack_uint(frameinfo.packed_jitcode_pc)
- state.position -= 2
- state.append(rffi.cast(rffi.SHORT, jitcode_pos))
- state.append(rffi.cast(rffi.SHORT, pc))
+ for i in range(len(snapshot_list) - 1, 0, -1):
+ state.position -= len(snapshot_list[i].boxes) + 2
+ frameinfo = framestack_list[i - 1]
+ jitcode_pos, pc = unpack_uint(frameinfo.packed_jitcode_pc)
+ state.append(rffi.cast(rffi.SHORT, jitcode_pos))
+ state.append(rffi.cast(rffi.SHORT, pc))
self._number_boxes(snapshot_list[i].boxes, optimizer, state)
+ state.position -= len(snapshot_list[i].boxes) + 2
- numb = resumecode.create_numbering(state.current,
- len(vref_snapshot.boxes))
+ assert isinstance(topsnapshot, TopSnapshot)
+ special_boxes_size = (1 + len(topsnapshot.vable_boxes) +
+ 1 + len(topsnapshot.boxes))
+ assert state.position == special_boxes_size
+ state.position = 0
+ state.append(rffi.cast(rffi.SHORT, len(topsnapshot.vable_boxes)))
+ self._number_boxes(topsnapshot.vable_boxes, optimizer, state)
+ n = len(topsnapshot.boxes)
+ assert not (n & 1)
+ state.append(rffi.cast(rffi.SHORT, n >> 1))
+ self._number_boxes(topsnapshot.boxes, optimizer, state)
+ assert state.position == special_boxes_size
+
+ numb = resumecode.create_numbering(state.current)
return numb, state.liveboxes, state.v
def forget_numberings(self):
@@ -1113,48 +1134,42 @@
self.boxes_f = boxes_f
self._prepare_next_section(info)
- def consume_virtualizable_boxes(self, vinfo):
+ def consume_virtualizable_boxes(self, vinfo, index):
# we have to ignore the initial part of 'nums' (containing vrefs),
# find the virtualizable from nums[-1], and use it to know how many
# boxes of which type we have to return. This does not write
# anything into the virtualizable.
numb = self.numb
- first_snapshot_size = rffi.cast(lltype.Signed, numb.first_snapshot_size)
- item, _ = resumecode.numb_next_item(numb, first_snapshot_size - 1)
+ item, index = resumecode.numb_next_item(numb, index)
virtualizablebox = self.decode_ref(item)
- index = first_snapshot_size - vinfo.get_total_size(virtualizablebox.getref_base()) - 1
virtualizable = vinfo.unwrap_virtualizable_box(virtualizablebox)
return vinfo.load_list_of_boxes(virtualizable, self, virtualizablebox,
numb, index)
- def consume_virtualref_boxes(self, end):
+ def consume_virtualref_boxes(self, index):
# Returns a list of boxes, assumed to be all BoxPtrs.
# We leave up to the caller to call vrefinfo.continue_tracing().
- assert (end & 1) == 0
+ size, index = resumecode.numb_next_item(self.numb, index)
+ if size == 0:
+ return [], index
lst = []
- self.cur_index = 0
- for i in range(end):
- item, self.cur_index = resumecode.numb_next_item(self.numb,
- self.cur_index)
+ for i in range(size * 2):
+ item, index = resumecode.numb_next_item(self.numb, index)
lst.append(self.decode_ref(item))
- return lst
+ return lst, index
def consume_vref_and_vable_boxes(self, vinfo, ginfo):
- first_snapshot_size = rffi.cast(lltype.Signed,
- self.numb.first_snapshot_size)
+ vable_size, index = resumecode.numb_next_item(self.numb, 0)
if vinfo is not None:
- virtualizable_boxes = self.consume_virtualizable_boxes(vinfo)
- end = first_snapshot_size - len(virtualizable_boxes)
+ virtualizable_boxes, index = self.consume_virtualizable_boxes(vinfo,
+ index)
elif ginfo is not None:
- item, self.cur_index = resumecode.numb_next_item(self.numb,
- first_snapshot_size - 1)
+ item, index = resumecode.numb_next_item(self.numb, index)
virtualizable_boxes = [self.decode_ref(item)]
- end = first_snapshot_size - 1
else:
- end = first_snapshot_size
virtualizable_boxes = None
- virtualref_boxes = self.consume_virtualref_boxes(end)
- self.cur_index = rffi.cast(lltype.Signed, self.numb.first_snapshot_size)
+ virtualref_boxes, index = self.consume_virtualref_boxes(index)
+ self.cur_index = index
return virtualizable_boxes, virtualref_boxes
def allocate_with_vtable(self, descr=None):
@@ -1429,39 +1444,36 @@
info = blackholeinterp.get_current_position_info()
self._prepare_next_section(info)
- def consume_virtualref_info(self, vrefinfo, end):
+ def consume_virtualref_info(self, vrefinfo, index):
# we have to decode a list of references containing pairs
- # [..., virtual, vref, ...] stopping at 'end'
- if vrefinfo is None:
- assert end == 0
- return
- assert (end & 1) == 0
- self.cur_index = 0
- for i in range(0, end, 2):
- virtual_item, self.cur_index = resumecode.numb_next_item(
- self.numb, self.cur_index)
- vref_item, self.cur_index = resumecode.numb_next_item(
- self.numb, self.cur_index)
+ # [..., virtual, vref, ...] and returns the index at the end
+ size, index = resumecode.numb_next_item(self.numb, index)
+ if vrefinfo is None or size == 0:
+ assert size == 0
+ return index
+ for i in range(size):
+ virtual_item, index = resumecode.numb_next_item(
+ self.numb, index)
+ vref_item, index = resumecode.numb_next_item(
+ self.numb, index)
virtual = self.decode_ref(virtual_item)
vref = self.decode_ref(vref_item)
# For each pair, we store the virtual inside the vref.
vrefinfo.continue_tracing(vref, virtual)
+ return index
- def consume_vable_info(self, vinfo):
+ def consume_vable_info(self, vinfo, index):
# we have to ignore the initial part of 'nums' (containing vrefs),
# find the virtualizable from nums[-1], load all other values
# from the CPU stack, and copy them into the virtualizable
numb = self.numb
- first_snapshot_size = rffi.cast(lltype.Signed, numb.first_snapshot_size)
- item, _ = resumecode.numb_next_item(self.numb,
- first_snapshot_size - 1)
+ item, index = resumecode.numb_next_item(self.numb, index)
virtualizable = self.decode_ref(item)
- start_index = first_snapshot_size - 1 - vinfo.get_total_size(virtualizable)
# just reset the token, we'll force it later
vinfo.reset_token_gcref(virtualizable)
- vinfo.write_from_resume_data_partial(virtualizable, self, start_index,
- numb)
- return start_index
+ index = vinfo.write_from_resume_data_partial(virtualizable, self,
+ index, numb)
+ return index
def load_value_of_type(self, TYPE, tagged):
from rpython.jit.metainterp.warmstate import specialize_value
@@ -1478,14 +1490,18 @@
load_value_of_type._annspecialcase_ = 'specialize:arg(1)'
def consume_vref_and_vable(self, vrefinfo, vinfo, ginfo):
+ vable_size, index = resumecode.numb_next_item(self.numb, 0)
if self.resume_after_guard_not_forced != 2:
- end_vref = rffi.cast(lltype.Signed, self.numb.first_snapshot_size)
if vinfo is not None:
- end_vref = self.consume_vable_info(vinfo)
+ index = self.consume_vable_info(vinfo, index)
if ginfo is not None:
- end_vref -= 1
- self.consume_virtualref_info(vrefinfo, end_vref)
- self.cur_index = rffi.cast(lltype.Signed, self.numb.first_snapshot_size)
+ _, index = resumecode.numb_next_item(self.numb, index)
+ index = self.consume_virtualref_info(vrefinfo, index)
+ else:
+ index = resumecode.numb_next_n_items(self.numb, vable_size, index)
+ vref_size, index = resumecode.numb_next_item(self.numb, index)
+ index = resumecode.numb_next_n_items(self.numb, vref_size * 2, index)
+ self.cur_index = index
def allocate_with_vtable(self, descr=None):
from rpython.jit.metainterp.executor import exec_new_with_vtable
diff --git a/rpython/jit/metainterp/resumecode.py b/rpython/jit/metainterp/resumecode.py
--- a/rpython/jit/metainterp/resumecode.py
+++ b/rpython/jit/metainterp/resumecode.py
@@ -1,96 +1,74 @@
""" Resume bytecode. It goes as following:
-<numb> <numb> <pc> <jitcode> <numb> <numb> <numb> <pc> <jitcode>
+ [<length> <virtualizable object> <numb> <numb> <numb>] if vinfo is not None
+ -OR-
+ [1 <ginfo object>] if ginfo is not None
+ -OR-
+ [0] if both are None
-until the length of the array.
+ [<length> <virtual> <vref> <virtual> <vref>] for virtualrefs
-The interface is only create_numbering/numb_next_item, but! there is a trick
-that uses first_snapshot_size + some knowledge about inside to decode
-virtualref/virtualizable_fields/virtualizable in that order in resume.py.
+ [<pc> <jitcode> <numb> <numb> <numb>] the frames
+ [<pc> <jitcode> <numb> <numb>]
+ ...
-If the algorithm changes, the part about how to find where virtualizable
-and virtualrefs are to be found
+ until the length of the array.
"""
from rpython.rtyper.lltypesystem import rffi, lltype
NUMBERINGP = lltype.Ptr(lltype.GcForwardReference())
NUMBERING = lltype.GcStruct('Numbering',
-# ('prev', NUMBERINGP),
-# ('prev_index', rffi.USHORT),
- ('first_snapshot_size', rffi.USHORT), # ugh, ugly
- ('code', lltype.Array(rffi.SHORT)))
+ ('code', lltype.Array(rffi.UCHAR)))
NUMBERINGP.TO.become(NUMBERING)
NULL_NUMBER = lltype.nullptr(NUMBERING)
-# this is the actually used version
+def create_numbering(lst):
+ result = []
+ for item in lst:
+ item = rffi.cast(lltype.Signed, item)
+ item *= 2
+ if item < 0:
+ item = -1 - item
-def create_numbering(lst, first_snapshot_size):
- numb = lltype.malloc(NUMBERING, len(lst))
- for i in range(len(lst)):
- numb.code[i] = rffi.cast(rffi.SHORT, lst[i])
- numb.first_snapshot_size = rffi.cast(rffi.USHORT, first_snapshot_size)
+ assert item >= 0
+ if item < 2**7:
+ result.append(rffi.cast(rffi.UCHAR, item))
+ elif item < 2**14:
+ result.append(rffi.cast(rffi.UCHAR, item | 0x80))
+ result.append(rffi.cast(rffi.UCHAR, item >> 7))
+ else:
+ assert item < 2**16
+ result.append(rffi.cast(rffi.UCHAR, item | 0x80))
+ result.append(rffi.cast(rffi.UCHAR, (item >> 7) | 0x80))
+ result.append(rffi.cast(rffi.UCHAR, item >> 14))
+
+ numb = lltype.malloc(NUMBERING, len(result))
+ for i in range(len(result)):
+ numb.code[i] = result[i]
return numb
def numb_next_item(numb, index):
- return rffi.cast(lltype.Signed, numb.code[index]), index + 1
+ value = rffi.cast(lltype.Signed, numb.code[index])
+ index += 1
+ if value & (2**7):
+ value &= 2**7 - 1
+ value |= rffi.cast(lltype.Signed, numb.code[index]) << 7
+ index += 1
+ if value & (2**14):
+ value &= 2**14 - 1
+ value |= rffi.cast(lltype.Signed, numb.code[index]) << 14
+ index += 1
+ if value & 1:
+ value = -1 - value
+ value >>= 1
+ return value, index
-# this is the version that can be potentially used
-
-def _create_numbering(lst, prev, prev_index, first_snapshot_size):
- count = 0
- for item in lst:
- if item < 0:
- if item < -63:
- count += 1
- if item > 127:
- count += 1
- count += 1
- numb = lltype.malloc(NUMBERING, count)
- numb.prev = prev
- numb.prev_index = rffi.cast(rffi.USHORT, prev_index)
- numb.first_snapshot_size = rffi.cast(rffi.USHORT, first_snapshot_size)
- index = 0
- for item in lst:
- if 0 <= item <= 128:
- numb.code[index] = rffi.cast(rffi.UCHAR, item)
- index += 1
- else:
- assert (item >> 8) <= 63
- if item < 0:
- item = -item
- if item <= 63:
- numb.code[index] = rffi.cast(rffi.UCHAR, item | 0x40)
- index += 1
- else:
- numb.code[index] = rffi.cast(rffi.UCHAR, (item >> 8) | 0x80 | 0x40)
- numb.code[index + 1] = rffi.cast(rffi.UCHAR, item & 0xff)
- index += 2
- else:
- numb.code[index] = rffi.cast(rffi.UCHAR, (item >> 8) | 0x80)
- numb.code[index + 1] = rffi.cast(rffi.UCHAR, item & 0xff)
- index += 2
- return numb
-
-def copy_from_list_to_numb(lst, numb, index):
- i = 0
- while i < len(lst):
- numb.code[i + index] = lst[i]
- i += 1
-
-def _numb_next_item(numb, index):
- one = rffi.cast(lltype.Signed, numb.code[index])
- if one & 0x40:
- if one & 0x80:
- two = rffi.cast(lltype.Signed, numb.code[index + 1])
- return -(((one & ~(0x80 | 0x40)) << 8) | two), index + 2
- else:
- return -(one & (~0x40)), index + 1
- if one & 0x80:
- two = rffi.cast(lltype.Signed, numb.code[index + 1])
- return ((one & 0x7f) << 8) | two, index + 2
- return one, index + 1
+def numb_next_n_items(numb, size, index):
+ for i in range(size):
+ _, index = numb_next_item(numb, index)
+ return index
def unpack_numbering(numb):
l = []
diff --git a/rpython/jit/metainterp/test/test_resume.py b/rpython/jit/metainterp/test/test_resume.py
--- a/rpython/jit/metainterp/test/test_resume.py
+++ b/rpython/jit/metainterp/test/test_resume.py
@@ -10,7 +10,7 @@
VArrayInfoNotClear, VStrPlainInfo, VStrConcatInfo, VStrSliceInfo,\
VUniPlainInfo, VUniConcatInfo, VUniSliceInfo, Snapshot, FrameInfo,\
capture_resumedata, ResumeDataLoopMemo, UNASSIGNEDVIRTUAL, INT,\
- annlowlevel, PENDINGFIELDSP, unpack_uint, TAG_CONST_OFFSET
+ annlowlevel, PENDINGFIELDSP, unpack_uint, TAG_CONST_OFFSET, TopSnapshot
from rpython.jit.metainterp.resumecode import unpack_numbering,\
create_numbering, NULL_NUMBER
@@ -22,9 +22,12 @@
from rpython.jit.codewriter import heaptracker, longlong
from rpython.jit.metainterp.resoperation import ResOperation, InputArgInt,\
InputArgRef, rop
+from rpython.jit.metainterp.test.strategies import boxlists
from rpython.rlib.debug import debug_start, debug_stop, debug_print,\
have_debug_prints
+from hypothesis import given
+
class Storage:
rd_frame_info_list = None
rd_numb = None
@@ -278,9 +281,7 @@
assert bh.written_f == expected_f
-def Numbering(nums):
- numb = create_numbering(nums, 0)
- return numb
+Numbering = create_numbering
def tagconst(i):
return tag(i + TAG_CONST_OFFSET, TAGCONST)
@@ -610,7 +611,8 @@
assert unpack_uint(frame_info_list.packed_jitcode_pc) == (2, 15)
snapshot = storage.rd_snapshot
- assert snapshot.boxes == vrs + vbs # in the same list
+ assert snapshot.boxes == vrs
+ assert snapshot.vable_boxes == [b2, b1]
snapshot = snapshot.prev
assert snapshot.prev is fs[2].parent_resumedata_snapshot
@@ -904,9 +906,9 @@
env = [b1, c1, b2, b1, c2]
snap = Snapshot(None, env)
env1 = [c3, b3, b1, c1]
- snap1 = Snapshot(snap, env1)
+ snap1 = TopSnapshot(snap, env1, [])
env2 = [c3, b3, b1, c3]
- snap2 = Snapshot(snap, env2)
+ snap2 = TopSnapshot(snap, env2, [])
memo = ResumeDataLoopMemo(FakeMetaInterpStaticData())
frameinfo = FrameInfo(None, FakeJitCode("jitcode", 0), 0)
@@ -916,10 +918,11 @@
assert liveboxes == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX),
b3: tag(2, TAGBOX)}
- base = [tag(0, TAGBOX), tag(1, TAGINT), tag(1, TAGBOX), tag(0, TAGBOX), tag(2, TAGINT)]
+ base = [0, 0, tag(0, TAGBOX), tag(1, TAGINT),
+ tag(1, TAGBOX), tag(0, TAGBOX), tag(2, TAGINT)]
- assert unpack_numbering(numb) == [
- tag(3, TAGINT), tag(2, TAGBOX), tag(0, TAGBOX), tag(1, TAGINT), 0, 0] + base
+ assert unpack_numbering(numb) == [0, 2, tag(3, TAGINT), tag(2, TAGBOX),
+ tag(0, TAGBOX), tag(1, TAGINT)] + base
numb2, liveboxes2, v = memo.number(FakeOptimizer(), snap2, frameinfo)
assert v == 0
@@ -927,11 +930,11 @@
assert liveboxes2 == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX),
b3: tag(2, TAGBOX)}
assert liveboxes2 is not liveboxes
- assert unpack_numbering(numb2) == [
- tag(3, TAGINT), tag(2, TAGBOX), tag(0, TAGBOX), tag(3, TAGINT), 0, 0] + base
+ assert unpack_numbering(numb2) == [0, 2, tag(3, TAGINT), tag(2, TAGBOX),
+ tag(0, TAGBOX), tag(3, TAGINT)] + base
env3 = [c3, b3, b1, c3]
- snap3 = Snapshot(snap, env3)
+ snap3 = TopSnapshot(snap, env3, [])
class FakeVirtualInfo(info.AbstractInfo):
def __init__(self, virt):
@@ -946,13 +949,12 @@
assert v == 0
assert liveboxes3 == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX)}
- assert unpack_numbering(numb3) == [tag(3, TAGINT), tag(4, TAGINT),
- tag(0, TAGBOX),
- tag(3, TAGINT), 0, 0] + base
+ assert unpack_numbering(numb3) == [0, 2, tag(3, TAGINT), tag(4, TAGINT),
+ tag(0, TAGBOX), tag(3, TAGINT)] + base
# virtual
env4 = [c3, b4, b1, c3]
- snap4 = Snapshot(snap, env4)
+ snap4 = TopSnapshot(snap, env4, [])
b4.set_forwarded(FakeVirtualInfo(True))
numb4, liveboxes4, v = memo.number(FakeOptimizer(), snap4, frameinfo)
@@ -960,11 +962,11 @@
assert liveboxes4 == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX),
b4: tag(0, TAGVIRTUAL)}
- assert unpack_numbering(numb4) == [tag(3, TAGINT), tag(0, TAGVIRTUAL),
- tag(0, TAGBOX), tag(3, TAGINT), 0, 0] + base
+ assert unpack_numbering(numb4) == [0, 2, tag(3, TAGINT), tag(0, TAGVIRTUAL),
+ tag(0, TAGBOX), tag(3, TAGINT)] + base
env5 = [b1, b4, b5]
- snap5 = Snapshot(snap4, env5)
+ snap5 = TopSnapshot(snap4, [], env5)
b4.set_forwarded(FakeVirtualInfo(True))
b5.set_forwarded(FakeVirtualInfo(True))
@@ -974,9 +976,30 @@
assert liveboxes5 == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX),
b4: tag(0, TAGVIRTUAL), b5: tag(1, TAGVIRTUAL)}
- assert unpack_numbering(numb5) == [tag(0, TAGBOX), tag(0, TAGVIRTUAL),
- tag(1, TAGVIRTUAL), 2, 1] + unpack_numbering(numb4)
+ assert unpack_numbering(numb5) == [
+ 3, tag(0, TAGBOX), tag(0, TAGVIRTUAL), tag(1, TAGVIRTUAL),
+ 0,
+ 2, 1, tag(3, TAGINT), tag(0, TAGVIRTUAL), tag(0, TAGBOX), tag(3, TAGINT)
+ ] + base
+ at given(boxlists)
+def test_ResumeDataLoopMemo_random(lst):
+ s = TopSnapshot(None, [], lst)
+ frameinfo = FrameInfo(None, FakeJitCode("foo", 0), 0)
+ memo = ResumeDataLoopMemo(FakeMetaInterpStaticData())
+ num, liveboxes, v = memo.number(FakeOptimizer(), s, frameinfo)
+ l = unpack_numbering(num)
+ assert l[-1] == 0
+ assert l[0] == len(lst)
+ for i, item in enumerate(lst):
+ v, tag = untag(l[i + 1])
+ if tag == TAGBOX:
+ assert l[i + 1] == liveboxes[item]
+ elif tag == TAGCONST:
+ assert memo.consts[v].getint() == item.getint()
+ elif tag == TAGINT:
+ assert v == item.getint()
+
def test_ResumeDataLoopMemo_number_boxes():
memo = ResumeDataLoopMemo(FakeMetaInterpStaticData())
b1, b2 = [InputArgInt(), InputArgInt()]
@@ -1060,10 +1083,11 @@
storage = Storage()
snapshot = Snapshot(None, [b1, ConstInt(1), b1, b2])
snapshot = Snapshot(snapshot, [ConstInt(2), ConstInt(3)])
- snapshot = Snapshot(snapshot, [b1, b2, b3])
- frameinfo = FrameInfo(FrameInfo(None, FakeJitCode("code1", 21), 22),
- FakeJitCode("code2", 31), 32)
- storage.rd_snapshot = snapshot
+ snapshot = Snapshot(snapshot, [b1, b2, b3])
+ top_snapshot = TopSnapshot(snapshot, [], [])
+ frameinfo = FrameInfo(FrameInfo(FrameInfo(None, FakeJitCode("code1", 21), 22),
+ FakeJitCode("code2", 31), 32), FakeJitCode("code3", 41), 42)
+ storage.rd_snapshot = top_snapshot
storage.rd_frame_info_list = frameinfo
return storage
@@ -1076,6 +1100,8 @@
assert storage.rd_snapshot is None
cpu = MyCPU([])
reader = ResumeDataDirectReader(MyMetaInterp(cpu), storage, "deadframe")
+ reader.consume_vref_and_vable(None, None, None)
+ reader.cur_index += 2 # framestack
_next_section(reader, sys.maxint, 2**16, -65)
reader.cur_index += 2 # framestack
_next_section(reader, 2, 3)
diff --git a/rpython/jit/metainterp/test/test_resumecode.py b/rpython/jit/metainterp/test/test_resumecode.py
--- a/rpython/jit/metainterp/test/test_resumecode.py
+++ b/rpython/jit/metainterp/test/test_resumecode.py
@@ -1,9 +1,12 @@
from rpython.jit.metainterp.resumecode import NUMBERING, NULL_NUMBER
from rpython.jit.metainterp.resumecode import create_numbering,\
- unpack_numbering, copy_from_list_to_numb
+ unpack_numbering
from rpython.rtyper.lltypesystem import lltype
+from hypothesis import strategies, given
+
+
def test_pack_unpack():
examples = [
[1, 2, 3, 4, 257, 10000, 13, 15],
@@ -12,5 +15,15 @@
[13000, 12000, 10000, 256, 255, 254, 257, -3, -1000]
]
for l in examples:
- n = create_numbering(l, 0)
+ n = create_numbering(l)
assert unpack_numbering(n) == l
+
+ at given(strategies.lists(strategies.integers(-2**15, 2**15-1)))
+def test_roundtrip(l):
+ n = create_numbering(l)
+ assert unpack_numbering(n) == l
+
+ at given(strategies.lists(strategies.integers(-2**15, 2**15-1)))
+def test_compressing(l):
+ n = create_numbering(l)
+ assert len(n.code) <= len(l) * 3
diff --git a/rpython/jit/metainterp/virtualizable.py b/rpython/jit/metainterp/virtualizable.py
--- a/rpython/jit/metainterp/virtualizable.py
+++ b/rpython/jit/metainterp/virtualizable.py
@@ -142,6 +142,7 @@
item, index = numb_next_item(numb, index)
x = reader.load_value_of_type(ARRAYITEMTYPE, item)
setarrayitem(lst, j, x)
+ return index
def load_list_of_boxes(virtualizable, reader, vable_box, numb, index):
virtualizable = cast_gcref_to_vtype(virtualizable)
@@ -161,7 +162,7 @@
box = reader.decode_box_of_type(ARRAYITEMTYPE, item)
boxes.append(box)
boxes.append(vable_box)
- return boxes
+ return boxes, index
def check_boxes(virtualizable, boxes):
virtualizable = cast_gcref_to_vtype(virtualizable)
diff --git a/rpython/rtyper/lltypesystem/ll2ctypes.py b/rpython/rtyper/lltypesystem/ll2ctypes.py
--- a/rpython/rtyper/lltypesystem/ll2ctypes.py
+++ b/rpython/rtyper/lltypesystem/ll2ctypes.py
@@ -426,7 +426,12 @@
else:
n = None
cstruct = cls._malloc(n)
- add_storage(container, _struct_mixin, ctypes.pointer(cstruct))
+
+ if isinstance(container, lltype._fixedsizearray):
+ cls_mixin = _fixedsizedarray_mixin
+ else:
+ cls_mixin = _struct_mixin
+ add_storage(container, cls_mixin, ctypes.pointer(cstruct))
if delayed_converters is None:
delayed_converters_was_None = True
@@ -506,7 +511,11 @@
def struct_use_ctypes_storage(container, ctypes_storage):
STRUCT = container._TYPE
assert isinstance(STRUCT, lltype.Struct)
- add_storage(container, _struct_mixin, ctypes_storage)
+ if isinstance(container, lltype._fixedsizearray):
+ cls_mixin = _fixedsizedarray_mixin
+ else:
+ cls_mixin = _struct_mixin
+ add_storage(container, cls_mixin, ctypes_storage)
remove_regular_struct_content(container)
for field_name in STRUCT._names:
FIELDTYPE = getattr(STRUCT, field_name)
@@ -645,11 +654,44 @@
cobj = lltype2ctypes(value)
setattr(self._storage.contents, field_name, cobj)
+class _fixedsizedarray_mixin(_parentable_mixin):
+ """Mixin added to _fixedsizearray containers when they become ctypes-based."""
+ __slots__ = ()
+
+ def __getattr__(self, field_name):
+ if hasattr(self, '_items'):
+ obj = lltype._fixedsizearray.__getattr__.im_func(self, field_name)
+ return obj
+ else:
+ cobj = getattr(self._storage.contents, field_name)
+ T = getattr(self._TYPE, field_name)
+ return ctypes2lltype(T, cobj)
+
+ def __setattr__(self, field_name, value):
+ if field_name.startswith('_'):
+ object.__setattr__(self, field_name, value) # '_xxx' attributes
+ else:
+ cobj = lltype2ctypes(value)
+ if hasattr(self, '_items'):
+ lltype._fixedsizearray.__setattr__.im_func(self, field_name, cobj)
+ else:
+ setattr(self._storage.contents, field_name, cobj)
+
+
def getitem(self, index, uninitialized_ok=False):
- return getattr(self, "item%s" % index)
+ if hasattr(self, '_items'):
+ obj = lltype._fixedsizearray.getitem.im_func(self,
+ index, uninitialized_ok=uninitialized_ok)
+ return obj
+ else:
+ return getattr(self, 'item%d' % index)
def setitem(self, index, value):
- setattr(self, "item%s" % index, value)
+ cobj = lltype2ctypes(value)
+ if hasattr(self, '_items'):
+ lltype._fixedsizearray.setitem.im_func(self, index, value)
+ else:
+ setattr(self, 'item%d' % index, cobj)
class _array_mixin(_parentable_mixin):
"""Mixin added to _array containers when they become ctypes-based."""
diff --git a/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py b/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py
--- a/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py
+++ b/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py
@@ -1461,3 +1461,20 @@
assert a[3].a == 17
#lltype.free(a, flavor='raw')
py.test.skip("free() not working correctly here...")
+
+ def test_fixedsizedarray_to_ctypes(self):
+ T = lltype.Ptr(rffi.CFixedArray(rffi.INT, 1))
+ inst = lltype.malloc(T.TO, flavor='raw')
+ inst[0] = rffi.cast(rffi.INT, 42)
+ assert inst[0] == 42
+ cinst = lltype2ctypes(inst)
+ assert rffi.cast(lltype.Signed, inst[0]) == 42
+ assert cinst.contents.item0 == 42
+ lltype.free(inst, flavor='raw')
+
+ def test_fixedsizedarray_to_ctypes(self):
+ T = lltype.Ptr(rffi.CFixedArray(rffi.CHAR, 123))
+ inst = lltype.malloc(T.TO, flavor='raw', zero=True)
+ cinst = lltype2ctypes(inst)
+ assert cinst.contents.item0 == 0
+ lltype.free(inst, flavor='raw')
More information about the pypy-commit
mailing list