aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'rpython')
-rw-r--r--rpython/annotator/policy.py34
-rw-r--r--rpython/annotator/specialize.py12
-rw-r--r--rpython/annotator/unaryop.py5
-rw-r--r--rpython/jit/backend/ppc/test/test_runner.py2
-rw-r--r--rpython/jit/codewriter/jtransform.py5
-rw-r--r--rpython/jit/codewriter/support.py15
-rw-r--r--rpython/jit/metainterp/blackhole.py8
-rw-r--r--rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py2
-rw-r--r--rpython/jit/metainterp/optimizeopt/test/test_util.py5
-rw-r--r--rpython/jit/metainterp/optimizeopt/util.py2
-rw-r--r--rpython/jit/metainterp/pyjitpl.py11
-rw-r--r--rpython/jit/metainterp/resume.py154
-rw-r--r--rpython/jit/metainterp/resumecode.py122
-rw-r--r--rpython/jit/metainterp/test/strategies.py13
-rw-r--r--rpython/jit/metainterp/test/test_ajit.py25
-rw-r--r--rpython/jit/metainterp/test/test_jitdriver.py17
-rw-r--r--rpython/jit/metainterp/test/test_resume.py91
-rw-r--r--rpython/jit/metainterp/test/test_resumecode.py17
-rw-r--r--rpython/jit/metainterp/test/test_tlc.py1
-rw-r--r--rpython/jit/metainterp/virtualizable.py3
-rw-r--r--rpython/memory/gc/incminimark.py294
-rw-r--r--rpython/memory/gc/test/test_rawrefcount.py282
-rw-r--r--rpython/memory/gctransform/boehm.py4
-rw-r--r--rpython/memory/gctransform/framework.py72
-rw-r--r--rpython/memory/gctransform/refcounting.py4
-rw-r--r--rpython/memory/gctransform/test/test_transform.py5
-rw-r--r--rpython/memory/test/test_transformed_gc.py5
-rw-r--r--rpython/rlib/_os_support.py (renamed from rpython/rtyper/module/support.py)122
-rw-r--r--rpython/rlib/exports.py2
-rw-r--r--rpython/rlib/jit.py20
-rw-r--r--rpython/rlib/objectmodel.py2
-rw-r--r--rpython/rlib/rawrefcount.py265
-rw-r--r--rpython/rlib/rgc.py1
-rw-r--r--rpython/rlib/rposix.py62
-rw-r--r--rpython/rlib/rposix_environ.py3
-rw-r--r--rpython/rlib/rposix_stat.py4
-rw-r--r--rpython/rlib/rsocket.py9
-rw-r--r--rpython/rlib/rvmprof/cintf.py2
-rw-r--r--rpython/rlib/test/test_jit.py11
-rw-r--r--rpython/rlib/test/test_posix.py304
-rw-r--r--rpython/rlib/test/test_rawrefcount.py268
-rw-r--r--rpython/rlib/test/test_rerased.py2
-rw-r--r--rpython/rtyper/extfunc.py200
-rw-r--r--rpython/rtyper/llinterp.py15
-rw-r--r--rpython/rtyper/lltypesystem/ll2ctypes.py78
-rw-r--r--rpython/rtyper/lltypesystem/lloperation.py8
-rw-r--r--rpython/rtyper/lltypesystem/lltype.py43
-rw-r--r--rpython/rtyper/lltypesystem/module/ll_math.py2
-rw-r--r--rpython/rtyper/lltypesystem/opimpl.py6
-rw-r--r--rpython/rtyper/lltypesystem/rffi.py17
-rw-r--r--rpython/rtyper/lltypesystem/rstr.py7
-rw-r--r--rpython/rtyper/lltypesystem/test/test_ll2ctypes.py19
-rw-r--r--rpython/rtyper/module/__init__.py1
-rw-r--r--rpython/rtyper/module/test/__init__.py1
-rw-r--r--rpython/rtyper/module/test/test_ll_strtod.py13
-rw-r--r--rpython/rtyper/module/test/test_posix.py304
-rw-r--r--rpython/rtyper/rtyper.py15
-rw-r--r--rpython/rtyper/test/test_extfunc.py73
-rw-r--r--rpython/rtyper/test/test_llinterp.py3
-rw-r--r--rpython/rtyper/test/test_rbuiltin.py23
-rw-r--r--rpython/rtyper/test/test_rdict.py365
-rw-r--r--rpython/rtyper/test/test_rfloat.py8
-rw-r--r--rpython/rtyper/test/test_rpbc.py57
-rw-r--r--rpython/rtyper/test/tool.py75
-rwxr-xr-xrpython/rtyper/tool/rffi_platform.py17
-rw-r--r--rpython/rtyper/tool/test/test_rffi_platform.py13
-rw-r--r--rpython/translator/c/funcgen.py6
-rw-r--r--rpython/translator/c/node.py8
-rw-r--r--rpython/translator/sandbox/test/test_sandbox.py15
69 files changed, 2491 insertions, 1188 deletions
diff --git a/rpython/annotator/policy.py b/rpython/annotator/policy.py
index 5a33b79083..93c3dccdd1 100644
--- a/rpython/annotator/policy.py
+++ b/rpython/annotator/policy.py
@@ -3,6 +3,9 @@ from rpython.annotator.specialize import default_specialize as default
from rpython.annotator.specialize import (
specialize_argvalue, specialize_argtype, specialize_arglistitemtype,
specialize_arg_or_var, memo, specialize_call_location)
+from rpython.flowspace.operation import op
+from rpython.flowspace.model import Constant
+from rpython.annotator.model import SomeTuple
class AnnotatorPolicy(object):
@@ -64,7 +67,34 @@ class AnnotatorPolicy(object):
return LowLevelAnnotatorPolicy.specialize__ll_and_arg(*args)
def no_more_blocks_to_annotate(pol, annotator):
+ bk = annotator.bookkeeper
# hint to all pending specializers that we are done
- for callback in annotator.bookkeeper.pending_specializations:
+ for callback in bk.pending_specializations:
callback()
- del annotator.bookkeeper.pending_specializations[:]
+ del bk.pending_specializations[:]
+ if annotator.added_blocks is not None:
+ all_blocks = annotator.added_blocks
+ else:
+ all_blocks = annotator.annotated
+ for block in list(all_blocks):
+ for i, instr in enumerate(block.operations):
+ if not isinstance(instr, (op.simple_call, op.call_args)):
+ continue
+ v_func = instr.args[0]
+ s_func = annotator.annotation(v_func)
+ if not hasattr(s_func, 'needs_sandboxing'):
+ continue
+ key = ('sandboxing', s_func.const)
+ if key not in bk.emulated_pbc_calls:
+ params_s = s_func.args_s
+ s_result = s_func.s_result
+ from rpython.translator.sandbox.rsandbox import make_sandbox_trampoline
+ sandbox_trampoline = make_sandbox_trampoline(
+ s_func.name, params_s, s_result)
+ sandbox_trampoline._signature_ = [SomeTuple(items=params_s)], s_result
+ bk.emulate_pbc_call(key, bk.immutablevalue(sandbox_trampoline), params_s)
+ else:
+ s_trampoline = bk.emulated_pbc_calls[key][0]
+ sandbox_trampoline = s_trampoline.const
+ new = instr.replace({instr.args[0]: Constant(sandbox_trampoline)})
+ block.operations[i] = new
diff --git a/rpython/annotator/specialize.py b/rpython/annotator/specialize.py
index 14bd0d6e85..436d8ec635 100644
--- a/rpython/annotator/specialize.py
+++ b/rpython/annotator/specialize.py
@@ -317,18 +317,6 @@ def cartesian_product(lstlst):
yield (value,) + tuple_tail
-def make_constgraphbuilder(n, v=None, factory=None, srcmodule=None):
- def constgraphbuilder(translator, ignore):
- args = ','.join(["arg%d" % i for i in range(n)])
- if factory is not None:
- computed_v = factory()
- else:
- computed_v = v
- miniglobals = {'v': computed_v, '__name__': srcmodule}
- exec py.code.Source("constf = lambda %s: v" % args).compile() in miniglobals
- return translator.buildflowgraph(miniglobals['constf'])
- return constgraphbuilder
-
def maybe_star_args(funcdesc, key, args_s):
args_s, key1, builder = flatten_star_args(funcdesc, args_s)
if key1 is not None:
diff --git a/rpython/annotator/unaryop.py b/rpython/annotator/unaryop.py
index 50ea9b7769..27629f5639 100644
--- a/rpython/annotator/unaryop.py
+++ b/rpython/annotator/unaryop.py
@@ -113,8 +113,9 @@ def contains_number(annotator, number, element):
@op.simple_call.register(SomeObject)
def simple_call_SomeObject(annotator, func, *args):
- return annotator.annotation(func).call(
- simple_args([annotator.annotation(arg) for arg in args]))
+ s_func = annotator.annotation(func)
+ argspec = simple_args([annotator.annotation(arg) for arg in args])
+ return s_func.call(argspec)
@op.call_args.register_transform(SomeObject)
def transform_varargs(annotator, v_func, v_shape, *data_v):
diff --git a/rpython/jit/backend/ppc/test/test_runner.py b/rpython/jit/backend/ppc/test/test_runner.py
index 3d2c0ef959..9137407b84 100644
--- a/rpython/jit/backend/ppc/test/test_runner.py
+++ b/rpython/jit/backend/ppc/test/test_runner.py
@@ -134,7 +134,7 @@ class TestPPC(LLtypeBackendTest):
def test_debugger_on(self):
py.test.skip("XXX")
- from pypy.rlib import debug
+ from rpython.rlib import debug
targettoken, preambletoken = TargetToken(), TargetToken()
loop = """
diff --git a/rpython/jit/codewriter/jtransform.py b/rpython/jit/codewriter/jtransform.py
index 2e8e683dd9..12357425a9 100644
--- a/rpython/jit/codewriter/jtransform.py
+++ b/rpython/jit/codewriter/jtransform.py
@@ -2042,6 +2042,11 @@ class Transformer(object):
self.vable_flags[op.args[0]] = op.args[2].value
return []
+ def rewrite_op_jit_enter_portal_frame(self, op):
+ return [op]
+ def rewrite_op_jit_leave_portal_frame(self, op):
+ return [op]
+
# ---------
# ll_math.sqrt_nonneg()
diff --git a/rpython/jit/codewriter/support.py b/rpython/jit/codewriter/support.py
index 177a507e69..e319a87883 100644
--- a/rpython/jit/codewriter/support.py
+++ b/rpython/jit/codewriter/support.py
@@ -246,12 +246,12 @@ def _ll_1_jit_force_virtual(inst):
def _ll_2_int_floordiv_ovf_zer(x, y):
if y == 0:
raise ZeroDivisionError
- if x == -sys.maxint - 1 and y == -1:
- raise OverflowError
- return llop.int_floordiv(lltype.Signed, x, y)
+ return _ll_2_int_floordiv_ovf(x, y)
def _ll_2_int_floordiv_ovf(x, y):
- if x == -sys.maxint - 1 and y == -1:
+ # intentionally not short-circuited to produce only one guard
+ # and to remove the check fully if one of the arguments is known
+ if (x == -sys.maxint - 1) & (y == -1):
raise OverflowError
return llop.int_floordiv(lltype.Signed, x, y)
@@ -263,12 +263,11 @@ def _ll_2_int_floordiv_zer(x, y):
def _ll_2_int_mod_ovf_zer(x, y):
if y == 0:
raise ZeroDivisionError
- if x == -sys.maxint - 1 and y == -1:
- raise OverflowError
- return llop.int_mod(lltype.Signed, x, y)
+ return _ll_2_int_mod_ovf(x, y)
def _ll_2_int_mod_ovf(x, y):
- if x == -sys.maxint - 1 and y == -1:
+ #see comment in _ll_2_int_floordiv_ovf
+ if (x == -sys.maxint - 1) & (y == -1):
raise OverflowError
return llop.int_mod(lltype.Signed, x, y)
diff --git a/rpython/jit/metainterp/blackhole.py b/rpython/jit/metainterp/blackhole.py
index 47e5ff0950..20a69597a5 100644
--- a/rpython/jit/metainterp/blackhole.py
+++ b/rpython/jit/metainterp/blackhole.py
@@ -944,6 +944,14 @@ class BlackholeInterpreter(object):
pass
@arguments("i")
+ def bhimpl_jit_enter_portal_frame(x):
+ pass
+
+ @arguments()
+ def bhimpl_jit_leave_portal_frame():
+ pass
+
+ @arguments("i")
def bhimpl_int_assert_green(x):
pass
@arguments("r")
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
index 2081651bbc..b196fe341e 100644
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
@@ -28,7 +28,7 @@ def test_store_final_boxes_in_guard():
# setup rd data
fi0 = resume.FrameInfo(None, FakeJitCode(), 11)
snapshot0 = resume.Snapshot(None, [b0])
- op.rd_snapshot = resume.Snapshot(snapshot0, [b1])
+ op.rd_snapshot = resume.TopSnapshot(snapshot0, [], [b1])
op.rd_frame_info_list = resume.FrameInfo(fi0, FakeJitCode(), 33)
#
opt.store_final_boxes_in_guard(op, [])
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_util.py b/rpython/jit/metainterp/optimizeopt/test/test_util.py
index 8c190a2b9b..2ac0e98d1f 100644
--- a/rpython/jit/metainterp/optimizeopt/test/test_util.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_util.py
@@ -506,14 +506,15 @@ class BaseTest(object):
index = 0
if op.is_guard():
- op.rd_snapshot = resume.Snapshot(None, op.getfailargs())
+ op.rd_snapshot = resume.TopSnapshot(
+ resume.Snapshot(None, op.getfailargs()), [], [])
op.rd_frame_info_list = resume.FrameInfo(None, FakeJitCode(), 11)
def add_guard_future_condition(self, res):
# invent a GUARD_FUTURE_CONDITION to not have to change all tests
if res.operations[-1].getopnum() == rop.JUMP:
guard = ResOperation(rop.GUARD_FUTURE_CONDITION, [], None)
- guard.rd_snapshot = resume.Snapshot(None, [])
+ guard.rd_snapshot = resume.TopSnapshot(None, [], [])
res.operations.insert(-1, guard)
def assert_equal(self, optimized, expected, text_right=None):
diff --git a/rpython/jit/metainterp/optimizeopt/util.py b/rpython/jit/metainterp/optimizeopt/util.py
index c434fb63d5..78bccaf865 100644
--- a/rpython/jit/metainterp/optimizeopt/util.py
+++ b/rpython/jit/metainterp/optimizeopt/util.py
@@ -8,7 +8,7 @@ from rpython.rlib.objectmodel import we_are_translated
from rpython.jit.metainterp import resoperation
from rpython.rlib.debug import make_sure_not_resized
from rpython.jit.metainterp.resoperation import rop
-from rpython.jit.metainterp.resume import Snapshot, AccumInfo
+from rpython.jit.metainterp.resume import AccumInfo
# ____________________________________________________________
# Misc. utilities
diff --git a/rpython/jit/metainterp/pyjitpl.py b/rpython/jit/metainterp/pyjitpl.py
index 8bfb259e18..fe3521e995 100644
--- a/rpython/jit/metainterp/pyjitpl.py
+++ b/rpython/jit/metainterp/pyjitpl.py
@@ -1358,6 +1358,17 @@ class MIFrame(object):
self.metainterp.attach_debug_info(op)
@arguments("box")
+ def opimpl_jit_enter_portal_frame(self, uniqueidbox):
+ unique_id = uniqueidbox.getint()
+ jd_no = self.metainterp.jitdriver_sd.mainjitcode.index # fish
+ self.metainterp.enter_portal_frame(jd_no, unique_id)
+
+ @arguments()
+ def opimpl_jit_leave_portal_frame(self):
+ jd_no = self.metainterp.jitdriver_sd.mainjitcode.index # fish
+ self.metainterp.leave_portal_frame(jd_no)
+
+ @arguments("box")
def _opimpl_assert_green(self, box):
if not isinstance(box, Const):
msg = "assert_green failed at %s:%d" % (
diff --git a/rpython/jit/metainterp/resume.py b/rpython/jit/metainterp/resume.py
index e563896a91..21b8b55f7a 100644
--- a/rpython/jit/metainterp/resume.py
+++ b/rpython/jit/metainterp/resume.py
@@ -27,6 +27,13 @@ class Snapshot(object):
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 @@ def capture_resumedata(framestack, virtualizable_boxes, virtualref_boxes,
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 @@ def capture_resumedata(framestack, virtualizable_boxes, virtualref_boxes,
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 @@ class NumberingState(object):
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 @@ class ResumeDataLoopMemo(object):
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 @@ class ResumeDataLoopMemo(object):
# 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)
-
- numb = resumecode.create_numbering(state.current,
- len(vref_snapshot.boxes))
-
+ state.position -= len(snapshot_list[i].boxes) + 2
+
+ 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 @@ class ResumeDataBoxReader(AbstractResumeDataReader):
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 @@ class ResumeDataDirectReader(AbstractResumeDataReader):
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 @@ class ResumeDataDirectReader(AbstractResumeDataReader):
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
index 43f65a92a3..8c2104da83 100644
--- 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, 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)
- return numb
-
-def numb_next_item(numb, index):
- return rffi.cast(lltype.Signed, numb.code[index]), index + 1
-
-# this is the version that can be potentially used
-
-def _create_numbering(lst, prev, prev_index, first_snapshot_size):
- count = 0
+def create_numbering(lst):
+ result = []
for item in lst:
+ item = rffi.cast(lltype.Signed, item)
+ item *= 2
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
+ item = -1 - item
+
+ 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 >> 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
+ 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 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):
+ 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
-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/strategies.py b/rpython/jit/metainterp/test/strategies.py
new file mode 100644
index 0000000000..e7cd79ec20
--- /dev/null
+++ b/rpython/jit/metainterp/test/strategies.py
@@ -0,0 +1,13 @@
+
+import sys
+from hypothesis import strategies
+from rpython.jit.metainterp.resoperation import InputArgInt
+from rpython.jit.metainterp.history import ConstInt
+
+machine_ints = strategies.integers(min_value=-sys.maxint - 1,
+ max_value=sys.maxint)
+intboxes = strategies.builds(InputArgInt)
+intconsts = strategies.builds(ConstInt, machine_ints)
+boxes = intboxes | intconsts
+boxlists = strategies.lists(boxes, min_size=1).flatmap(
+ lambda cis: strategies.lists(strategies.sampled_from(cis))) \ No newline at end of file
diff --git a/rpython/jit/metainterp/test/test_ajit.py b/rpython/jit/metainterp/test/test_ajit.py
index fc4a231aca..ecf82bcbf6 100644
--- a/rpython/jit/metainterp/test/test_ajit.py
+++ b/rpython/jit/metainterp/test/test_ajit.py
@@ -1199,6 +1199,31 @@ class BasicTests:
(-sys.maxint-1) // (-6) +
100 * 8)
+ def test_overflow_fold_if_divisor_constant(self):
+ import sys
+ from rpython.rtyper.lltypesystem.lloperation import llop
+ myjitdriver = JitDriver(greens = [], reds = ['x', 'y', 'res'])
+ def f(x, y):
+ res = 0
+ while y > 0:
+ myjitdriver.can_enter_jit(x=x, y=y, res=res)
+ myjitdriver.jit_merge_point(x=x, y=y, res=res)
+ try:
+ res += llop.int_floordiv_ovf(lltype.Signed,
+ x, 2)
+ res += llop.int_mod_ovf(lltype.Signed,
+ x, 2)
+ x += 5
+ except OverflowError:
+ res += 100
+ y -= 1
+ return res
+ res = self.meta_interp(f, [-41, 8])
+ # the guard_true are for the loop condition
+ # the guard_false needed to check whether an overflow can occur have
+ # been folded away
+ self.check_resops(guard_true=2, guard_false=0)
+
def test_isinstance(self):
class A:
pass
diff --git a/rpython/jit/metainterp/test/test_jitdriver.py b/rpython/jit/metainterp/test/test_jitdriver.py
index 56f26ffd85..cba873a956 100644
--- a/rpython/jit/metainterp/test/test_jitdriver.py
+++ b/rpython/jit/metainterp/test/test_jitdriver.py
@@ -213,6 +213,21 @@ class MultipleJitDriversTests(object):
if op.getopname() == 'enter_portal_frame':
assert op.getarg(0).getint() == 0
assert op.getarg(1).getint() == 1
-
+
+ def test_manual_leave_enter_portal_frame(self):
+ from rpython.rlib import jit
+ driver = JitDriver(greens=[], reds='auto', is_recursive=True)
+
+ def f(arg):
+ i = 0
+ while i < 100:
+ driver.jit_merge_point()
+ jit.enter_portal_frame(42)
+ jit.leave_portal_frame()
+ i += 1
+
+ self.meta_interp(f, [0])
+ self.check_simple_loop(enter_portal_frame=1, leave_portal_frame=1)
+
class TestLLtype(MultipleJitDriversTests, LLJitMixin):
pass
diff --git a/rpython/jit/metainterp/test/test_resume.py b/rpython/jit/metainterp/test/test_resume.py
index ebbd602860..dd2a9f501d 100644
--- a/rpython/jit/metainterp/test/test_resume.py
+++ b/rpython/jit/metainterp/test/test_resume.py
@@ -10,7 +10,7 @@ from rpython.jit.metainterp.resume import ResumeDataVirtualAdder,\
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,8 +22,12 @@ from rpython.jit.metainterp import executor
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 rpython.jit.metainterp import resumecode
+
+from hypothesis import given
class Storage:
rd_frame_info_list = None
@@ -278,9 +282,7 @@ def _next_section(reader, *expected):
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 +612,8 @@ def test_capture_resumedata():
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 +907,9 @@ def test_ResumeDataLoopMemo_number():
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 +919,11 @@ def test_ResumeDataLoopMemo_number():
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 +931,11 @@ def test_ResumeDataLoopMemo_number():
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 +950,12 @@ def test_ResumeDataLoopMemo_number():
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 +963,11 @@ def test_ResumeDataLoopMemo_number():
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 +977,30 @@ def test_ResumeDataLoopMemo_number():
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
+
+@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 +1084,11 @@ def make_storage(b1, b2, b3):
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 +1101,8 @@ def test_virtual_adder_int_constants():
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)
@@ -1116,7 +1143,8 @@ class ResumeDataFakeReader(ResumeDataBoxReader):
class MyInfo:
@staticmethod
def enumerate_vars(callback_i, callback_r, callback_f, _, index):
- for tagged in self.numb.code:
+ while index < len(self.numb.code):
+ tagged, _ = resumecode.numb_next_item(self.numb, index)
_, tag = untag(tagged)
if tag == TAGVIRTUAL:
kind = REF
@@ -1131,6 +1159,13 @@ class ResumeDataFakeReader(ResumeDataBoxReader):
index = callback_f(index, index)
else:
assert 0
+ size, self.cur_index = resumecode.numb_next_item(self.numb, 0)
+ assert size == 0
+ size, self.cur_index = resumecode.numb_next_item(self.numb, self.cur_index)
+ assert size == 0
+ pc, self.cur_index = resumecode.numb_next_item(self.numb, self.cur_index)
+ jitcode_pos, self.cur_index = resumecode.numb_next_item(self.numb, self.cur_index)
+
self._prepare_next_section(MyInfo())
return self.lst
diff --git a/rpython/jit/metainterp/test/test_resumecode.py b/rpython/jit/metainterp/test/test_resumecode.py
index 848a4f1009..f65fae7d07 100644
--- 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 @@ def test_pack_unpack():
[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
+
+@given(strategies.lists(strategies.integers(-2**15, 2**15-1)))
+def test_roundtrip(l):
+ n = create_numbering(l)
+ assert unpack_numbering(n) == l
+
+@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/test/test_tlc.py b/rpython/jit/metainterp/test/test_tlc.py
index ee88035bf2..5c1396d533 100644
--- a/rpython/jit/metainterp/test/test_tlc.py
+++ b/rpython/jit/metainterp/test/test_tlc.py
@@ -1,5 +1,4 @@
import py
-from rpython.rtyper.module.support import LLSupport
from rpython.jit.tl import tlc
diff --git a/rpython/jit/metainterp/virtualizable.py b/rpython/jit/metainterp/virtualizable.py
index 08f2ec2059..3278ff0e84 100644
--- a/rpython/jit/metainterp/virtualizable.py
+++ b/rpython/jit/metainterp/virtualizable.py
@@ -142,6 +142,7 @@ class VirtualizableInfo(object):
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 @@ class VirtualizableInfo(object):
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/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py
index 6c04f077c6..7e9999fb6d 100644
--- a/rpython/memory/gc/incminimark.py
+++ b/rpython/memory/gc/incminimark.py
@@ -706,6 +706,7 @@ class IncrementalMiniMarkGC(MovingGCBase):
self.major_collection_step()
else:
self.minor_and_major_collection()
+ self.rrc_invoke_callback()
def collect_and_reserve(self, totalsize):
@@ -783,12 +784,15 @@ class IncrementalMiniMarkGC(MovingGCBase):
self.threshold_reached()): # ^^but only if still
self.minor_collection() # the same collection
self.major_collection_step()
- #
- # The nursery might not be empty now, because of
- # execute_finalizers(). If it is almost full again,
- # we need to fix it with another call to minor_collection().
- if self.nursery_free + totalsize > self.nursery_top:
- self.minor_collection()
+ #
+ self.rrc_invoke_callback()
+ #
+ # The nursery might not be empty now, because of
+ # execute_finalizers() or rrc_invoke_callback().
+ # If it is almost full again,
+ # we need to fix it with another call to minor_collection().
+ if self.nursery_free + totalsize > self.nursery_top:
+ self.minor_collection()
#
else:
ll_assert(minor_collection_count == 2,
@@ -861,6 +865,7 @@ class IncrementalMiniMarkGC(MovingGCBase):
if self.threshold_reached(raw_malloc_usage(totalsize) +
self.nursery_size // 2):
self.major_collection_step(raw_malloc_usage(totalsize))
+ self.rrc_invoke_callback()
# note that this loop should not be infinite: when the
# last step of a major collection is done but
# threshold_reached(totalsize) is still true, then
@@ -1080,35 +1085,19 @@ class IncrementalMiniMarkGC(MovingGCBase):
"odd-valued (i.e. tagged) pointer unexpected here")
return self.nursery <= addr < self.nursery + self.nursery_size
- def appears_to_be_young(self, addr):
- # "is a valid addr to a young object?"
- # but it's ok to occasionally return True accidentally.
- # Maybe the best implementation would be a bloom filter
- # of some kind instead of the dictionary lookup that is
- # sometimes done below. But the expected common answer
- # is "Yes" because addr points to the nursery, so it may
- # not be useful to optimize the other case too much.
- #
- # First, if 'addr' appears to be a pointer to some place within
- # the nursery, return True
- if not self.translated_to_c:
- # When non-translated, filter out tagged pointers explicitly.
- # When translated, it may occasionally give a wrong answer
- # of True if 'addr' is a tagged pointer with just the wrong value.
- if not self.is_valid_gc_object(addr):
- return False
-
+ def is_young_object(self, addr):
+ # Check if the object at 'addr' is young.
+ if not self.is_valid_gc_object(addr):
+ return False # filter out tagged pointers explicitly.
if self.nursery <= addr < self.nursery_top:
return True # addr is in the nursery
- #
# Else, it may be in the set 'young_rawmalloced_objects'
return (bool(self.young_rawmalloced_objects) and
self.young_rawmalloced_objects.contains(addr))
- appears_to_be_young._always_inline_ = True
def debug_is_old_object(self, addr):
return (self.is_valid_gc_object(addr)
- and not self.appears_to_be_young(addr))
+ and not self.is_young_object(addr))
def is_forwarded(self, obj):
"""Returns True if the nursery obj is marked as forwarded.
@@ -1618,6 +1607,10 @@ class IncrementalMiniMarkGC(MovingGCBase):
self._visit_old_objects_pointing_to_pinned, None)
current_old_objects_pointing_to_pinned.delete()
#
+ # visit the P list from rawrefcount, if enabled.
+ if self.rrc_enabled:
+ self.rrc_minor_collection_trace()
+ #
while True:
# If we are using card marking, do a partial trace of the arrays
# that are flagged with GCFLAG_CARDS_SET.
@@ -1666,6 +1659,10 @@ class IncrementalMiniMarkGC(MovingGCBase):
if self.young_rawmalloced_objects:
self.free_young_rawmalloced_objects()
#
+ # visit the P and O lists from rawrefcount, if enabled.
+ if self.rrc_enabled:
+ self.rrc_minor_collection_free()
+ #
# All live nursery objects are out of the nursery or pinned inside
# the nursery. Create nursery barriers to protect the pinned objects,
# fill the rest of the nursery with zeros and reset the current nursery
@@ -2178,9 +2175,13 @@ class IncrementalMiniMarkGC(MovingGCBase):
# finalizers/weak references are rare and short which means that
# they do not need a separate state and do not need to be
# made incremental.
+ # For now, the same applies to rawrefcount'ed objects.
if (not self.objects_to_trace.non_empty() and
not self.more_objects_to_trace.non_empty()):
#
+ if self.rrc_enabled:
+ self.rrc_major_collection_trace()
+ #
if self.objects_with_finalizers.non_empty():
self.deal_with_objects_with_finalizers()
elif self.old_objects_with_weakrefs.non_empty():
@@ -2215,6 +2216,10 @@ class IncrementalMiniMarkGC(MovingGCBase):
self.old_objects_pointing_to_pinned = \
new_old_objects_pointing_to_pinned
self.updated_old_objects_pointing_to_pinned = True
+ #
+ if self.rrc_enabled:
+ self.rrc_major_collection_free()
+ #
self.gc_state = STATE_SWEEPING
#END MARKING
elif self.gc_state == STATE_SWEEPING:
@@ -2745,3 +2750,238 @@ class IncrementalMiniMarkGC(MovingGCBase):
(obj + offset).address[0] = llmemory.NULL
self.old_objects_with_weakrefs.delete()
self.old_objects_with_weakrefs = new_with_weakref
+
+
+ # ----------
+ # RawRefCount
+
+ rrc_enabled = False
+
+ _ADDRARRAY = lltype.Array(llmemory.Address, hints={'nolength': True})
+ PYOBJ_HDR = lltype.Struct('GCHdr_PyObject',
+ ('ob_refcnt', lltype.Signed),
+ ('ob_pypy_link', lltype.Signed))
+ PYOBJ_HDR_PTR = lltype.Ptr(PYOBJ_HDR)
+ RAWREFCOUNT_DEALLOC_TRIGGER = lltype.Ptr(lltype.FuncType([], lltype.Void))
+
+ def _pyobj(self, pyobjaddr):
+ return llmemory.cast_adr_to_ptr(pyobjaddr, self.PYOBJ_HDR_PTR)
+
+ def rawrefcount_init(self, dealloc_trigger_callback):
+ # see pypy/doc/discussion/rawrefcount.rst
+ if not self.rrc_enabled:
+ self.rrc_p_list_young = self.AddressStack()
+ self.rrc_p_list_old = self.AddressStack()
+ self.rrc_o_list_young = self.AddressStack()
+ self.rrc_o_list_old = self.AddressStack()
+ self.rrc_p_dict = self.AddressDict() # non-nursery keys only
+ self.rrc_p_dict_nurs = self.AddressDict() # nursery keys only
+ p = lltype.malloc(self._ADDRARRAY, 1, flavor='raw',
+ track_allocation=False)
+ self.rrc_singleaddr = llmemory.cast_ptr_to_adr(p)
+ self.rrc_dealloc_trigger_callback = dealloc_trigger_callback
+ self.rrc_dealloc_pending = self.AddressStack()
+ self.rrc_enabled = True
+
+ def check_no_more_rawrefcount_state(self):
+ "NOT_RPYTHON: for tests"
+ assert self.rrc_p_list_young.length() == 0
+ assert self.rrc_p_list_old .length() == 0
+ assert self.rrc_o_list_young.length() == 0
+ assert self.rrc_o_list_old .length() == 0
+ def check_value_is_null(key, value, ignore):
+ assert value == llmemory.NULL
+ self.rrc_p_dict.foreach(check_value_is_null, None)
+ self.rrc_p_dict_nurs.foreach(check_value_is_null, None)
+
+ def rawrefcount_create_link_pypy(self, gcobj, pyobject):
+ ll_assert(self.rrc_enabled, "rawrefcount.init not called")
+ obj = llmemory.cast_ptr_to_adr(gcobj)
+ objint = llmemory.cast_adr_to_int(obj, "symbolic")
+ self._pyobj(pyobject).ob_pypy_link = objint
+ #
+ lst = self.rrc_p_list_young
+ if self.is_in_nursery(obj):
+ dct = self.rrc_p_dict_nurs
+ else:
+ dct = self.rrc_p_dict
+ if not self.is_young_object(obj):
+ lst = self.rrc_p_list_old
+ lst.append(pyobject)
+ dct.setitem(obj, pyobject)
+
+ def rawrefcount_create_link_pyobj(self, gcobj, pyobject):
+ ll_assert(self.rrc_enabled, "rawrefcount.init not called")
+ obj = llmemory.cast_ptr_to_adr(gcobj)
+ if self.is_young_object(obj):
+ self.rrc_o_list_young.append(pyobject)
+ else:
+ self.rrc_o_list_old.append(pyobject)
+ objint = llmemory.cast_adr_to_int(obj, "symbolic")
+ self._pyobj(pyobject).ob_pypy_link = objint
+ # there is no rrc_o_dict
+
+ def rawrefcount_from_obj(self, gcobj):
+ obj = llmemory.cast_ptr_to_adr(gcobj)
+ if self.is_in_nursery(obj):
+ dct = self.rrc_p_dict_nurs
+ else:
+ dct = self.rrc_p_dict
+ return dct.get(obj)
+
+ def rawrefcount_to_obj(self, pyobject):
+ obj = llmemory.cast_int_to_adr(self._pyobj(pyobject).ob_pypy_link)
+ return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
+
+ def rawrefcount_next_dead(self):
+ if self.rrc_dealloc_pending.non_empty():
+ return self.rrc_dealloc_pending.pop()
+ return llmemory.NULL
+
+
+ def rrc_invoke_callback(self):
+ if self.rrc_enabled and self.rrc_dealloc_pending.non_empty():
+ self.rrc_dealloc_trigger_callback()
+
+ def rrc_minor_collection_trace(self):
+ length_estimate = self.rrc_p_dict_nurs.length()
+ self.rrc_p_dict_nurs.delete()
+ self.rrc_p_dict_nurs = self.AddressDict(length_estimate)
+ self.rrc_p_list_young.foreach(self._rrc_minor_trace,
+ self.rrc_singleaddr)
+
+ def _rrc_minor_trace(self, pyobject, singleaddr):
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+ #
+ rc = self._pyobj(pyobject).ob_refcnt
+ if rc == REFCNT_FROM_PYPY or rc == REFCNT_FROM_PYPY_LIGHT:
+ pass # the corresponding object may die
+ else:
+ # force the corresponding object to be alive
+ intobj = self._pyobj(pyobject).ob_pypy_link
+ singleaddr.address[0] = llmemory.cast_int_to_adr(intobj)
+ self._trace_drag_out(singleaddr, llmemory.NULL)
+
+ def rrc_minor_collection_free(self):
+ ll_assert(self.rrc_p_dict_nurs.length() == 0, "p_dict_nurs not empty 1")
+ lst = self.rrc_p_list_young
+ while lst.non_empty():
+ self._rrc_minor_free(lst.pop(), self.rrc_p_list_old,
+ self.rrc_p_dict)
+ lst = self.rrc_o_list_young
+ no_o_dict = self.null_address_dict()
+ while lst.non_empty():
+ self._rrc_minor_free(lst.pop(), self.rrc_o_list_old,
+ no_o_dict)
+
+ def _rrc_minor_free(self, pyobject, surviving_list, surviving_dict):
+ intobj = self._pyobj(pyobject).ob_pypy_link
+ obj = llmemory.cast_int_to_adr(intobj)
+ if self.is_in_nursery(obj):
+ if self.is_forwarded(obj):
+ # Common case: survives and moves
+ obj = self.get_forwarding_address(obj)
+ intobj = llmemory.cast_adr_to_int(obj, "symbolic")
+ self._pyobj(pyobject).ob_pypy_link = intobj
+ surviving = True
+ if surviving_dict:
+ # Surviving nursery object: was originally in
+ # rrc_p_dict_nurs and now must be put into rrc_p_dict
+ surviving_dict.setitem(obj, pyobject)
+ else:
+ surviving = False
+ elif (bool(self.young_rawmalloced_objects) and
+ self.young_rawmalloced_objects.contains(obj)):
+ # young weakref to a young raw-malloced object
+ if self.header(obj).tid & GCFLAG_VISITED_RMY:
+ surviving = True # survives, but does not move
+ else:
+ surviving = False
+ if surviving_dict:
+ # Dying young large object: was in rrc_p_dict,
+ # must be deleted
+ surviving_dict.setitem(obj, llmemory.NULL)
+ else:
+ ll_assert(False, "rrc_X_list_young contains non-young obj")
+ return
+ #
+ if surviving:
+ surviving_list.append(pyobject)
+ else:
+ self._rrc_free(pyobject)
+
+ def _rrc_free(self, pyobject):
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+ #
+ rc = self._pyobj(pyobject).ob_refcnt
+ if rc >= REFCNT_FROM_PYPY_LIGHT:
+ rc -= REFCNT_FROM_PYPY_LIGHT
+ if rc == 0:
+ lltype.free(self._pyobj(pyobject), flavor='raw')
+ else:
+ # can only occur if LIGHT is used in create_link_pyobj()
+ self._pyobj(pyobject).ob_refcnt = rc
+ self._pyobj(pyobject).ob_pypy_link = 0
+ else:
+ ll_assert(rc >= REFCNT_FROM_PYPY, "refcount underflow?")
+ ll_assert(rc < int(REFCNT_FROM_PYPY_LIGHT * 0.99),
+ "refcount underflow from REFCNT_FROM_PYPY_LIGHT?")
+ rc -= REFCNT_FROM_PYPY
+ self._pyobj(pyobject).ob_refcnt = rc
+ self._pyobj(pyobject).ob_pypy_link = 0
+ if rc == 0:
+ self.rrc_dealloc_pending.append(pyobject)
+ _rrc_free._always_inline_ = True
+
+ def rrc_major_collection_trace(self):
+ self.rrc_p_list_old.foreach(self._rrc_major_trace, None)
+
+ def _rrc_major_trace(self, pyobject, ignore):
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+ #
+ rc = self._pyobj(pyobject).ob_refcnt
+ if rc == REFCNT_FROM_PYPY or rc == REFCNT_FROM_PYPY_LIGHT:
+ pass # the corresponding object may die
+ else:
+ # force the corresponding object to be alive
+ intobj = self._pyobj(pyobject).ob_pypy_link
+ obj = llmemory.cast_int_to_adr(intobj)
+ self.objects_to_trace.append(obj)
+ self.visit_all_objects()
+
+ def rrc_major_collection_free(self):
+ ll_assert(self.rrc_p_dict_nurs.length() == 0, "p_dict_nurs not empty 2")
+ length_estimate = self.rrc_p_dict.length()
+ self.rrc_p_dict.delete()
+ self.rrc_p_dict = new_p_dict = self.AddressDict(length_estimate)
+ new_p_list = self.AddressStack()
+ while self.rrc_p_list_old.non_empty():
+ self._rrc_major_free(self.rrc_p_list_old.pop(), new_p_list,
+ new_p_dict)
+ self.rrc_p_list_old.delete()
+ self.rrc_p_list_old = new_p_list
+ #
+ new_o_list = self.AddressStack()
+ no_o_dict = self.null_address_dict()
+ while self.rrc_o_list_old.non_empty():
+ self._rrc_major_free(self.rrc_o_list_old.pop(), new_o_list,
+ no_o_dict)
+ self.rrc_o_list_old.delete()
+ self.rrc_o_list_old = new_o_list
+
+ def _rrc_major_free(self, pyobject, surviving_list, surviving_dict):
+ # The pyobject survives if the corresponding obj survives.
+ # This is true if the obj has one of the following two flags:
+ # * GCFLAG_VISITED: was seen during tracing
+ # * GCFLAG_NO_HEAP_PTRS: immortal object never traced (so far)
+ intobj = self._pyobj(pyobject).ob_pypy_link
+ obj = llmemory.cast_int_to_adr(intobj)
+ if self.header(obj).tid & (GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS):
+ surviving_list.append(pyobject)
+ if surviving_dict:
+ surviving_dict.insertclean(obj, pyobject)
+ else:
+ self._rrc_free(pyobject)
diff --git a/rpython/memory/gc/test/test_rawrefcount.py b/rpython/memory/gc/test/test_rawrefcount.py
new file mode 100644
index 0000000000..aca61015b8
--- /dev/null
+++ b/rpython/memory/gc/test/test_rawrefcount.py
@@ -0,0 +1,282 @@
+import py
+from rpython.rtyper.lltypesystem import lltype, llmemory
+from rpython.memory.gc.incminimark import IncrementalMiniMarkGC
+from rpython.memory.gc.test.test_direct import BaseDirectGCTest
+from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
+from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+
+PYOBJ_HDR = IncrementalMiniMarkGC.PYOBJ_HDR
+PYOBJ_HDR_PTR = IncrementalMiniMarkGC.PYOBJ_HDR_PTR
+
+S = lltype.GcForwardReference()
+S.become(lltype.GcStruct('S',
+ ('x', lltype.Signed),
+ ('prev', lltype.Ptr(S)),
+ ('next', lltype.Ptr(S))))
+
+
+class TestRawRefCount(BaseDirectGCTest):
+ GCClass = IncrementalMiniMarkGC
+
+ def _collect(self, major, expected_trigger=0):
+ if major:
+ self.gc.collect()
+ else:
+ self.gc.minor_collection()
+ count1 = len(self.trigger)
+ self.gc.rrc_invoke_callback()
+ count2 = len(self.trigger)
+ assert count2 - count1 == expected_trigger
+
+ def _rawrefcount_pair(self, intval, is_light=False, is_pyobj=False,
+ create_old=False, create_immortal=False):
+ if is_light:
+ rc = REFCNT_FROM_PYPY_LIGHT
+ else:
+ rc = REFCNT_FROM_PYPY
+ self.trigger = []
+ self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+ #
+ if create_immortal:
+ p1 = lltype.malloc(S, immortal=True)
+ else:
+ p1 = self.malloc(S)
+ p1.x = intval
+ if create_immortal:
+ self.consider_constant(p1)
+ elif create_old:
+ self.stackroots.append(p1)
+ self._collect(major=False)
+ p1 = self.stackroots.pop()
+ p1ref = lltype.cast_opaque_ptr(llmemory.GCREF, p1)
+ r1 = lltype.malloc(PYOBJ_HDR, flavor='raw', immortal=create_immortal)
+ r1.ob_refcnt = rc
+ r1.ob_pypy_link = 0
+ r1addr = llmemory.cast_ptr_to_adr(r1)
+ if is_pyobj:
+ assert not is_light
+ self.gc.rawrefcount_create_link_pyobj(p1ref, r1addr)
+ else:
+ self.gc.rawrefcount_create_link_pypy(p1ref, r1addr)
+ assert r1.ob_refcnt == rc
+ assert r1.ob_pypy_link != 0
+
+ def check_alive(extra_refcount):
+ assert r1.ob_refcnt == rc + extra_refcount
+ assert r1.ob_pypy_link != 0
+ p1ref = self.gc.rawrefcount_to_obj(r1addr)
+ p1 = lltype.cast_opaque_ptr(lltype.Ptr(S), p1ref)
+ assert p1.x == intval
+ if not is_pyobj:
+ assert self.gc.rawrefcount_from_obj(p1ref) == r1addr
+ else:
+ assert self.gc.rawrefcount_from_obj(p1ref) == llmemory.NULL
+ return p1
+ return p1, p1ref, r1, r1addr, check_alive
+
+ def test_rawrefcount_objects_basic(self, old=False):
+ p1, p1ref, r1, r1addr, check_alive = (
+ self._rawrefcount_pair(42, is_light=True, create_old=old))
+ p2 = self.malloc(S)
+ p2.x = 84
+ p2ref = lltype.cast_opaque_ptr(llmemory.GCREF, p2)
+ r2 = lltype.malloc(PYOBJ_HDR, flavor='raw')
+ r2.ob_refcnt = 1
+ r2.ob_pypy_link = 0
+ r2addr = llmemory.cast_ptr_to_adr(r2)
+ # p2 and r2 are not linked
+ assert r1.ob_pypy_link != 0
+ assert r2.ob_pypy_link == 0
+ assert self.gc.rawrefcount_from_obj(p1ref) == r1addr
+ assert self.gc.rawrefcount_from_obj(p2ref) == llmemory.NULL
+ assert self.gc.rawrefcount_to_obj(r1addr) == p1ref
+ assert self.gc.rawrefcount_to_obj(r2addr) == lltype.nullptr(
+ llmemory.GCREF.TO)
+ lltype.free(r1, flavor='raw')
+ lltype.free(r2, flavor='raw')
+
+ def test_rawrefcount_objects_collection_survives_from_raw(self, old=False):
+ p1, p1ref, r1, r1addr, check_alive = (
+ self._rawrefcount_pair(42, is_light=True, create_old=old))
+ check_alive(0)
+ r1.ob_refcnt += 1
+ self._collect(major=False)
+ check_alive(+1)
+ self._collect(major=True)
+ check_alive(+1)
+ r1.ob_refcnt -= 1
+ self._collect(major=False)
+ p1 = check_alive(0)
+ self._collect(major=True)
+ py.test.raises(RuntimeError, "r1.ob_refcnt") # dead
+ py.test.raises(RuntimeError, "p1.x") # dead
+ self.gc.check_no_more_rawrefcount_state()
+ assert self.trigger == []
+ assert self.gc.rawrefcount_next_dead() == llmemory.NULL
+
+ def test_rawrefcount_dies_quickly(self, old=False):
+ p1, p1ref, r1, r1addr, check_alive = (
+ self._rawrefcount_pair(42, is_light=True, create_old=old))
+ check_alive(0)
+ self._collect(major=False)
+ if old:
+ check_alive(0)
+ self._collect(major=True)
+ py.test.raises(RuntimeError, "r1.ob_refcnt") # dead
+ py.test.raises(RuntimeError, "p1.x") # dead
+ self.gc.check_no_more_rawrefcount_state()
+
+ def test_rawrefcount_objects_collection_survives_from_obj(self, old=False):
+ p1, p1ref, r1, r1addr, check_alive = (
+ self._rawrefcount_pair(42, is_light=True, create_old=old))
+ check_alive(0)
+ self.stackroots.append(p1)
+ self._collect(major=False)
+ check_alive(0)
+ self._collect(major=True)
+ check_alive(0)
+ p1 = self.stackroots.pop()
+ self._collect(major=False)
+ check_alive(0)
+ assert p1.x == 42
+ self._collect(major=True)
+ py.test.raises(RuntimeError, "r1.ob_refcnt") # dead
+ py.test.raises(RuntimeError, "p1.x") # dead
+ self.gc.check_no_more_rawrefcount_state()
+
+ def test_rawrefcount_objects_basic_old(self):
+ self.test_rawrefcount_objects_basic(old=True)
+ def test_rawrefcount_objects_collection_survives_from_raw_old(self):
+ self.test_rawrefcount_objects_collection_survives_from_raw(old=True)
+ def test_rawrefcount_dies_quickly_old(self):
+ self.test_rawrefcount_dies_quickly(old=True)
+ def test_rawrefcount_objects_collection_survives_from_obj_old(self):
+ self.test_rawrefcount_objects_collection_survives_from_obj(old=True)
+
+ def test_pypy_nonlight_survives_from_raw(self, old=False):
+ p1, p1ref, r1, r1addr, check_alive = (
+ self._rawrefcount_pair(42, is_light=False, create_old=old))
+ check_alive(0)
+ r1.ob_refcnt += 1
+ self._collect(major=False)
+ check_alive(+1)
+ self._collect(major=True)
+ check_alive(+1)
+ r1.ob_refcnt -= 1
+ self._collect(major=False)
+ p1 = check_alive(0)
+ self._collect(major=True, expected_trigger=1)
+ py.test.raises(RuntimeError, "p1.x") # dead
+ assert r1.ob_refcnt == 0
+ assert r1.ob_pypy_link == 0
+ assert self.gc.rawrefcount_next_dead() == r1addr
+ assert self.gc.rawrefcount_next_dead() == llmemory.NULL
+ assert self.gc.rawrefcount_next_dead() == llmemory.NULL
+ self.gc.check_no_more_rawrefcount_state()
+ lltype.free(r1, flavor='raw')
+
+ def test_pypy_nonlight_survives_from_obj(self, old=False):
+ p1, p1ref, r1, r1addr, check_alive = (
+ self._rawrefcount_pair(42, is_light=False, create_old=old))
+ check_alive(0)
+ self.stackroots.append(p1)
+ self._collect(major=False)
+ check_alive(0)
+ self._collect(major=True)
+ check_alive(0)
+ p1 = self.stackroots.pop()
+ self._collect(major=False)
+ check_alive(0)
+ assert p1.x == 42
+ self._collect(major=True, expected_trigger=1)
+ py.test.raises(RuntimeError, "p1.x") # dead
+ assert r1.ob_refcnt == 0
+ assert r1.ob_pypy_link == 0
+ assert self.gc.rawrefcount_next_dead() == r1addr
+ self.gc.check_no_more_rawrefcount_state()
+ lltype.free(r1, flavor='raw')
+
+ def test_pypy_nonlight_dies_quickly(self, old=False):
+ p1, p1ref, r1, r1addr, check_alive = (
+ self._rawrefcount_pair(42, is_light=False, create_old=old))
+ check_alive(0)
+ if old:
+ self._collect(major=False)
+ check_alive(0)
+ self._collect(major=True, expected_trigger=1)
+ else:
+ self._collect(major=False, expected_trigger=1)
+ py.test.raises(RuntimeError, "p1.x") # dead
+ assert r1.ob_refcnt == 0
+ assert r1.ob_pypy_link == 0
+ assert self.gc.rawrefcount_next_dead() == r1addr
+ self.gc.check_no_more_rawrefcount_state()
+ lltype.free(r1, flavor='raw')
+
+ def test_pypy_nonlight_survives_from_raw_old(self):
+ self.test_pypy_nonlight_survives_from_raw(old=True)
+ def test_pypy_nonlight_survives_from_obj_old(self):
+ self.test_pypy_nonlight_survives_from_obj(old=True)
+ def test_pypy_nonlight_dies_quickly_old(self):
+ self.test_pypy_nonlight_dies_quickly(old=True)
+
+ def test_pyobject_pypy_link_dies_on_minor_collection(self):
+ p1, p1ref, r1, r1addr, check_alive = (
+ self._rawrefcount_pair(42, is_pyobj=True))
+ check_alive(0)
+ r1.ob_refcnt += 1 # the pyobject is kept alive
+ self._collect(major=False)
+ assert r1.ob_refcnt == 1 # refcnt dropped to 1
+ assert r1.ob_pypy_link == 0 # detached
+ self.gc.check_no_more_rawrefcount_state()
+ lltype.free(r1, flavor='raw')
+
+ def test_pyobject_dies(self, old=False):
+ p1, p1ref, r1, r1addr, check_alive = (
+ self._rawrefcount_pair(42, is_pyobj=True, create_old=old))
+ check_alive(0)
+ if old:
+ self._collect(major=False)
+ check_alive(0)
+ self._collect(major=True, expected_trigger=1)
+ else:
+ self._collect(major=False, expected_trigger=1)
+ assert r1.ob_refcnt == 0 # refcnt dropped to 0
+ assert r1.ob_pypy_link == 0 # detached
+ assert self.gc.rawrefcount_next_dead() == r1addr
+ self.gc.check_no_more_rawrefcount_state()
+ lltype.free(r1, flavor='raw')
+
+ def test_pyobject_survives_from_obj(self, old=False):
+ p1, p1ref, r1, r1addr, check_alive = (
+ self._rawrefcount_pair(42, is_pyobj=True, create_old=old))
+ check_alive(0)
+ self.stackroots.append(p1)
+ self._collect(major=False)
+ check_alive(0)
+ self._collect(major=True)
+ check_alive(0)
+ p1 = self.stackroots.pop()
+ self._collect(major=False)
+ check_alive(0)
+ assert p1.x == 42
+ assert self.trigger == []
+ self._collect(major=True, expected_trigger=1)
+ py.test.raises(RuntimeError, "p1.x") # dead
+ assert r1.ob_refcnt == 0
+ assert r1.ob_pypy_link == 0
+ assert self.gc.rawrefcount_next_dead() == r1addr
+ self.gc.check_no_more_rawrefcount_state()
+ lltype.free(r1, flavor='raw')
+
+ def test_pyobject_dies_old(self):
+ self.test_pyobject_dies(old=True)
+ def test_pyobject_survives_from_obj_old(self):
+ self.test_pyobject_survives_from_obj(old=True)
+
+ def test_pyobject_attached_to_prebuilt_obj(self):
+ p1, p1ref, r1, r1addr, check_alive = (
+ self._rawrefcount_pair(42, create_immortal=True))
+ check_alive(0)
+ self._collect(major=True)
+ check_alive(0)
diff --git a/rpython/memory/gctransform/boehm.py b/rpython/memory/gctransform/boehm.py
index 5144714329..25f72d32f6 100644
--- a/rpython/memory/gctransform/boehm.py
+++ b/rpython/memory/gctransform/boehm.py
@@ -156,9 +156,9 @@ class BoehmGCTransformer(GCTransformer):
resulttype = lltype.Signed)
hop.genop('int_invert', [v_int], resultvar=hop.spaceop.result)
- def gcheader_initdata(self, defnode):
+ def gcheader_initdata(self, obj):
hdr = lltype.malloc(self.HDR, immortal=True)
- hdr.hash = lltype.identityhash_nocache(defnode.obj._as_ptr())
+ hdr.hash = lltype.identityhash_nocache(obj._as_ptr())
return hdr._obj
diff --git a/rpython/memory/gctransform/framework.py b/rpython/memory/gctransform/framework.py
index 140e3cab11..8a7c0f06ad 100644
--- a/rpython/memory/gctransform/framework.py
+++ b/rpython/memory/gctransform/framework.py
@@ -153,6 +153,7 @@ class BaseFrameworkGCTransformer(GCTransformer):
else:
# for regular translation: pick the GC from the config
GCClass, GC_PARAMS = choose_gc_from_config(translator.config)
+ self.GCClass = GCClass
if hasattr(translator, '_jit2gc'):
self.layoutbuilder = translator._jit2gc['layoutbuilder']
@@ -482,6 +483,29 @@ class BaseFrameworkGCTransformer(GCTransformer):
annmodel.SomeInteger(nonneg=True)],
annmodel.s_None)
+ if hasattr(GCClass, 'rawrefcount_init'):
+ self.rawrefcount_init_ptr = getfn(
+ GCClass.rawrefcount_init,
+ [s_gc, SomePtr(GCClass.RAWREFCOUNT_DEALLOC_TRIGGER)],
+ annmodel.s_None)
+ self.rawrefcount_create_link_pypy_ptr = getfn(
+ GCClass.rawrefcount_create_link_pypy,
+ [s_gc, s_gcref, SomeAddress()],
+ annmodel.s_None)
+ self.rawrefcount_create_link_pyobj_ptr = getfn(
+ GCClass.rawrefcount_create_link_pyobj,
+ [s_gc, s_gcref, SomeAddress()],
+ annmodel.s_None)
+ self.rawrefcount_from_obj_ptr = getfn(
+ GCClass.rawrefcount_from_obj, [s_gc, s_gcref], SomeAddress(),
+ inline = True)
+ self.rawrefcount_to_obj_ptr = getfn(
+ GCClass.rawrefcount_to_obj, [s_gc, SomeAddress()], s_gcref,
+ inline = True)
+ self.rawrefcount_next_dead_ptr = getfn(
+ GCClass.rawrefcount_next_dead, [s_gc], SomeAddress(),
+ inline = True)
+
if GCClass.can_usually_pin_objects:
self.pin_ptr = getfn(GCClass.pin,
[s_gc, SomeAddress()],
@@ -1227,6 +1251,50 @@ class BaseFrameworkGCTransformer(GCTransformer):
resultvar=hop.spaceop.result)
self.pop_roots(hop, livevars)
+ def gct_gc_rawrefcount_init(self, hop):
+ [v_fnptr] = hop.spaceop.args
+ assert v_fnptr.concretetype == self.GCClass.RAWREFCOUNT_DEALLOC_TRIGGER
+ hop.genop("direct_call",
+ [self.rawrefcount_init_ptr, self.c_const_gc, v_fnptr])
+
+ def gct_gc_rawrefcount_create_link_pypy(self, hop):
+ [v_gcobj, v_pyobject] = hop.spaceop.args
+ assert v_gcobj.concretetype == llmemory.GCREF
+ assert v_pyobject.concretetype == llmemory.Address
+ hop.genop("direct_call",
+ [self.rawrefcount_create_link_pypy_ptr, self.c_const_gc,
+ v_gcobj, v_pyobject])
+
+ def gct_gc_rawrefcount_create_link_pyobj(self, hop):
+ [v_gcobj, v_pyobject] = hop.spaceop.args
+ assert v_gcobj.concretetype == llmemory.GCREF
+ assert v_pyobject.concretetype == llmemory.Address
+ hop.genop("direct_call",
+ [self.rawrefcount_create_link_pyobj_ptr, self.c_const_gc,
+ v_gcobj, v_pyobject])
+
+ def gct_gc_rawrefcount_from_obj(self, hop):
+ [v_gcobj] = hop.spaceop.args
+ assert v_gcobj.concretetype == llmemory.GCREF
+ assert hop.spaceop.result.concretetype == llmemory.Address
+ hop.genop("direct_call",
+ [self.rawrefcount_from_obj_ptr, self.c_const_gc, v_gcobj],
+ resultvar=hop.spaceop.result)
+
+ def gct_gc_rawrefcount_to_obj(self, hop):
+ [v_pyobject] = hop.spaceop.args
+ assert v_pyobject.concretetype == llmemory.Address
+ assert hop.spaceop.result.concretetype == llmemory.GCREF
+ hop.genop("direct_call",
+ [self.rawrefcount_to_obj_ptr, self.c_const_gc, v_pyobject],
+ resultvar=hop.spaceop.result)
+
+ def gct_gc_rawrefcount_next_dead(self, hop):
+ assert hop.spaceop.result.concretetype == llmemory.Address
+ hop.genop("direct_call",
+ [self.rawrefcount_next_dead_ptr, self.c_const_gc],
+ resultvar=hop.spaceop.result)
+
def _set_into_gc_array_part(self, op):
if op.opname == 'setarrayitem':
return op.args[1]
@@ -1411,8 +1479,8 @@ class BaseFrameworkGCTransformer(GCTransformer):
resulttype=llmemory.Address)
llops.genop('raw_memclear', [v_adr, v_totalsize])
- def gcheader_initdata(self, defnode):
- o = lltype.top_container(defnode.obj)
+ def gcheader_initdata(self, obj):
+ o = lltype.top_container(obj)
needs_hash = self.get_prebuilt_hash(o) is not None
hdr = self.gc_header_for(o, needs_hash)
return hdr._obj
diff --git a/rpython/memory/gctransform/refcounting.py b/rpython/memory/gctransform/refcounting.py
index ae6f64b4ee..a496db590c 100644
--- a/rpython/memory/gctransform/refcounting.py
+++ b/rpython/memory/gctransform/refcounting.py
@@ -286,6 +286,6 @@ def ll_deallocator(addr):
hop.genop("direct_call", [self.identityhash_ptr, v_adr],
resultvar=hop.spaceop.result)
- def gcheader_initdata(self, defnode):
- top = lltype.top_container(defnode.obj)
+ def gcheader_initdata(self, obj):
+ top = lltype.top_container(obj)
return self.gcheaderbuilder.header_of_object(top)._obj
diff --git a/rpython/memory/gctransform/test/test_transform.py b/rpython/memory/gctransform/test/test_transform.py
index 03343618db..9cd4382c5d 100644
--- a/rpython/memory/gctransform/test/test_transform.py
+++ b/rpython/memory/gctransform/test/test_transform.py
@@ -5,6 +5,7 @@ from rpython.translator.translator import TranslationContext, graphof
from rpython.translator.exceptiontransform import ExceptionTransformer
from rpython.rtyper.lltypesystem import lltype
from rpython.conftest import option
+from rpython.rtyper.rtyper import llinterp_backend
class LLInterpedTranformerTests:
@@ -131,8 +132,10 @@ def checkblock(block, is_borrowed, is_start_block):
def rtype(func, inputtypes, specialize=True):
t = TranslationContext()
t.buildannotator().build_types(func, inputtypes)
+ rtyper = t.buildrtyper()
+ rtyper.backend = llinterp_backend
if specialize:
- t.buildrtyper().specialize()
+ rtyper.specialize()
if option.view:
t.view()
return t
diff --git a/rpython/memory/test/test_transformed_gc.py b/rpython/memory/test/test_transformed_gc.py
index 81b77451a2..8feeb9198b 100644
--- a/rpython/memory/test/test_transformed_gc.py
+++ b/rpython/memory/test/test_transformed_gc.py
@@ -14,6 +14,7 @@ from rpython.rlib import rgc
from rpython.conftest import option
from rpython.rlib.rstring import StringBuilder
from rpython.rlib.rarithmetic import LONG_BIT
+from rpython.rtyper.rtyper import llinterp_backend
WORD = LONG_BIT // 8
@@ -29,9 +30,11 @@ def rtype(func, inputtypes, specialize=True, gcname='ref',
t.config.set(**extraconfigopts)
ann = t.buildannotator()
ann.build_types(func, inputtypes)
+ rtyper = t.buildrtyper()
+ rtyper.backend = llinterp_backend
if specialize:
- t.buildrtyper().specialize()
+ rtyper.specialize()
if backendopt:
from rpython.translator.backendopt.all import backend_optimizations
backend_optimizations(t)
diff --git a/rpython/rtyper/module/support.py b/rpython/rlib/_os_support.py
index fac520788b..0365efdb08 100644
--- a/rpython/rtyper/module/support.py
+++ b/rpython/rlib/_os_support.py
@@ -1,53 +1,20 @@
import sys
-from rpython.annotator import model as annmodel
-from rpython.rtyper.lltypesystem import lltype, rffi
-from rpython.rlib.objectmodel import specialize
+from rpython.annotator.model import s_Str0, s_Unicode0
from rpython.rlib import rstring
+from rpython.rlib.objectmodel import specialize
+from rpython.rtyper.lltypesystem import rffi
+
+_CYGWIN = sys.platform == 'cygwin'
_WIN32 = sys.platform.startswith('win')
UNDERSCORE_ON_WIN32 = '_' if _WIN32 else ''
-
-# utility conversion functions
-class LLSupport:
- _mixin_ = True
-
- def to_rstr(s):
- from rpython.rtyper.lltypesystem.rstr import STR, mallocstr
- if s is None:
- return lltype.nullptr(STR)
- p = mallocstr(len(s))
- for i in range(len(s)):
- p.chars[i] = s[i]
- return p
- to_rstr = staticmethod(to_rstr)
-
- def to_runicode(s):
- from rpython.rtyper.lltypesystem.rstr import UNICODE, mallocunicode
- if s is None:
- return lltype.nullptr(UNICODE)
- p = mallocunicode(len(s))
- for i in range(len(s)):
- p.chars[i] = s[i]
- return p
- to_runicode = staticmethod(to_runicode)
-
- def from_rstr(rs):
- if not rs: # null pointer
- return None
- else:
- return ''.join([rs.chars[i] for i in range(len(rs.chars))])
- from_rstr = staticmethod(from_rstr)
-
- def from_rstr_nonnull(rs):
- assert rs
- return ''.join([rs.chars[i] for i in range(len(rs.chars))])
- from_rstr_nonnull = staticmethod(from_rstr_nonnull)
+_MACRO_ON_POSIX = True if not _WIN32 else None
-class StringTraits:
+class StringTraits(object):
str = str
- str0 = annmodel.s_Str0
+ str0 = s_Str0
CHAR = rffi.CHAR
CCHARP = rffi.CCHARP
charp2str = staticmethod(rffi.charp2str)
@@ -58,14 +25,6 @@ class StringTraits:
scoped_alloc_buffer = staticmethod(rffi.scoped_alloc_buffer)
@staticmethod
- def posix_function_name(name):
- return UNDERSCORE_ON_WIN32 + name
-
- @staticmethod
- def ll_os_name(name):
- return 'll_os.ll_os_' + name
-
- @staticmethod
@specialize.argtype(0)
def as_str(path):
assert path is not None
@@ -77,7 +36,7 @@ class StringTraits:
# We implement python2 behavior: silently convert to ascii.
return path.encode('ascii')
else:
- return path.as_bytes()
+ return path.as_bytes()
@staticmethod
@specialize.argtype(0)
@@ -86,9 +45,10 @@ class StringTraits:
rstring.check_str0(res)
return res
-class UnicodeTraits:
+
+class UnicodeTraits(object):
str = unicode
- str0 = annmodel.s_Unicode0
+ str0 = s_Unicode0
CHAR = rffi.WCHAR_T
CCHARP = rffi.CWCHARP
charp2str = staticmethod(rffi.wcharp2unicode)
@@ -99,15 +59,6 @@ class UnicodeTraits:
scoped_alloc_buffer = staticmethod(rffi.scoped_alloc_unicodebuffer)
@staticmethod
- def posix_function_name(name):
- return UNDERSCORE_ON_WIN32 + 'w' + name
-
- @staticmethod
- @specialize.argtype(0)
- def ll_os_name(name):
- return 'll_os.ll_os_w' + name
-
- @staticmethod
@specialize.argtype(0)
def as_str(path):
assert path is not None
@@ -115,7 +66,7 @@ class UnicodeTraits:
return path
else:
return path.as_unicode()
-
+
@staticmethod
@specialize.argtype(0)
def as_str0(path):
@@ -123,17 +74,36 @@ class UnicodeTraits:
rstring.check_str0(res)
return res
-def ll_strcpy(dst_s, src_s, n):
- dstchars = dst_s.chars
- srcchars = src_s.chars
- i = 0
- while i < n:
- dstchars[i] = srcchars[i]
- i += 1
-
-def _ll_strfill(dst_s, srcchars, n):
- dstchars = dst_s.chars
- i = 0
- while i < n:
- dstchars[i] = srcchars[i]
- i += 1
+
+string_traits = StringTraits()
+unicode_traits = UnicodeTraits()
+
+
+# Returns True when the unicode function should be called:
+# - on Windows
+# - if the path is Unicode.
+if _WIN32:
+ @specialize.argtype(0)
+ def _prefer_unicode(path):
+ assert path is not None
+ if isinstance(path, str):
+ return False
+ elif isinstance(path, unicode):
+ return True
+ else:
+ return path.is_unicode
+
+ @specialize.argtype(0)
+ def _preferred_traits(path):
+ if _prefer_unicode(path):
+ return unicode_traits
+ else:
+ return string_traits
+else:
+ @specialize.argtype(0)
+ def _prefer_unicode(path):
+ return False
+
+ @specialize.argtype(0)
+ def _preferred_traits(path):
+ return string_traits
diff --git a/rpython/rlib/exports.py b/rpython/rlib/exports.py
index 1ae9ae4778..391678e1c4 100644
--- a/rpython/rlib/exports.py
+++ b/rpython/rlib/exports.py
@@ -1,5 +1,7 @@
from rpython.rtyper.lltypesystem.lltype import typeOf, ContainerType
+# XXX kill me
+
def export_struct(name, struct):
assert name not in EXPORTS_names, "Duplicate export " + name
assert isinstance(typeOf(struct), ContainerType)
diff --git a/rpython/rlib/jit.py b/rpython/rlib/jit.py
index d17e919e62..7017795baf 100644
--- a/rpython/rlib/jit.py
+++ b/rpython/rlib/jit.py
@@ -284,7 +284,7 @@ isvirtual._annspecialcase_ = "specialize:call_location"
def loop_unrolling_heuristic(lst, size, cutoff=2):
""" In which cases iterating over items of lst can be unrolled
"""
- return isvirtual(lst) or (isconstant(size) and size <= cutoff)
+ return size == 0 or isvirtual(lst) or (isconstant(size) and size <= cutoff)
class Entry(ExtRegistryEntry):
_about_ = hint
@@ -1168,6 +1168,24 @@ class ConditionalCallEntry(ExtRegistryEntry):
hop.exception_is_here()
return hop.genop('jit_conditional_call', args_v)
+def enter_portal_frame(unique_id):
+ """call this when starting to interpret a function. calling this is not
+ necessary for almost all interpreters. The only exception is stackless
+ interpreters where the portal never calls itself.
+ """
+ from rpython.rtyper.lltypesystem import lltype
+ from rpython.rtyper.lltypesystem.lloperation import llop
+ llop.jit_enter_portal_frame(lltype.Void, unique_id)
+
+def leave_portal_frame():
+ """call this after the end of executing a function. calling this is not
+ necessary for almost all interpreters. The only exception is stackless
+ interpreters where the portal never calls itself.
+ """
+ from rpython.rtyper.lltypesystem import lltype
+ from rpython.rtyper.lltypesystem.lloperation import llop
+ llop.jit_leave_portal_frame(lltype.Void)
+
class Counters(object):
counters="""
TRACING
diff --git a/rpython/rlib/objectmodel.py b/rpython/rlib/objectmodel.py
index 464afa1ee5..aa2a2d78c0 100644
--- a/rpython/rlib/objectmodel.py
+++ b/rpython/rlib/objectmodel.py
@@ -275,8 +275,6 @@ class CDefinedIntSymbolic(Symbolic):
return lltype.Signed
malloc_zero_filled = CDefinedIntSymbolic('MALLOC_ZERO_FILLED', default=0)
-running_on_llinterp = CDefinedIntSymbolic('RUNNING_ON_LLINTERP', default=1)
-# running_on_llinterp is meant to have the value 0 in all backends
# ____________________________________________________________
diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py
new file mode 100644
index 0000000000..cb19b18c47
--- /dev/null
+++ b/rpython/rlib/rawrefcount.py
@@ -0,0 +1,265 @@
+#
+# See documentation in pypy/doc/discussion/rawrefcount.rst
+#
+# This is meant for pypy's cpyext module, but is a generally
+# useful interface over our GC. XXX "pypy" should be removed here
+#
+import sys, weakref
+from rpython.rtyper.lltypesystem import lltype, llmemory
+from rpython.rlib.objectmodel import we_are_translated, specialize
+from rpython.rtyper.extregistry import ExtRegistryEntry
+from rpython.rlib import rgc
+
+
+REFCNT_FROM_PYPY = sys.maxint // 4 + 1
+REFCNT_FROM_PYPY_LIGHT = REFCNT_FROM_PYPY + (sys.maxint // 2 + 1)
+
+RAWREFCOUNT_DEALLOC_TRIGGER = lltype.Ptr(lltype.FuncType([], lltype.Void))
+
+
+def _build_pypy_link(p):
+ res = len(_adr2pypy)
+ _adr2pypy.append(p)
+ return res
+
+
+def init(dealloc_trigger_callback=None):
+ """NOT_RPYTHON: set up rawrefcount with the GC. This is only used
+ for tests; it should not be called at all during translation.
+ """
+ global _p_list, _o_list, _adr2pypy, _pypy2ob
+ global _d_list, _dealloc_trigger_callback
+ _p_list = []
+ _o_list = []
+ _adr2pypy = [None]
+ _pypy2ob = {}
+ _d_list = []
+ _dealloc_trigger_callback = dealloc_trigger_callback
+
+def create_link_pypy(p, ob):
+ "NOT_RPYTHON: a link where the PyPy object contains some or all the data"
+ #print 'create_link_pypy\n\t%s\n\t%s' % (p, ob)
+ assert p not in _pypy2ob
+ #assert not ob.c_ob_pypy_link
+ ob.c_ob_pypy_link = _build_pypy_link(p)
+ _pypy2ob[p] = ob
+ _p_list.append(ob)
+
+def create_link_pyobj(p, ob):
+ """NOT_RPYTHON: a link where the PyObject contains all the data.
+ from_obj() will not work on this 'p'."""
+ #print 'create_link_pyobj\n\t%s\n\t%s' % (p, ob)
+ assert p not in _pypy2ob
+ #assert not ob.c_ob_pypy_link
+ ob.c_ob_pypy_link = _build_pypy_link(p)
+ _o_list.append(ob)
+
+def from_obj(OB_PTR_TYPE, p):
+ "NOT_RPYTHON"
+ ob = _pypy2ob.get(p)
+ if ob is None:
+ return lltype.nullptr(OB_PTR_TYPE.TO)
+ assert lltype.typeOf(ob) == OB_PTR_TYPE
+ return ob
+
+def to_obj(Class, ob):
+ "NOT_RPYTHON"
+ link = ob.c_ob_pypy_link
+ if link == 0:
+ return None
+ p = _adr2pypy[link]
+ assert isinstance(p, Class)
+ return p
+
+def next_dead(OB_PTR_TYPE):
+ if len(_d_list) == 0:
+ return lltype.nullptr(OB_PTR_TYPE.TO)
+ ob = _d_list.pop()
+ assert lltype.typeOf(ob) == OB_PTR_TYPE
+ return ob
+
+def _collect(track_allocation=True):
+ """NOT_RPYTHON: for tests only. Emulates a GC collection.
+ Will invoke dealloc_trigger_callback() once if there are objects
+ whose _Py_Dealloc() should be called.
+ """
+ def detach(ob, wr_list):
+ assert ob.c_ob_refcnt >= REFCNT_FROM_PYPY
+ assert ob.c_ob_pypy_link
+ p = _adr2pypy[ob.c_ob_pypy_link]
+ assert p is not None
+ _adr2pypy[ob.c_ob_pypy_link] = None
+ wr_list.append((ob, weakref.ref(p)))
+ return p
+
+ global _p_list, _o_list
+ wr_p_list = []
+ new_p_list = []
+ for ob in reversed(_p_list):
+ if ob.c_ob_refcnt not in (REFCNT_FROM_PYPY, REFCNT_FROM_PYPY_LIGHT):
+ new_p_list.append(ob)
+ else:
+ p = detach(ob, wr_p_list)
+ del _pypy2ob[p]
+ del p
+ ob = None
+ _p_list = Ellipsis
+
+ wr_o_list = []
+ for ob in reversed(_o_list):
+ detach(ob, wr_o_list)
+ _o_list = Ellipsis
+
+ rgc.collect() # forces the cycles to be resolved and the weakrefs to die
+ rgc.collect()
+ rgc.collect()
+
+ def attach(ob, wr, final_list):
+ assert ob.c_ob_refcnt >= REFCNT_FROM_PYPY
+ p = wr()
+ if p is not None:
+ assert ob.c_ob_pypy_link
+ _adr2pypy[ob.c_ob_pypy_link] = p
+ final_list.append(ob)
+ return p
+ else:
+ ob.c_ob_pypy_link = 0
+ if ob.c_ob_refcnt >= REFCNT_FROM_PYPY_LIGHT:
+ ob.c_ob_refcnt -= REFCNT_FROM_PYPY_LIGHT
+ ob.c_ob_pypy_link = 0
+ if ob.c_ob_refcnt == 0:
+ lltype.free(ob, flavor='raw',
+ track_allocation=track_allocation)
+ else:
+ assert ob.c_ob_refcnt >= REFCNT_FROM_PYPY
+ assert ob.c_ob_refcnt < int(REFCNT_FROM_PYPY_LIGHT * 0.99)
+ ob.c_ob_refcnt -= REFCNT_FROM_PYPY
+ ob.c_ob_pypy_link = 0
+ if ob.c_ob_refcnt == 0:
+ _d_list.append(ob)
+ return None
+
+ _p_list = new_p_list
+ for ob, wr in wr_p_list:
+ p = attach(ob, wr, _p_list)
+ if p is not None:
+ _pypy2ob[p] = ob
+ _o_list = []
+ for ob, wr in wr_o_list:
+ attach(ob, wr, _o_list)
+
+ if _d_list:
+ res = _dealloc_trigger_callback()
+ if res == "RETRY":
+ _collect(track_allocation=track_allocation)
+
+_keepalive_forever = set()
+def _dont_free_any_more():
+ "Make sure that any object still referenced won't be freed any more."
+ for ob in _p_list + _o_list:
+ _keepalive_forever.add(to_obj(object, ob))
+ del _d_list[:]
+
+# ____________________________________________________________
+
+
+def _unspec_p(hop, v_p):
+ assert isinstance(v_p.concretetype, lltype.Ptr)
+ assert v_p.concretetype.TO._gckind == 'gc'
+ return hop.genop('cast_opaque_ptr', [v_p], resulttype=llmemory.GCREF)
+
+def _unspec_ob(hop, v_ob):
+ assert isinstance(v_ob.concretetype, lltype.Ptr)
+ assert v_ob.concretetype.TO._gckind == 'raw'
+ return hop.genop('cast_ptr_to_adr', [v_ob], resulttype=llmemory.Address)
+
+def _spec_p(hop, v_p):
+ assert v_p.concretetype == llmemory.GCREF
+ return hop.genop('cast_opaque_ptr', [v_p],
+ resulttype=hop.r_result.lowleveltype)
+
+def _spec_ob(hop, v_ob):
+ assert v_ob.concretetype == llmemory.Address
+ return hop.genop('cast_adr_to_ptr', [v_ob],
+ resulttype=hop.r_result.lowleveltype)
+
+
+class Entry(ExtRegistryEntry):
+ _about_ = init
+
+ def compute_result_annotation(self, s_dealloc_callback):
+ from rpython.rtyper.llannotation import SomePtr
+ assert isinstance(s_dealloc_callback, SomePtr) # ll-ptr-to-function
+
+ def specialize_call(self, hop):
+ hop.exception_cannot_occur()
+ [v_dealloc_callback] = hop.inputargs(hop.args_r[0])
+ hop.genop('gc_rawrefcount_init', [v_dealloc_callback])
+
+
+class Entry(ExtRegistryEntry):
+ _about_ = (create_link_pypy, create_link_pyobj)
+
+ def compute_result_annotation(self, s_p, s_ob):
+ pass
+
+ def specialize_call(self, hop):
+ if self.instance is create_link_pypy:
+ name = 'gc_rawrefcount_create_link_pypy'
+ elif self.instance is create_link_pyobj:
+ name = 'gc_rawrefcount_create_link_pyobj'
+ v_p, v_ob = hop.inputargs(*hop.args_r)
+ hop.exception_cannot_occur()
+ hop.genop(name, [_unspec_p(hop, v_p), _unspec_ob(hop, v_ob)])
+
+
+class Entry(ExtRegistryEntry):
+ _about_ = from_obj
+
+ def compute_result_annotation(self, s_OB_PTR_TYPE, s_p):
+ from rpython.annotator import model as annmodel
+ from rpython.rtyper.llannotation import lltype_to_annotation
+ assert (isinstance(s_p, annmodel.SomeInstance) or
+ annmodel.s_None.contains(s_p))
+ assert s_OB_PTR_TYPE.is_constant()
+ return lltype_to_annotation(s_OB_PTR_TYPE.const)
+
+ def specialize_call(self, hop):
+ hop.exception_cannot_occur()
+ v_p = hop.inputarg(hop.args_r[1], arg=1)
+ v_ob = hop.genop('gc_rawrefcount_from_obj', [_unspec_p(hop, v_p)],
+ resulttype = llmemory.Address)
+ return _spec_ob(hop, v_ob)
+
+class Entry(ExtRegistryEntry):
+ _about_ = to_obj
+
+ def compute_result_annotation(self, s_Class, s_ob):
+ from rpython.annotator import model as annmodel
+ from rpython.rtyper.llannotation import SomePtr
+ assert isinstance(s_ob, SomePtr)
+ assert s_Class.is_constant()
+ classdef = self.bookkeeper.getuniqueclassdef(s_Class.const)
+ return annmodel.SomeInstance(classdef, can_be_None=True)
+
+ def specialize_call(self, hop):
+ hop.exception_cannot_occur()
+ v_ob = hop.inputarg(hop.args_r[1], arg=1)
+ v_p = hop.genop('gc_rawrefcount_to_obj', [_unspec_ob(hop, v_ob)],
+ resulttype = llmemory.GCREF)
+ return _spec_p(hop, v_p)
+
+class Entry(ExtRegistryEntry):
+ _about_ = next_dead
+
+ def compute_result_annotation(self, s_OB_PTR_TYPE):
+ from rpython.annotator import model as annmodel
+ from rpython.rtyper.llannotation import lltype_to_annotation
+ assert s_OB_PTR_TYPE.is_constant()
+ return lltype_to_annotation(s_OB_PTR_TYPE.const)
+
+ def specialize_call(self, hop):
+ hop.exception_cannot_occur()
+ v_ob = hop.genop('gc_rawrefcount_next_dead', [],
+ resulttype = llmemory.Address)
+ return _spec_ob(hop, v_ob)
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
index e87849d393..99be096eef 100644
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -487,6 +487,7 @@ NULL_GCREF = lltype.nullptr(llmemory.GCREF.TO)
class _GcRef(object):
# implementation-specific: there should not be any after translation
__slots__ = ['_x', '_handle']
+ _TYPE = llmemory.GCREF
def __init__(self, x):
self._x = x
def __hash__(self):
diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py
index 7ef8da67e9..b2d29d345f 100644
--- a/rpython/rlib/rposix.py
+++ b/rpython/rlib/rposix.py
@@ -1,27 +1,22 @@
import os
import sys
import errno
+from rpython.annotator.model import s_Str0
from rpython.rtyper.lltypesystem.rffi import CConstant, CExternVariable, INT
from rpython.rtyper.lltypesystem import lltype, ll2ctypes, rffi
-from rpython.rtyper.module.support import StringTraits, UnicodeTraits
from rpython.rtyper.tool import rffi_platform
-from rpython.tool.sourcetools import func_renamer
-from rpython.translator.tool.cbuild import ExternalCompilationInfo
-from rpython.rlib.rarithmetic import intmask, widen
+from rpython.rlib import debug, jit, rstring, rthread, types
+from rpython.rlib._os_support import (
+ _CYGWIN, _MACRO_ON_POSIX, UNDERSCORE_ON_WIN32, _WIN32,
+ _prefer_unicode, _preferred_traits)
from rpython.rlib.objectmodel import (
specialize, enforceargs, register_replacement_for, NOT_CONSTANT)
+from rpython.rlib.rarithmetic import intmask, widen
from rpython.rlib.signature import signature
-from rpython.rlib import types
-from rpython.annotator.model import s_Str0
-from rpython.rlib import jit
+from rpython.tool.sourcetools import func_renamer
from rpython.translator.platform import platform
-from rpython.rlib import rstring
-from rpython.rlib import debug, rthread
+from rpython.translator.tool.cbuild import ExternalCompilationInfo
-_WIN32 = sys.platform.startswith('win')
-_CYGWIN = sys.platform == 'cygwin'
-UNDERSCORE_ON_WIN32 = '_' if _WIN32 else ''
-_MACRO_ON_POSIX = True if not _WIN32 else None
if _WIN32:
from rpython.rlib import rwin32
@@ -119,7 +114,6 @@ def get_saved_errno():
with the flag llexternal(..., save_err=rffi.RFFI_SAVE_ERRNO).
Functions without that flag don't change the saved errno.
"""
- from rpython.rlib import rthread
return intmask(rthread.tlfield_rpy_errno.getraw())
def set_saved_errno(errno):
@@ -130,7 +124,6 @@ def set_saved_errno(errno):
zero; for that case, use llexternal(..., save_err=RFFI_ZERO_ERRNO_BEFORE)
and then you don't need set_saved_errno(0).
"""
- from rpython.rlib import rthread
rthread.tlfield_rpy_errno.setraw(rffi.cast(INT, errno))
def get_saved_alterrno():
@@ -139,7 +132,6 @@ def get_saved_alterrno():
with the flag llexternal(..., save_err=rffi.RFFI_SAVE_ERRNO | rffl.RFFI_ALT_ERRNO).
Functions without that flag don't change the saved errno.
"""
- from rpython.rlib import rthread
return intmask(rthread.tlfield_alt_errno.getraw())
def set_saved_alterrno(errno):
@@ -150,7 +142,6 @@ def set_saved_alterrno(errno):
zero; for that case, use llexternal(..., save_err=RFFI_ZERO_ERRNO_BEFORE)
and then you don't need set_saved_errno(0).
"""
- from rpython.rlib import rthread
rthread.tlfield_alt_errno.setraw(rffi.cast(INT, errno))
@@ -158,7 +149,6 @@ def set_saved_alterrno(errno):
@specialize.call_location()
def _errno_before(save_err):
if save_err & rffi.RFFI_READSAVED_ERRNO:
- from rpython.rlib import rthread
if save_err & rffi.RFFI_ALT_ERRNO:
_set_errno(rthread.tlfield_alt_errno.getraw())
else:
@@ -166,7 +156,6 @@ def _errno_before(save_err):
elif save_err & rffi.RFFI_ZERO_ERRNO_BEFORE:
_set_errno(rffi.cast(rffi.INT, 0))
if _WIN32 and (save_err & rffi.RFFI_READSAVED_LASTERROR):
- from rpython.rlib import rthread, rwin32
if save_err & rffi.RFFI_ALT_ERRNO:
err = rthread.tlfield_alt_lasterror.getraw()
else:
@@ -180,7 +169,6 @@ def _errno_before(save_err):
def _errno_after(save_err):
if _WIN32:
if save_err & rffi.RFFI_SAVE_LASTERROR:
- from rpython.rlib import rthread, rwin32
err = rwin32._GetLastError()
# careful, setraw() overwrites GetLastError.
# We must read it first, before the errno handling.
@@ -189,14 +177,13 @@ def _errno_after(save_err):
else:
rthread.tlfield_rpy_lasterror.setraw(err)
elif save_err & rffi.RFFI_SAVE_WSALASTERROR:
- from rpython.rlib import rthread, _rsocket_rffi
+ from rpython.rlib import _rsocket_rffi
err = _rsocket_rffi._WSAGetLastError()
if save_err & rffi.RFFI_ALT_ERRNO:
rthread.tlfield_alt_lasterror.setraw(err)
else:
rthread.tlfield_rpy_lasterror.setraw(err)
if save_err & rffi.RFFI_SAVE_ERRNO:
- from rpython.rlib import rthread
if save_err & rffi.RFFI_ALT_ERRNO:
rthread.tlfield_alt_errno.setraw(_get_errno())
else:
@@ -342,37 +329,6 @@ def _as_unicode0(path):
rstring.check_str0(res)
return res
-# Returns True when the unicode function should be called:
-# - on Windows
-# - if the path is Unicode.
-unicode_traits = UnicodeTraits()
-string_traits = StringTraits()
-if _WIN32:
- @specialize.argtype(0)
- def _prefer_unicode(path):
- assert path is not None
- if isinstance(path, str):
- return False
- elif isinstance(path, unicode):
- return True
- else:
- return path.is_unicode
-
- @specialize.argtype(0)
- def _preferred_traits(path):
- if _prefer_unicode(path):
- return unicode_traits
- else:
- return string_traits
-else:
- @specialize.argtype(0)
- def _prefer_unicode(path):
- return False
-
- @specialize.argtype(0)
- def _preferred_traits(path):
- return string_traits
-
@specialize.argtype(0, 1)
def putenv(name, value):
os.environ[_as_bytes(name)] = _as_bytes(value)
diff --git a/rpython/rlib/rposix_environ.py b/rpython/rlib/rposix_environ.py
index 9906ceb0d3..b419b6eaeb 100644
--- a/rpython/rlib/rposix_environ.py
+++ b/rpython/rlib/rposix_environ.py
@@ -1,11 +1,12 @@
import os
import sys
from rpython.annotator import model as annmodel
+from rpython.rlib._os_support import _WIN32, StringTraits, UnicodeTraits
from rpython.rlib.objectmodel import enforceargs
+# importing rposix here creates a cycle on Windows
from rpython.rtyper.controllerentry import Controller
from rpython.rtyper.extfunc import register_external
from rpython.rtyper.lltypesystem import rffi, lltype
-from rpython.rtyper.module.support import _WIN32, StringTraits, UnicodeTraits
from rpython.translator.tool.cbuild import ExternalCompilationInfo
str0 = annmodel.s_Str0
diff --git a/rpython/rlib/rposix_stat.py b/rpython/rlib/rposix_stat.py
index b6c20b591c..1b66547e50 100644
--- a/rpython/rlib/rposix_stat.py
+++ b/rpython/rlib/rposix_stat.py
@@ -16,13 +16,13 @@ from rpython.rtyper.rmodel import Repr
from rpython.rtyper.rint import IntegerRepr
from rpython.rtyper.error import TyperError
+from rpython.rlib._os_support import _preferred_traits, string_traits
from rpython.rlib.objectmodel import specialize
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.translator.tool.cbuild import ExternalCompilationInfo
from rpython.rlib.rarithmetic import intmask
from rpython.rlib.rposix import (
- replace_os_function, handle_posix_error, _as_bytes0,
- _preferred_traits, string_traits)
+ replace_os_function, handle_posix_error, _as_bytes0)
_WIN32 = sys.platform.startswith('win')
_LINUX = sys.platform.startswith('linux')
diff --git a/rpython/rlib/rsocket.py b/rpython/rlib/rsocket.py
index 9f2247e05b..ff6b9b4257 100644
--- a/rpython/rlib/rsocket.py
+++ b/rpython/rlib/rsocket.py
@@ -516,6 +516,10 @@ class RSocket(object):
"""RPython-level socket object.
"""
fd = _c.INVALID_SOCKET
+ family = 0
+ type = 0
+ proto = 0
+ timeout = -1.0
def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0,
fd=_c.INVALID_SOCKET):
@@ -531,6 +535,11 @@ class RSocket(object):
self.proto = proto
self.timeout = defaults.timeout
+ @staticmethod
+ def empty_rsocket():
+ rsocket = instantiate(RSocket)
+ return rsocket
+
@rgc.must_be_light_finalizer
def __del__(self):
fd = self.fd
diff --git a/rpython/rlib/rvmprof/cintf.py b/rpython/rlib/rvmprof/cintf.py
index 154c06bcbd..80276a0975 100644
--- a/rpython/rlib/rvmprof/cintf.py
+++ b/rpython/rlib/rvmprof/cintf.py
@@ -7,8 +7,6 @@ from rpython.translator.tool.cbuild import ExternalCompilationInfo
from rpython.rtyper.tool import rffi_platform as platform
from rpython.rlib import rthread
-from rpython.jit.backend import detect_cpu
-
class VMProfPlatformUnsupported(Exception):
pass
diff --git a/rpython/rlib/test/test_jit.py b/rpython/rlib/test/test_jit.py
index 95b4c8987c..86abed36ba 100644
--- a/rpython/rlib/test/test_jit.py
+++ b/rpython/rlib/test/test_jit.py
@@ -4,7 +4,8 @@ from rpython.conftest import option
from rpython.annotator.model import UnionError
from rpython.rlib.jit import (hint, we_are_jitted, JitDriver, elidable_promote,
JitHintError, oopspec, isconstant, conditional_call,
- elidable, unroll_safe, dont_look_inside)
+ elidable, unroll_safe, dont_look_inside,
+ enter_portal_frame, leave_portal_frame)
from rpython.rlib.rarithmetic import r_uint
from rpython.rtyper.test.tool import BaseRtypingTest
from rpython.rtyper.lltypesystem import lltype
@@ -300,3 +301,11 @@ class TestJIT(BaseRtypingTest):
mix = MixLevelHelperAnnotator(t.rtyper)
mix.getgraph(later, [annmodel.s_Bool], annmodel.s_None)
mix.finish()
+
+ def test_enter_leave_portal_frame(self):
+ from rpython.translator.interactive import Translation
+ def g():
+ enter_portal_frame(1)
+ leave_portal_frame()
+ t = Translation(g, [])
+ t.compile_c() # does not crash
diff --git a/rpython/rlib/test/test_posix.py b/rpython/rlib/test/test_posix.py
new file mode 100644
index 0000000000..6f4b2bce62
--- /dev/null
+++ b/rpython/rlib/test/test_posix.py
@@ -0,0 +1,304 @@
+import py.test
+from rpython.rtyper.test.tool import BaseRtypingTest
+from rpython.rtyper.annlowlevel import hlstr
+from rpython.tool.udir import udir
+from rpython.rlib.rarithmetic import is_valid_int
+
+import os
+exec 'import %s as posix' % os.name
+
+def setup_module(module):
+ testf = udir.join('test.txt')
+ module.path = testf.strpath
+
+class TestPosix(BaseRtypingTest):
+
+ def setup_method(self, meth):
+ # prepare/restore the file before each test
+ testfile = open(path, 'wb')
+ testfile.write('This is a test')
+ testfile.close()
+
+ def test_open(self):
+ def f():
+ ff = posix.open(path, posix.O_RDONLY, 0777)
+ return ff
+ func = self.interpret(f, [])
+ assert is_valid_int(func)
+
+ def test_fstat(self):
+ def fo(fi):
+ g = posix.fstat(fi)
+ return g
+ fi = os.open(path,os.O_RDONLY,0777)
+ func = self.interpret(fo,[fi])
+ stat = os.fstat(fi)
+ for i in range(len(stat)):
+ assert long(getattr(func, 'item%d' % i)) == stat[i]
+
+
+ def test_stat(self):
+ def fo():
+ g = posix.stat(path)
+ return g
+ func = self.interpret(fo,[])
+ stat = os.stat(path)
+ for i in range(len(stat)):
+ assert long(getattr(func, 'item%d' % i)) == stat[i]
+
+ def test_stat_exception(self):
+ def fo():
+ try:
+ posix.stat('I/do/not/exist')
+ except OSError:
+ return True
+ else:
+ return False
+ res = self.interpret(fo,[])
+ assert res
+
+ def test_times(self):
+ py.test.skip("llinterp does not like tuple returns")
+ from rpython.rtyper.test.test_llinterp import interpret
+ times = interpret(lambda: posix.times(), ())
+ assert isinstance(times, tuple)
+ assert len(times) == 5
+ for value in times:
+ assert is_valid_int(value)
+
+
+ def test_lseek(self):
+ def f(fi, pos):
+ posix.lseek(fi, pos, 0)
+ fi = os.open(path, os.O_RDONLY, 0777)
+ func = self.interpret(f, [fi, 5])
+ res = os.read(fi, 2)
+ assert res =='is'
+
+ def test_isatty(self):
+ def f(fi):
+ posix.isatty(fi)
+ fi = os.open(path, os.O_RDONLY, 0777)
+ func = self.interpret(f, [fi])
+ assert not func
+ os.close(fi)
+ func = self.interpret(f, [fi])
+ assert not func
+
+ def test_getcwd(self):
+ def f():
+ return posix.getcwd()
+ res = self.interpret(f,[])
+ cwd = os.getcwd()
+ #print res.chars,cwd
+ assert self.ll_to_string(res) == cwd
+
+ def test_write(self):
+ def f(fi):
+ if fi > 0:
+ text = 'This is a test'
+ else:
+ text = '333'
+ return posix.write(fi,text)
+ fi = os.open(path,os.O_WRONLY,0777)
+ text = 'This is a test'
+ func = self.interpret(f,[fi])
+ os.close(fi)
+ fi = os.open(path,os.O_RDONLY,0777)
+ res = os.read(fi,20)
+ assert res == text
+
+ def test_read(self):
+ def f(fi,len):
+ return posix.read(fi,len)
+ fi = os.open(path,os.O_WRONLY,0777)
+ text = 'This is a test'
+ os.write(fi,text)
+ os.close(fi)
+ fi = os.open(path,os.O_RDONLY,0777)
+ res = self.interpret(f,[fi,20])
+ assert self.ll_to_string(res) == text
+
+ @py.test.mark.skipif("not hasattr(os, 'chown')")
+ def test_chown(self):
+ f = open(path, "w")
+ f.write("xyz")
+ f.close()
+ def f():
+ try:
+ posix.chown(path, os.getuid(), os.getgid())
+ return 1
+ except OSError:
+ return 2
+
+ assert self.interpret(f, []) == 1
+ os.unlink(path)
+ assert self.interpret(f, []) == 2
+
+ def test_close(self):
+ def f(fi):
+ return posix.close(fi)
+ fi = os.open(path,os.O_WRONLY,0777)
+ text = 'This is a test'
+ os.write(fi,text)
+ res = self.interpret(f,[fi])
+ py.test.raises( OSError, os.fstat, fi)
+
+ @py.test.mark.skipif("not hasattr(os, 'ftruncate')")
+ def test_ftruncate(self):
+ def f(fi,len):
+ os.ftruncate(fi,len)
+ fi = os.open(path,os.O_RDWR,0777)
+ func = self.interpret(f,[fi,6])
+ assert os.fstat(fi).st_size == 6
+
+ @py.test.mark.skipif("not hasattr(os, 'getuid')")
+ def test_getuid(self):
+ def f():
+ return os.getuid()
+ assert self.interpret(f, []) == f()
+
+ @py.test.mark.skipif("not hasattr(os, 'getgid')")
+ def test_getgid(self):
+ def f():
+ return os.getgid()
+ assert self.interpret(f, []) == f()
+
+ @py.test.mark.skipif("not hasattr(os, 'setuid')")
+ def test_os_setuid(self):
+ def f():
+ os.setuid(os.getuid())
+ return os.getuid()
+ assert self.interpret(f, []) == f()
+
+ @py.test.mark.skipif("not hasattr(os, 'sysconf')")
+ def test_os_sysconf(self):
+ def f(i):
+ return os.sysconf(i)
+ assert self.interpret(f, [13]) == f(13)
+
+ @py.test.mark.skipif("not hasattr(os, 'confstr')")
+ def test_os_confstr(self):
+ def f(i):
+ try:
+ return os.confstr(i)
+ except OSError:
+ return "oooops!!"
+ some_value = os.confstr_names.values()[-1]
+ res = self.interpret(f, [some_value])
+ assert hlstr(res) == f(some_value)
+ res = self.interpret(f, [94781413])
+ assert hlstr(res) == "oooops!!"
+
+ @py.test.mark.skipif("not hasattr(os, 'pathconf')")
+ def test_os_pathconf(self):
+ def f(i):
+ return os.pathconf("/tmp", i)
+ i = os.pathconf_names["PC_NAME_MAX"]
+ some_value = self.interpret(f, [i])
+ assert some_value >= 31
+
+ @py.test.mark.skipif("not hasattr(os, 'chroot')")
+ def test_os_chroot(self):
+ def f():
+ try:
+ os.chroot('!@$#!#%$#^#@!#!$$#^')
+ except OSError:
+ return 1
+ return 0
+
+ assert self.interpret(f, []) == 1
+
+ def test_os_wstar(self):
+ from rpython.rlib import rposix
+ for name in rposix.WAIT_MACROS:
+ if not hasattr(os, name):
+ continue
+ def fun(s):
+ return getattr(os, name)(s)
+
+ for value in [0, 1, 127, 128, 255]:
+ res = self.interpret(fun, [value])
+ assert res == fun(value)
+
+ @py.test.mark.skipif("not hasattr(os, 'getgroups')")
+ def test_getgroups(self):
+ def f():
+ return os.getgroups()
+ ll_a = self.interpret(f, [])
+ assert self.ll_to_list(ll_a) == f()
+
+ @py.test.mark.skipif("not hasattr(os, 'setgroups')")
+ def test_setgroups(self):
+ def f():
+ try:
+ os.setgroups(os.getgroups())
+ except OSError:
+ pass
+ self.interpret(f, [])
+
+ @py.test.mark.skipif("not hasattr(os, 'initgroups')")
+ def test_initgroups(self):
+ def f():
+ try:
+ os.initgroups('sUJJeumz', 4321)
+ except OSError:
+ return 1
+ return 0
+ res = self.interpret(f, [])
+ assert res == 1
+
+ @py.test.mark.skipif("not hasattr(os, 'tcgetpgrp')")
+ def test_tcgetpgrp(self):
+ def f(fd):
+ try:
+ return os.tcgetpgrp(fd)
+ except OSError:
+ return 42
+ res = self.interpret(f, [9999])
+ assert res == 42
+
+ @py.test.mark.skipif("not hasattr(os, 'tcsetpgrp')")
+ def test_tcsetpgrp(self):
+ def f(fd, pgrp):
+ try:
+ os.tcsetpgrp(fd, pgrp)
+ except OSError:
+ return 1
+ return 0
+ res = self.interpret(f, [9999, 1])
+ assert res == 1
+
+ @py.test.mark.skipif("not hasattr(os, 'getresuid')")
+ def test_getresuid(self):
+ def f():
+ a, b, c = os.getresuid()
+ return a + b * 37 + c * 1291
+ res = self.interpret(f, [])
+ a, b, c = os.getresuid()
+ assert res == a + b * 37 + c * 1291
+
+ @py.test.mark.skipif("not hasattr(os, 'getresgid')")
+ def test_getresgid(self):
+ def f():
+ a, b, c = os.getresgid()
+ return a + b * 37 + c * 1291
+ res = self.interpret(f, [])
+ a, b, c = os.getresgid()
+ assert res == a + b * 37 + c * 1291
+
+ @py.test.mark.skipif("not hasattr(os, 'setresuid')")
+ def test_setresuid(self):
+ def f():
+ a, b, c = os.getresuid()
+ a = (a + 1) - 1
+ os.setresuid(a, b, c)
+ self.interpret(f, [])
+
+ @py.test.mark.skipif("not hasattr(os, 'setresgid')")
+ def test_setresgid(self):
+ def f():
+ a, b, c = os.getresgid()
+ a = (a + 1) - 1
+ os.setresgid(a, b, c)
+ self.interpret(f, [])
diff --git a/rpython/rlib/test/test_rawrefcount.py b/rpython/rlib/test/test_rawrefcount.py
new file mode 100644
index 0000000000..6f095b9ae7
--- /dev/null
+++ b/rpython/rlib/test/test_rawrefcount.py
@@ -0,0 +1,268 @@
+import weakref
+from rpython.rlib import rawrefcount, objectmodel, rgc
+from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY, REFCNT_FROM_PYPY_LIGHT
+from rpython.rtyper.lltypesystem import lltype, llmemory
+from rpython.rtyper.annlowlevel import llhelper
+from rpython.translator.c.test.test_standalone import StandaloneTests
+from rpython.config.translationoption import get_combined_translation_config
+
+
+class W_Root(object):
+ def __init__(self, intval=0):
+ self.intval = intval
+ def __nonzero__(self):
+ raise Exception("you cannot do that, you must use space.is_true()")
+
+PyObjectS = lltype.Struct('PyObjectS',
+ ('c_ob_refcnt', lltype.Signed),
+ ('c_ob_pypy_link', lltype.Signed))
+PyObject = lltype.Ptr(PyObjectS)
+
+
+class TestRawRefCount:
+
+ def setup_method(self, meth):
+ rawrefcount.init()
+
+ def test_create_link_pypy(self):
+ p = W_Root(42)
+ ob = lltype.malloc(PyObjectS, flavor='raw', zero=True)
+ assert rawrefcount.from_obj(PyObject, p) == lltype.nullptr(PyObjectS)
+ assert rawrefcount.to_obj(W_Root, ob) == None
+ rawrefcount.create_link_pypy(p, ob)
+ assert ob.c_ob_refcnt == 0
+ ob.c_ob_refcnt += REFCNT_FROM_PYPY_LIGHT
+ assert rawrefcount.from_obj(PyObject, p) == ob
+ assert rawrefcount.to_obj(W_Root, ob) == p
+ lltype.free(ob, flavor='raw')
+
+ def test_create_link_pyobj(self):
+ p = W_Root(42)
+ ob = lltype.malloc(PyObjectS, flavor='raw', zero=True)
+ assert rawrefcount.from_obj(PyObject, p) == lltype.nullptr(PyObjectS)
+ assert rawrefcount.to_obj(W_Root, ob) == None
+ rawrefcount.create_link_pyobj(p, ob)
+ assert ob.c_ob_refcnt == 0
+ ob.c_ob_refcnt += REFCNT_FROM_PYPY
+ assert rawrefcount.from_obj(PyObject, p) == lltype.nullptr(PyObjectS)
+ assert rawrefcount.to_obj(W_Root, ob) == p
+ lltype.free(ob, flavor='raw')
+
+ def test_collect_p_dies(self):
+ p = W_Root(42)
+ ob = lltype.malloc(PyObjectS, flavor='raw', zero=True)
+ rawrefcount.create_link_pypy(p, ob)
+ ob.c_ob_refcnt += REFCNT_FROM_PYPY_LIGHT
+ assert rawrefcount._p_list == [ob]
+ wr_ob = weakref.ref(ob)
+ wr_p = weakref.ref(p)
+ del ob, p
+ rawrefcount._collect()
+ assert rawrefcount._p_list == []
+ assert wr_ob() is None
+ assert wr_p() is None
+
+ def test_collect_p_keepalive_pyobject(self):
+ p = W_Root(42)
+ ob = lltype.malloc(PyObjectS, flavor='raw', zero=True)
+ rawrefcount.create_link_pypy(p, ob)
+ ob.c_ob_refcnt += REFCNT_FROM_PYPY_LIGHT
+ assert rawrefcount._p_list == [ob]
+ wr_ob = weakref.ref(ob)
+ wr_p = weakref.ref(p)
+ ob.c_ob_refcnt += 1 # <=
+ del ob, p
+ rawrefcount._collect()
+ ob = wr_ob()
+ p = wr_p()
+ assert ob is not None and p is not None
+ assert rawrefcount._p_list == [ob]
+ assert rawrefcount.to_obj(W_Root, ob) == p
+ assert rawrefcount.from_obj(PyObject, p) == ob
+ lltype.free(ob, flavor='raw')
+
+ def test_collect_p_keepalive_w_root(self):
+ p = W_Root(42)
+ ob = lltype.malloc(PyObjectS, flavor='raw', zero=True)
+ rawrefcount.create_link_pypy(p, ob)
+ ob.c_ob_refcnt += REFCNT_FROM_PYPY_LIGHT
+ assert rawrefcount._p_list == [ob]
+ wr_ob = weakref.ref(ob)
+ del ob # p remains
+ rawrefcount._collect()
+ ob = wr_ob()
+ assert ob is not None
+ assert rawrefcount._p_list == [ob]
+ assert rawrefcount.to_obj(W_Root, ob) == p
+ assert rawrefcount.from_obj(PyObject, p) == ob
+ lltype.free(ob, flavor='raw')
+
+ def test_collect_o_dies(self):
+ trigger = []; rawrefcount.init(lambda: trigger.append(1))
+ p = W_Root(42)
+ ob = lltype.malloc(PyObjectS, flavor='raw', zero=True)
+ rawrefcount.create_link_pyobj(p, ob)
+ ob.c_ob_refcnt += REFCNT_FROM_PYPY
+ assert rawrefcount._o_list == [ob]
+ wr_ob = weakref.ref(ob)
+ wr_p = weakref.ref(p)
+ del ob, p
+ rawrefcount._collect()
+ ob = wr_ob()
+ assert ob is not None
+ assert trigger == [1]
+ assert rawrefcount.next_dead(PyObject) == ob
+ assert rawrefcount.next_dead(PyObject) == lltype.nullptr(PyObjectS)
+ assert rawrefcount.next_dead(PyObject) == lltype.nullptr(PyObjectS)
+ assert rawrefcount._o_list == []
+ assert wr_p() is None
+ assert ob.c_ob_refcnt == 0
+ assert ob.c_ob_pypy_link == 0
+ lltype.free(ob, flavor='raw')
+
+ def test_collect_o_keepalive_pyobject(self):
+ p = W_Root(42)
+ ob = lltype.malloc(PyObjectS, flavor='raw', zero=True)
+ p.pyobj = ob
+ rawrefcount.create_link_pyobj(p, ob)
+ ob.c_ob_refcnt += REFCNT_FROM_PYPY
+ assert rawrefcount._o_list == [ob]
+ wr_ob = weakref.ref(ob)
+ wr_p = weakref.ref(p)
+ ob.c_ob_refcnt += 1 # <=
+ del p
+ rawrefcount._collect()
+ p = wr_p()
+ assert p is None # was unlinked
+ assert ob.c_ob_refcnt == 1 # != REFCNT_FROM_PYPY_OBJECT + 1
+ assert rawrefcount._o_list == []
+ assert rawrefcount.to_obj(W_Root, ob) == None
+ lltype.free(ob, flavor='raw')
+
+ def test_collect_o_keepalive_w_root(self):
+ p = W_Root(42)
+ ob = lltype.malloc(PyObjectS, flavor='raw', zero=True)
+ p.pyobj = ob
+ rawrefcount.create_link_pyobj(p, ob)
+ ob.c_ob_refcnt += REFCNT_FROM_PYPY
+ assert rawrefcount._o_list == [ob]
+ wr_ob = weakref.ref(ob)
+ del ob # p remains
+ rawrefcount._collect()
+ ob = wr_ob()
+ assert ob is not None
+ assert rawrefcount._o_list == [ob]
+ assert rawrefcount.to_obj(W_Root, ob) == p
+ assert p.pyobj == ob
+ lltype.free(ob, flavor='raw')
+
+ def test_collect_s_dies(self):
+ trigger = []; rawrefcount.init(lambda: trigger.append(1))
+ p = W_Root(42)
+ ob = lltype.malloc(PyObjectS, flavor='raw', zero=True)
+ rawrefcount.create_link_pypy(p, ob)
+ ob.c_ob_refcnt += REFCNT_FROM_PYPY
+ assert rawrefcount._p_list == [ob]
+ wr_ob = weakref.ref(ob)
+ wr_p = weakref.ref(p)
+ del ob, p
+ rawrefcount._collect()
+ ob = wr_ob()
+ assert ob is not None
+ assert trigger == [1]
+ assert rawrefcount._d_list == [ob]
+ assert rawrefcount._p_list == []
+ assert wr_p() is None
+ assert ob.c_ob_refcnt == 0
+ assert ob.c_ob_pypy_link == 0
+ lltype.free(ob, flavor='raw')
+
+ def test_collect_s_keepalive_pyobject(self):
+ p = W_Root(42)
+ ob = lltype.malloc(PyObjectS, flavor='raw', zero=True)
+ p.pyobj = ob
+ rawrefcount.create_link_pypy(p, ob)
+ ob.c_ob_refcnt += REFCNT_FROM_PYPY
+ assert rawrefcount._p_list == [ob]
+ wr_ob = weakref.ref(ob)
+ wr_p = weakref.ref(p)
+ ob.c_ob_refcnt += 1 # <=
+ del ob, p
+ rawrefcount._collect()
+ ob = wr_ob()
+ p = wr_p()
+ assert ob is not None and p is not None
+ assert rawrefcount._p_list == [ob]
+ assert rawrefcount.to_obj(W_Root, ob) == p
+ lltype.free(ob, flavor='raw')
+
+ def test_collect_s_keepalive_w_root(self):
+ p = W_Root(42)
+ ob = lltype.malloc(PyObjectS, flavor='raw', zero=True)
+ p.pyobj = ob
+ rawrefcount.create_link_pypy(p, ob)
+ ob.c_ob_refcnt += REFCNT_FROM_PYPY
+ assert rawrefcount._p_list == [ob]
+ wr_ob = weakref.ref(ob)
+ del ob # p remains
+ rawrefcount._collect()
+ ob = wr_ob()
+ assert ob is not None
+ assert rawrefcount._p_list == [ob]
+ assert rawrefcount.to_obj(W_Root, ob) == p
+ lltype.free(ob, flavor='raw')
+
+
+class TestTranslated(StandaloneTests):
+
+ def test_full_translation(self):
+ class State:
+ pass
+ state = State()
+ state.seen = []
+ def dealloc_trigger():
+ state.seen.append(1)
+
+ def make_p():
+ p = W_Root(42)
+ ob = lltype.malloc(PyObjectS, flavor='raw', zero=True)
+ rawrefcount.create_link_pypy(p, ob)
+ ob.c_ob_refcnt += REFCNT_FROM_PYPY
+ assert rawrefcount.from_obj(PyObject, p) == ob
+ assert rawrefcount.to_obj(W_Root, ob) == p
+ return ob, p
+
+ FTYPE = rawrefcount.RAWREFCOUNT_DEALLOC_TRIGGER
+
+ def entry_point(argv):
+ ll_dealloc_trigger_callback = llhelper(FTYPE, dealloc_trigger)
+ rawrefcount.init(ll_dealloc_trigger_callback)
+ ob, p = make_p()
+ if state.seen != []:
+ print "OB COLLECTED REALLY TOO SOON"
+ return 1
+ rgc.collect()
+ if state.seen != []:
+ print "OB COLLECTED TOO SOON"
+ return 1
+ objectmodel.keepalive_until_here(p)
+ p = None
+ rgc.collect()
+ if state.seen != [1]:
+ print "OB NOT COLLECTED"
+ return 1
+ if rawrefcount.next_dead(PyObject) != ob:
+ print "NEXT_DEAD != OB"
+ return 1
+ if rawrefcount.next_dead(PyObject) != lltype.nullptr(PyObjectS):
+ print "NEXT_DEAD second time != NULL"
+ return 1
+ print "OK!"
+ lltype.free(ob, flavor='raw')
+ return 0
+
+ self.config = get_combined_translation_config(translating=True)
+ self.config.translation.gc = "incminimark"
+ t, cbuilder = self.compile(entry_point)
+ data = cbuilder.cmdexec('hi there')
+ assert data.startswith('OK!\n')
diff --git a/rpython/rlib/test/test_rerased.py b/rpython/rlib/test/test_rerased.py
index 9cb4924cfc..989a62bede 100644
--- a/rpython/rlib/test/test_rerased.py
+++ b/rpython/rlib/test/test_rerased.py
@@ -192,7 +192,7 @@ class TestRErased(BaseRtypingTest):
def interpret(self, *args, **kwargs):
kwargs["taggedpointers"] = True
- return BaseRtypingTest.interpret(self, *args, **kwargs)
+ return BaseRtypingTest.interpret(*args, **kwargs)
def test_rtype_1(self):
def f():
return eraseX(X())
diff --git a/rpython/rtyper/extfunc.py b/rpython/rtyper/extfunc.py
index 30d61ec4ee..0b926c9ce1 100644
--- a/rpython/rtyper/extfunc.py
+++ b/rpython/rtyper/extfunc.py
@@ -1,99 +1,105 @@
-from rpython.rtyper.extregistry import ExtRegistryEntry
-from rpython.rtyper.lltypesystem.lltype import typeOf, FuncType, functionptr
-from rpython.annotator.model import unionof
+from rpython.annotator.model import unionof, SomeObject
from rpython.annotator.signature import annotation, SignatureError
+from rpython.rtyper.extregistry import ExtRegistryEntry, lookup
+from rpython.rtyper.lltypesystem.lltype import (
+ typeOf, FuncType, functionptr, _ptr, Void)
+from rpython.rtyper.error import TyperError
+from rpython.rtyper.rmodel import Repr
-import py
+class SomeExternalFunction(SomeObject):
+ def __init__(self, name, args_s, s_result):
+ self.name = name
+ self.args_s = args_s
+ self.s_result = s_result
-class ExtFuncEntry(ExtRegistryEntry):
- safe_not_sandboxed = False
+ def check_args(self, callspec):
+ params_s = self.args_s
+ args_s, kwargs = callspec.unpack()
+ if kwargs:
+ raise SignatureError(
+ "External functions cannot be called with keyword arguments")
+ if len(args_s) != len(params_s):
+ raise SignatureError("Argument number mismatch")
+ for i, s_param in enumerate(params_s):
+ arg = unionof(args_s[i], s_param)
+ if not s_param.contains(arg):
+ raise SignatureError(
+ "In call to external function %r:\n"
+ "arg %d must be %s,\n"
+ " got %s" % (
+ self.name, i + 1, s_param, args_s[i]))
+
+ def call(self, callspec):
+ self.check_args(callspec)
+ return self.s_result
+
+ def rtyper_makerepr(self, rtyper):
+ if not self.is_constant():
+ raise TyperError("Non-constant external function!")
+ entry = lookup(self.const)
+ impl = getattr(entry, 'lltypeimpl', None)
+ fakeimpl = getattr(entry, 'lltypefakeimpl', None)
+ return ExternalFunctionRepr(self, impl, fakeimpl)
+
+ def rtyper_makekey(self):
+ return self.__class__, self
- # common case: args is a list of annotation or types
- def normalize_args(self, *args_s):
- args = self.signature_args
- signature_args = [annotation(arg, None) for arg in args]
- assert len(args_s) == len(signature_args),\
- "Argument number mismatch"
-
- for i, expected in enumerate(signature_args):
- arg = unionof(args_s[i], expected)
- if not expected.contains(arg):
- name = getattr(self, 'name', None)
- if not name:
- try:
- name = self.instance.__name__
- except AttributeError:
- name = '?'
- raise SignatureError("In call to external function %r:\n"
- "arg %d must be %s,\n"
- " got %s" % (
- name, i+1, expected, args_s[i]))
- return signature_args
-
- def compute_result_annotation(self, *args_s):
- self.normalize_args(*args_s) # check arguments
- return self.signature_result
-
- def specialize_call(self, hop):
+class ExternalFunctionRepr(Repr):
+ lowleveltype = Void
+
+ def __init__(self, s_func, impl, fakeimpl):
+ self.s_func = s_func
+ self.impl = impl
+ self.fakeimpl = fakeimpl
+
+ def rtype_simple_call(self, hop):
rtyper = hop.rtyper
- signature_args = self.normalize_args(*hop.args_s)
- args_r = [rtyper.getrepr(s_arg) for s_arg in signature_args]
+ args_r = [rtyper.getrepr(s_arg) for s_arg in self.s_func.args_s]
+ r_result = rtyper.getrepr(self.s_func.s_result)
+ obj = self.get_funcptr(rtyper, args_r, r_result)
+ hop2 = hop.copy()
+ hop2.r_s_popfirstarg()
+ vlist = [hop2.inputconst(typeOf(obj), obj)] + hop2.inputargs(*args_r)
+ hop2.exception_is_here()
+ return hop2.genop('direct_call', vlist, r_result)
+
+ def get_funcptr(self, rtyper, args_r, r_result):
+ from rpython.rtyper.rtyper import llinterp_backend
args_ll = [r_arg.lowleveltype for r_arg in args_r]
- s_result = hop.s_result
- r_result = rtyper.getrepr(s_result)
ll_result = r_result.lowleveltype
- name = getattr(self, 'name', None) or self.instance.__name__
- impl = getattr(self, 'lltypeimpl', None)
- fakeimpl = getattr(self, 'lltypefakeimpl', self.instance)
- if impl:
- if (rtyper.annotator.translator.config.translation.sandbox
- and not self.safe_not_sandboxed):
- from rpython.translator.sandbox.rsandbox import (
- make_sandbox_trampoline)
- impl = make_sandbox_trampoline(
- self.name, signature_args, s_result)
- if hasattr(self, 'lltypefakeimpl'):
- # If we have both an llimpl and an llfakeimpl,
- # we need a wrapper that selects the proper one and calls it
- from rpython.tool.sourcetools import func_with_new_name
- # Using '*args' is delicate because this wrapper is also
- # created for init-time functions like llarena.arena_malloc
- # which are called before the GC is fully initialized
- args = ', '.join(['arg%d' % i for i in range(len(args_ll))])
- d = {'original_impl': impl,
- 's_result': s_result,
- 'fakeimpl': fakeimpl,
- '__name__': __name__,
- }
- exec py.code.compile("""
- from rpython.rlib.objectmodel import running_on_llinterp
- from rpython.rlib.debug import llinterpcall
- from rpython.rlib.jit import dont_look_inside
- # note: we say 'dont_look_inside' mostly because the
- # JIT does not support 'running_on_llinterp', but in
- # theory it is probably right to stop jitting anyway.
- @dont_look_inside
- def ll_wrapper(%s):
- if running_on_llinterp:
- return llinterpcall(s_result, fakeimpl, %s)
- else:
- return original_impl(%s)
- """ % (args, args, args)) in d
- impl = func_with_new_name(d['ll_wrapper'], name + '_wrapper')
- # store some attributes to the 'impl' function, where
- # the eventual call to rtyper.getcallable() will find them
- # and transfer them to the final lltype.functionptr().
- impl._llfnobjattrs_ = {'_name': self.name}
- obj = rtyper.getannmixlevel().delayedfunction(
- impl, signature_args, hop.s_result)
+ name = self.s_func.name
+ if self.fakeimpl and rtyper.backend is llinterp_backend:
+ FT = FuncType(args_ll, ll_result)
+ return functionptr(
+ FT, name, _external_name=name, _callable=self.fakeimpl)
+ elif self.impl:
+ if isinstance(self.impl, _ptr):
+ return self.impl
+ else:
+ # store some attributes to the 'impl' function, where
+ # the eventual call to rtyper.getcallable() will find them
+ # and transfer them to the final lltype.functionptr().
+ self.impl._llfnobjattrs_ = {'_name': name}
+ return rtyper.getannmixlevel().delayedfunction(
+ self.impl, self.s_func.args_s, self.s_func.s_result)
else:
+ fakeimpl = self.fakeimpl or self.s_func.const
FT = FuncType(args_ll, ll_result)
- obj = functionptr(FT, name, _external_name=self.name,
- _callable=fakeimpl,
- _safe_not_sandboxed=self.safe_not_sandboxed)
- vlist = [hop.inputconst(typeOf(obj), obj)] + hop.inputargs(*args_r)
- hop.exception_is_here()
- return hop.genop('direct_call', vlist, r_result)
+ return functionptr(
+ FT, name, _external_name=name, _callable=fakeimpl)
+
+
+class ExtFuncEntry(ExtRegistryEntry):
+ safe_not_sandboxed = False
+
+ def compute_annotation(self):
+ s_result = SomeExternalFunction(
+ self.name, self.signature_args, self.signature_result)
+ if (self.bookkeeper.annotator.translator.config.translation.sandbox
+ and not self.safe_not_sandboxed):
+ s_result.needs_sandboxing = True
+ return s_result
+
def register_external(function, args, result=None, export_name=None,
llimpl=None, llfakeimpl=None, sandboxsafe=False):
@@ -109,32 +115,20 @@ def register_external(function, args, result=None, export_name=None,
if export_name is None:
export_name = function.__name__
+ params_s = [annotation(arg) for arg in args]
+ s_result = annotation(result)
class FunEntry(ExtFuncEntry):
_about_ = function
safe_not_sandboxed = sandboxsafe
-
- if args is None:
- def normalize_args(self, *args_s):
- return args_s # accept any argument unmodified
- elif callable(args):
- # custom annotation normalizer (see e.g. os.utime())
- normalize_args = staticmethod(args)
- else: # use common case behavior
- signature_args = args
-
- signature_result = annotation(result, None)
+ signature_args = params_s
+ signature_result = s_result
name = export_name
if llimpl:
lltypeimpl = staticmethod(llimpl)
if llfakeimpl:
lltypefakeimpl = staticmethod(llfakeimpl)
- if export_name:
- FunEntry.__name__ = export_name
- else:
- FunEntry.__name__ = function.func_name
-
def is_external(func):
if hasattr(func, 'value'):
func = func.value
diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py
index caeea15e6f..8f29a3208e 100644
--- a/rpython/rtyper/llinterp.py
+++ b/rpython/rtyper/llinterp.py
@@ -925,6 +925,21 @@ class LLFrame(object):
def op_gc_gcflag_extra(self, subopnum, *args):
return self.heap.gcflag_extra(subopnum, *args)
+ def op_gc_rawrefcount_init(self, *args):
+ raise NotImplementedError("gc_rawrefcount_init")
+
+ def op_gc_rawrefcount_to_obj(self, *args):
+ raise NotImplementedError("gc_rawrefcount_to_obj")
+
+ def op_gc_rawrefcount_from_obj(self, *args):
+ raise NotImplementedError("gc_rawrefcount_from_obj")
+
+ def op_gc_rawrefcount_create_link_pyobj(self, *args):
+ raise NotImplementedError("gc_rawrefcount_create_link_pyobj")
+
+ def op_gc_rawrefcount_create_link_pypy(self, *args):
+ raise NotImplementedError("gc_rawrefcount_create_link_pypy")
+
def op_do_malloc_fixedsize(self):
raise NotImplementedError("do_malloc_fixedsize")
def op_do_malloc_fixedsize_clear(self):
diff --git a/rpython/rtyper/lltypesystem/ll2ctypes.py b/rpython/rtyper/lltypesystem/ll2ctypes.py
index 43a59f82cf..664be37524 100644
--- a/rpython/rtyper/lltypesystem/ll2ctypes.py
+++ b/rpython/rtyper/lltypesystem/ll2ctypes.py
@@ -426,7 +426,12 @@ def convert_struct(container, cstruct=None, delayed_converters=None):
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
@@ -463,6 +468,9 @@ def convert_struct(container, cstruct=None, delayed_converters=None):
def remove_regular_struct_content(container):
STRUCT = container._TYPE
+ if isinstance(STRUCT, lltype.FixedSizeArray):
+ del container._items
+ return
for field_name in STRUCT._names:
FIELDTYPE = getattr(STRUCT, field_name)
if not isinstance(FIELDTYPE, lltype.ContainerType):
@@ -503,7 +511,11 @@ def remove_regular_array_content(container):
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)
@@ -515,8 +527,10 @@ def struct_use_ctypes_storage(container, ctypes_storage):
struct_use_ctypes_storage(struct_container, struct_storage)
struct_container._setparentstructure(container, field_name)
elif isinstance(FIELDTYPE, lltype.Array):
- assert FIELDTYPE._hints.get('nolength', False) == False
- arraycontainer = _array_of_known_length(FIELDTYPE)
+ if FIELDTYPE._hints.get('nolength', False):
+ arraycontainer = _array_of_unknown_length(FIELDTYPE)
+ else:
+ arraycontainer = _array_of_known_length(FIELDTYPE)
arraycontainer._storage = ctypes.pointer(
getattr(ctypes_storage.contents, field_name))
arraycontainer._setparentstructure(container, field_name)
@@ -528,6 +542,7 @@ def struct_use_ctypes_storage(container, ctypes_storage):
# Ctypes-aware subclasses of the _parentable classes
ALLOCATED = {} # mapping {address: _container}
+DEBUG_ALLOCATED = False
def get_common_subclass(cls1, cls2, cache={}):
"""Return a unique subclass with (cls1, cls2) as bases."""
@@ -567,6 +582,8 @@ class _parentable_mixin(object):
raise Exception("internal ll2ctypes error - "
"double conversion from lltype to ctypes?")
# XXX don't store here immortal structures
+ if DEBUG_ALLOCATED:
+ print >> sys.stderr, "LL2CTYPES:", hex(addr)
ALLOCATED[addr] = self
def _addressof_storage(self):
@@ -579,6 +596,8 @@ class _parentable_mixin(object):
self._check() # no double-frees
# allow the ctypes object to go away now
addr = ctypes.cast(self._storage, ctypes.c_void_p).value
+ if DEBUG_ALLOCATED:
+ print >> sys.stderr, "LL2C FREE:", hex(addr)
try:
del ALLOCATED[addr]
except KeyError:
@@ -613,11 +632,14 @@ class _parentable_mixin(object):
return object.__hash__(self)
def __repr__(self):
+ if '__str__' in self._TYPE._adtmeths:
+ r = self._TYPE._adtmeths['__str__'](self)
+ else:
+ r = 'C object %s' % (self._TYPE,)
if self._storage is None:
- return '<freed C object %s>' % (self._TYPE,)
+ return '<freed %s>' % (r,)
else:
- return '<C object %s at 0x%x>' % (self._TYPE,
- fixid(self._addressof_storage()))
+ return '<%s at 0x%x>' % (r, fixid(self._addressof_storage()))
def __str__(self):
return repr(self)
@@ -642,6 +664,45 @@ class _struct_mixin(_parentable_mixin):
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):
+ 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):
+ 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."""
__slots__ = ()
@@ -942,7 +1003,8 @@ def ctypes2lltype(T, cobj):
REAL_TYPE = T.TO
if T.TO._arrayfld is not None:
carray = getattr(cobj.contents, T.TO._arrayfld)
- container = lltype._struct(T.TO, carray.length)
+ length = getattr(carray, 'length', 9999) # XXX
+ container = lltype._struct(T.TO, length)
else:
# special treatment of 'OBJECT' subclasses
if get_rtyper() and lltype._castdepth(REAL_TYPE, OBJECT) >= 0:
diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py
index 87256ac849..9368164578 100644
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -453,6 +453,8 @@ LL_OPERATIONS = {
'jit_record_exact_class' : LLOp(canrun=True),
'jit_ffi_save_result': LLOp(canrun=True),
'jit_conditional_call': LLOp(),
+ 'jit_enter_portal_frame': LLOp(canrun=True),
+ 'jit_leave_portal_frame': LLOp(canrun=True),
'get_exception_addr': LLOp(),
'get_exc_value_addr': LLOp(),
'do_malloc_fixedsize':LLOp(canmallocgc=True),
@@ -503,6 +505,12 @@ LL_OPERATIONS = {
'gc_gcflag_extra' : LLOp(),
'gc_add_memory_pressure': LLOp(),
+ 'gc_rawrefcount_init': LLOp(),
+ 'gc_rawrefcount_create_link_pypy': LLOp(),
+ 'gc_rawrefcount_create_link_pyobj': LLOp(),
+ 'gc_rawrefcount_from_obj': LLOp(sideeffects=False),
+ 'gc_rawrefcount_to_obj': LLOp(sideeffects=False),
+
# ------- JIT & GC interaction, only for some GCs ----------
'gc_adr_of_nursery_free' : LLOp(),
diff --git a/rpython/rtyper/lltypesystem/lltype.py b/rpython/rtyper/lltypesystem/lltype.py
index ac95f57e2d..677170a553 100644
--- a/rpython/rtyper/lltypesystem/lltype.py
+++ b/rpython/rtyper/lltypesystem/lltype.py
@@ -1761,7 +1761,10 @@ class _struct(_parentable):
def __new__(self, TYPE, n=None, initialization=None, parent=None,
parentindex=None):
- my_variety = _struct_variety(TYPE._names)
+ if isinstance(TYPE, FixedSizeArray):
+ my_variety = _fixedsizearray
+ else:
+ my_variety = _struct_variety(TYPE._names)
return object.__new__(my_variety)
def __init__(self, TYPE, n=None, initialization=None, parent=None,
@@ -1771,7 +1774,6 @@ class _struct(_parentable):
raise TypeError("%r is not variable-sized" % (TYPE,))
if n is None and TYPE._arrayfld is not None:
raise TypeError("%r is variable-sized" % (TYPE,))
- first, FIRSTTYPE = TYPE._first_struct()
for fld, typ in TYPE._flds.items():
if fld == TYPE._arrayfld:
value = _array(typ, n, initialization=initialization,
@@ -1814,23 +1816,48 @@ class _struct(_parentable):
raise UninitializedMemoryAccess("%r.%s"%(self, field_name))
return r
- # for FixedSizeArray kind of structs:
+
+class _fixedsizearray(_struct):
+ def __init__(self, TYPE, n=None, initialization=None, parent=None,
+ parentindex=None):
+ _parentable.__init__(self, TYPE)
+ if n is not None:
+ raise TypeError("%r is not variable-sized" % (TYPE,))
+ typ = TYPE.OF
+ storage = []
+ for i, fld in enumerate(TYPE._names):
+ value = typ._allocate(initialization=initialization,
+ parent=self, parentindex=fld)
+ storage.append(value)
+ self._items = storage
+ if parent is not None:
+ self._setparentstructure(parent, parentindex)
def getlength(self):
- assert isinstance(self._TYPE, FixedSizeArray)
return self._TYPE.length
def getbounds(self):
return 0, self.getlength()
def getitem(self, index, uninitialized_ok=False):
- assert isinstance(self._TYPE, FixedSizeArray)
- return self._getattr('item%d' % index, uninitialized_ok)
+ assert 0 <= index < self.getlength()
+ return self._items[index]
def setitem(self, index, value):
- assert isinstance(self._TYPE, FixedSizeArray)
- setattr(self, 'item%d' % index, value)
+ assert 0 <= index < self.getlength()
+ self._items[index] = value
+ def __getattr__(self, name):
+ # obscure
+ if name.startswith("item"):
+ return self.getitem(int(name[len('item'):]))
+ return _struct.__getattr__(self, name)
+
+ def __setattr__(self, name, value):
+ if name.startswith("item"):
+ self.setitem(int(name[len('item'):]), value)
+ return
+ _struct.__setattr__(self, name, value)
class _array(_parentable):
_kind = "array"
diff --git a/rpython/rtyper/lltypesystem/module/ll_math.py b/rpython/rtyper/lltypesystem/module/ll_math.py
index 259b709d69..f3bbe4b033 100644
--- a/rpython/rtyper/lltypesystem/module/ll_math.py
+++ b/rpython/rtyper/lltypesystem/module/ll_math.py
@@ -6,8 +6,8 @@ import sys
from rpython.translator import cdir
from rpython.rlib import jit, rposix
from rpython.rlib.rfloat import INFINITY, NAN, isfinite, isinf, isnan
+from rpython.rlib.rposix import UNDERSCORE_ON_WIN32
from rpython.rtyper.lltypesystem import lltype, rffi
-from rpython.rtyper.module.support import UNDERSCORE_ON_WIN32
from rpython.tool.sourcetools import func_with_new_name
from rpython.translator.tool.cbuild import ExternalCompilationInfo
from rpython.translator.platform import platform
diff --git a/rpython/rtyper/lltypesystem/opimpl.py b/rpython/rtyper/lltypesystem/opimpl.py
index dd702f73af..ae7db848a3 100644
--- a/rpython/rtyper/lltypesystem/opimpl.py
+++ b/rpython/rtyper/lltypesystem/opimpl.py
@@ -624,6 +624,12 @@ def op_jit_record_exact_class(x, y):
def op_jit_ffi_save_result(*args):
pass
+def op_jit_enter_portal_frame(x):
+ pass
+
+def op_jit_leave_portal_frame():
+ pass
+
def op_get_group_member(TYPE, grpptr, memberoffset):
from rpython.rtyper.lltypesystem import llgroup
assert isinstance(memberoffset, llgroup.GroupMemberOffset)
diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py
index 0b5a124b06..ec0811d5a3 100644
--- a/rpython/rtyper/lltypesystem/rffi.py
+++ b/rpython/rtyper/lltypesystem/rffi.py
@@ -631,7 +631,8 @@ def COpaquePtr(*args, **kwds):
def CExternVariable(TYPE, name, eci, _CConstantClass=CConstant,
sandboxsafe=False, _nowrapper=False,
- c_type=None, getter_only=False):
+ c_type=None, getter_only=False,
+ declare_as_extern=(sys.platform != 'win32')):
"""Return a pair of functions - a getter and a setter - to access
the given global C variable.
"""
@@ -661,7 +662,7 @@ def CExternVariable(TYPE, name, eci, _CConstantClass=CConstant,
c_setter = "void %(setter_name)s (%(c_type)s v) { %(name)s = v; }" % locals()
lines = ["#include <%s>" % i for i in eci.includes]
- if sys.platform != 'win32':
+ if declare_as_extern:
lines.append('extern %s %s;' % (c_type, name))
lines.append(c_getter)
if not getter_only:
@@ -790,6 +791,12 @@ def make_string_mappings(strtype):
return length
str2chararray._annenforceargs_ = [strtype, None, int]
+ # s[start:start+length] -> already-existing char[],
+ # all characters including zeros
+ def str2rawmem(s, array, start, length):
+ ll_s = llstrtype(s)
+ copy_string_to_raw(ll_s, array, start, length)
+
# char* -> str
# doesn't free char*
def charp2str(cp):
@@ -940,19 +947,19 @@ def make_string_mappings(strtype):
return (str2charp, free_charp, charp2str,
get_nonmovingbuffer, free_nonmovingbuffer,
alloc_buffer, str_from_buffer, keep_buffer_alive_until_here,
- charp2strn, charpsize2str, str2chararray,
+ charp2strn, charpsize2str, str2chararray, str2rawmem,
)
(str2charp, free_charp, charp2str,
get_nonmovingbuffer, free_nonmovingbuffer,
alloc_buffer, str_from_buffer, keep_buffer_alive_until_here,
- charp2strn, charpsize2str, str2chararray,
+ charp2strn, charpsize2str, str2chararray, str2rawmem,
) = make_string_mappings(str)
(unicode2wcharp, free_wcharp, wcharp2unicode,
get_nonmoving_unicodebuffer, free_nonmoving_unicodebuffer,
alloc_unicodebuffer, unicode_from_buffer, keep_unicodebuffer_alive_until_here,
- wcharp2unicoden, wcharpsize2unicode, unicode2wchararray,
+ wcharp2unicoden, wcharpsize2unicode, unicode2wchararray, unicode2rawmem,
) = make_string_mappings(unicode)
# char**
diff --git a/rpython/rtyper/lltypesystem/rstr.py b/rpython/rtyper/lltypesystem/rstr.py
index b99c607e49..6e76ab4526 100644
--- a/rpython/rtyper/lltypesystem/rstr.py
+++ b/rpython/rtyper/lltypesystem/rstr.py
@@ -717,10 +717,7 @@ class LLHelpers(AbstractLLHelpers):
return cls.ll_count_char(s1, s2.chars[0], start, end)
res = cls.ll_search(s1, s2, start, end, FAST_COUNT)
- # For a few cases ll_search can return -1 to indicate an "impossible"
- # condition for a string match, count just returns 0 in these cases.
- if res < 0:
- res = 0
+ assert res >= 0
return res
@staticmethod
@@ -741,6 +738,8 @@ class LLHelpers(AbstractLLHelpers):
w = n - m
if w < 0:
+ if mode == FAST_COUNT:
+ return 0
return -1
mlast = m - 1
diff --git a/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py b/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py
index 4b1e59c884..3a9535925c 100644
--- a/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py
+++ b/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py
@@ -11,12 +11,12 @@ from rpython.rtyper.lltypesystem.ll2ctypes import cast_adr_to_int, get_ctypes_ty
from rpython.rtyper.lltypesystem.ll2ctypes import _llgcopaque
from rpython.rtyper.annlowlevel import llhelper
from rpython.rlib import rposix
+from rpython.rlib.rposix import UNDERSCORE_ON_WIN32
from rpython.translator.tool.cbuild import ExternalCompilationInfo
from rpython.translator import cdir
from rpython.tool.udir import udir
from rpython.rtyper.test.test_llinterp import interpret
from rpython.annotator.annrpython import RPythonAnnotator
-from rpython.rtyper.module.support import UNDERSCORE_ON_WIN32
from rpython.rtyper.rtyper import RPythonTyper
from rpython.rlib.rarithmetic import r_uint, get_long_pattern, is_emulated_long
from rpython.rlib.rarithmetic import is_valid_int
@@ -1466,3 +1466,20 @@ class TestPlatform(object):
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')
diff --git a/rpython/rtyper/module/__init__.py b/rpython/rtyper/module/__init__.py
deleted file mode 100644
index 792d600548..0000000000
--- a/rpython/rtyper/module/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-#
diff --git a/rpython/rtyper/module/test/__init__.py b/rpython/rtyper/module/test/__init__.py
deleted file mode 100644
index 792d600548..0000000000
--- a/rpython/rtyper/module/test/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-#
diff --git a/rpython/rtyper/module/test/test_ll_strtod.py b/rpython/rtyper/module/test/test_ll_strtod.py
deleted file mode 100644
index 4e1788eb69..0000000000
--- a/rpython/rtyper/module/test/test_ll_strtod.py
+++ /dev/null
@@ -1,13 +0,0 @@
-import py
-
-from rpython.rtyper.test.tool import BaseRtypingTest
-from rpython.rlib import rfloat
-
-class TestStrtod(BaseRtypingTest):
- def test_formatd(self):
- for flags in [0,
- rfloat.DTSF_ADD_DOT_0]:
- def f(y):
- return rfloat.formatd(y, 'g', 2, flags)
-
- assert self.ll_to_string(self.interpret(f, [3.0])) == f(3.0)
diff --git a/rpython/rtyper/module/test/test_posix.py b/rpython/rtyper/module/test/test_posix.py
deleted file mode 100644
index df091ed1c4..0000000000
--- a/rpython/rtyper/module/test/test_posix.py
+++ /dev/null
@@ -1,304 +0,0 @@
-import py
-from rpython.rtyper.test.tool import BaseRtypingTest
-from rpython.rtyper.annlowlevel import hlstr
-from rpython.tool.udir import udir
-from rpython.rlib.rarithmetic import is_valid_int
-
-import os
-exec 'import %s as posix' % os.name
-
-def setup_module(module):
- testf = udir.join('test.txt')
- module.path = testf.strpath
-
-class TestPosix(BaseRtypingTest):
-
- def setup_method(self, meth):
- # prepare/restore the file before each test
- testfile = open(path, 'wb')
- testfile.write('This is a test')
- testfile.close()
-
- def test_open(self):
- def f():
- ff = posix.open(path, posix.O_RDONLY, 0777)
- return ff
- func = self.interpret(f, [])
- assert is_valid_int(func)
-
- def test_fstat(self):
- def fo(fi):
- g = posix.fstat(fi)
- return g
- fi = os.open(path,os.O_RDONLY,0777)
- func = self.interpret(fo,[fi])
- stat = os.fstat(fi)
- for i in range(len(stat)):
- assert long(getattr(func, 'item%d' % i)) == stat[i]
-
-
- def test_stat(self):
- def fo():
- g = posix.stat(path)
- return g
- func = self.interpret(fo,[])
- stat = os.stat(path)
- for i in range(len(stat)):
- assert long(getattr(func, 'item%d' % i)) == stat[i]
-
- def test_stat_exception(self):
- def fo():
- try:
- posix.stat('I/do/not/exist')
- except OSError:
- return True
- else:
- return False
- res = self.interpret(fo,[])
- assert res
-
- def test_times(self):
- import py; py.test.skip("llinterp does not like tuple returns")
- from rpython.rtyper.test.test_llinterp import interpret
- times = interpret(lambda: posix.times(), ())
- assert isinstance(times, tuple)
- assert len(times) == 5
- for value in times:
- assert is_valid_int(value)
-
-
- def test_lseek(self):
- def f(fi, pos):
- posix.lseek(fi, pos, 0)
- fi = os.open(path, os.O_RDONLY, 0777)
- func = self.interpret(f, [fi, 5])
- res = os.read(fi, 2)
- assert res =='is'
-
- def test_isatty(self):
- def f(fi):
- posix.isatty(fi)
- fi = os.open(path, os.O_RDONLY, 0777)
- func = self.interpret(f, [fi])
- assert not func
- os.close(fi)
- func = self.interpret(f, [fi])
- assert not func
-
- def test_getcwd(self):
- def f():
- return posix.getcwd()
- res = self.interpret(f,[])
- cwd = os.getcwd()
- #print res.chars,cwd
- assert self.ll_to_string(res) == cwd
-
- def test_write(self):
- def f(fi):
- if fi > 0:
- text = 'This is a test'
- else:
- text = '333'
- return posix.write(fi,text)
- fi = os.open(path,os.O_WRONLY,0777)
- text = 'This is a test'
- func = self.interpret(f,[fi])
- os.close(fi)
- fi = os.open(path,os.O_RDONLY,0777)
- res = os.read(fi,20)
- assert res == text
-
- def test_read(self):
- def f(fi,len):
- return posix.read(fi,len)
- fi = os.open(path,os.O_WRONLY,0777)
- text = 'This is a test'
- os.write(fi,text)
- os.close(fi)
- fi = os.open(path,os.O_RDONLY,0777)
- res = self.interpret(f,[fi,20])
- assert self.ll_to_string(res) == text
-
- if hasattr(os, 'chown'):
- def test_chown(self):
- f = open(path, "w")
- f.write("xyz")
- f.close()
- def f():
- try:
- posix.chown(path, os.getuid(), os.getgid())
- return 1
- except OSError:
- return 2
-
- assert self.interpret(f, []) == 1
- os.unlink(path)
- assert self.interpret(f, []) == 2
-
- def test_close(self):
- def f(fi):
- return posix.close(fi)
- fi = os.open(path,os.O_WRONLY,0777)
- text = 'This is a test'
- os.write(fi,text)
- res = self.interpret(f,[fi])
- py.test.raises( OSError, os.fstat, fi)
-
- if hasattr(os, 'ftruncate'):
- def test_ftruncate(self):
- def f(fi,len):
- os.ftruncate(fi,len)
- fi = os.open(path,os.O_RDWR,0777)
- func = self.interpret(f,[fi,6])
- assert os.fstat(fi).st_size == 6
-
- if hasattr(os, 'getuid'):
- def test_getuid(self):
- def f():
- return os.getuid()
- assert self.interpret(f, []) == f()
-
- if hasattr(os, 'getgid'):
- def test_getgid(self):
- def f():
- return os.getgid()
- assert self.interpret(f, []) == f()
-
- if hasattr(os, 'setuid'):
- def test_os_setuid(self):
- def f():
- os.setuid(os.getuid())
- return os.getuid()
- assert self.interpret(f, []) == f()
-
- if hasattr(os, 'sysconf'):
- def test_os_sysconf(self):
- def f(i):
- return os.sysconf(i)
- assert self.interpret(f, [13]) == f(13)
-
- if hasattr(os, 'confstr'):
- def test_os_confstr(self):
- def f(i):
- try:
- return os.confstr(i)
- except OSError:
- return "oooops!!"
- some_value = os.confstr_names.values()[-1]
- res = self.interpret(f, [some_value])
- assert hlstr(res) == f(some_value)
- res = self.interpret(f, [94781413])
- assert hlstr(res) == "oooops!!"
-
- if hasattr(os, 'pathconf'):
- def test_os_pathconf(self):
- def f(i):
- return os.pathconf("/tmp", i)
- i = os.pathconf_names["PC_NAME_MAX"]
- some_value = self.interpret(f, [i])
- assert some_value >= 31
-
- if hasattr(os, 'chroot'):
- def test_os_chroot(self):
- def f():
- try:
- os.chroot('!@$#!#%$#^#@!#!$$#^')
- except OSError:
- return 1
- return 0
-
- assert self.interpret(f, []) == 1
-
- def test_os_wstar(self):
- from rpython.rlib import rposix
- for name in rposix.WAIT_MACROS:
- if not hasattr(os, name):
- continue
- def fun(s):
- return getattr(os, name)(s)
-
- for value in [0, 1, 127, 128, 255]:
- res = self.interpret(fun, [value])
- assert res == fun(value)
-
- if hasattr(os, 'getgroups'):
- def test_getgroups(self):
- def f():
- return os.getgroups()
- ll_a = self.interpret(f, [])
- assert self.ll_to_list(ll_a) == f()
-
- if hasattr(os, 'setgroups'):
- def test_setgroups(self):
- def f():
- try:
- os.setgroups(os.getgroups())
- except OSError:
- pass
- self.interpret(f, [])
-
- if hasattr(os, 'initgroups'):
- def test_initgroups(self):
- def f():
- try:
- os.initgroups('sUJJeumz', 4321)
- except OSError:
- return 1
- return 0
- res = self.interpret(f, [])
- assert res == 1
-
- if hasattr(os, 'tcgetpgrp'):
- def test_tcgetpgrp(self):
- def f(fd):
- try:
- return os.tcgetpgrp(fd)
- except OSError:
- return 42
- res = self.interpret(f, [9999])
- assert res == 42
-
- if hasattr(os, 'tcsetpgrp'):
- def test_tcsetpgrp(self):
- def f(fd, pgrp):
- try:
- os.tcsetpgrp(fd, pgrp)
- except OSError:
- return 1
- return 0
- res = self.interpret(f, [9999, 1])
- assert res == 1
-
- if hasattr(os, 'getresuid'):
- def test_getresuid(self):
- def f():
- a, b, c = os.getresuid()
- return a + b * 37 + c * 1291
- res = self.interpret(f, [])
- a, b, c = os.getresuid()
- assert res == a + b * 37 + c * 1291
-
- if hasattr(os, 'getresgid'):
- def test_getresgid(self):
- def f():
- a, b, c = os.getresgid()
- return a + b * 37 + c * 1291
- res = self.interpret(f, [])
- a, b, c = os.getresgid()
- assert res == a + b * 37 + c * 1291
-
- if hasattr(os, 'setresuid'):
- def test_setresuid(self):
- def f():
- a, b, c = os.getresuid()
- a = (a + 1) - 1
- os.setresuid(a, b, c)
- self.interpret(f, [])
-
- if hasattr(os, 'setresgid'):
- def test_setresgid(self):
- def f():
- a, b, c = os.getresgid()
- a = (a + 1) - 1
- os.setresgid(a, b, c)
- self.interpret(f, [])
diff --git a/rpython/rtyper/rtyper.py b/rpython/rtyper/rtyper.py
index 7734f74554..dfe867ad00 100644
--- a/rpython/rtyper/rtyper.py
+++ b/rpython/rtyper/rtyper.py
@@ -32,11 +32,24 @@ from rpython.translator.unsimplify import insert_empty_block
from rpython.translator.sandbox.rsandbox import make_sandbox_trampoline
+class RTyperBackend(object):
+ pass
+
+class GenCBackend(RTyperBackend):
+ pass
+genc_backend = GenCBackend()
+
+class LLInterpBackend(RTyperBackend):
+ pass
+llinterp_backend = LLInterpBackend()
+
+
class RPythonTyper(object):
from rpython.rtyper.rmodel import log
- def __init__(self, annotator):
+ def __init__(self, annotator, backend=genc_backend):
self.annotator = annotator
+ self.backend = backend
self.lowlevel_ann_policy = LowLevelAnnotatorPolicy(self)
self.reprs = {}
self._reprs_must_call_setup = []
diff --git a/rpython/rtyper/test/test_extfunc.py b/rpython/rtyper/test/test_extfunc.py
index c6b3948a9a..778be158f7 100644
--- a/rpython/rtyper/test/test_extfunc.py
+++ b/rpython/rtyper/test/test_extfunc.py
@@ -1,7 +1,6 @@
import py
-from rpython.rtyper.extfunc import ExtFuncEntry, register_external,\
- is_external
+from rpython.rtyper.extfunc import register_external
from rpython.annotator.model import SomeInteger, SomeString, AnnotatorError
from rpython.annotator.annrpython import RPythonAnnotator
from rpython.annotator.policy import AnnotatorPolicy
@@ -19,11 +18,7 @@ class TestExtFuncEntry:
"NOT_RPYTHON"
return eval("x+40")
- class BTestFuncEntry(ExtFuncEntry):
- _about_ = b
- name = 'b'
- signature_args = [SomeInteger()]
- signature_result = SomeInteger()
+ register_external(b, [int], result=int)
def f():
return b(2)
@@ -43,15 +38,11 @@ class TestExtFuncEntry:
def c(y, x):
yyy
- class CTestFuncEntry(ExtFuncEntry):
- _about_ = c
- name = 'ccc'
- signature_args = [SomeInteger()] * 2
- signature_result = SomeInteger()
+ def llimpl(y, x):
+ return y + x
- def lltypeimpl(y, x):
- return y + x
- lltypeimpl = staticmethod(lltypeimpl)
+ register_external(c, [int, int], result=int, llimpl=llimpl,
+ export_name='ccc')
def f():
return c(3, 4)
@@ -59,22 +50,6 @@ class TestExtFuncEntry:
res = interpret(f, [])
assert res == 7
- def test_register_external_signature(self):
- """
- Test the standard interface for external functions.
- """
- def dd():
- pass
- register_external(dd, [int], int)
-
- def f():
- return dd(3)
-
- policy = AnnotatorPolicy()
- a = RPythonAnnotator(policy=policy)
- s = a.build_types(f, [])
- assert isinstance(s, SomeInteger)
-
def test_register_external_tuple_args(self):
"""
Verify the annotation of a registered external function which takes a
@@ -121,23 +96,6 @@ class TestExtFuncEntry:
s = a.build_types(f, [])
assert isinstance(s, SomeInteger)
- def test_register_external_specialcase(self):
- """
- When args=None, the external function accepts any arguments unmodified.
- """
- def function_withspecialcase(arg):
- return repr(arg)
- register_external(function_withspecialcase, args=None, result=str)
-
- def f():
- x = function_withspecialcase
- return x(33) + x("aaa") + x([]) + "\n"
-
- policy = AnnotatorPolicy()
- a = RPythonAnnotator(policy=policy)
- s = a.build_types(f, [])
- assert isinstance(s, SomeString)
-
def test_str0(self):
str0 = SomeString(no_nul=True)
def os_open(s):
@@ -182,3 +140,22 @@ class TestExtFuncEntry:
# fails with TooLateForChange
a.build_types(g, [[str]])
a.build_types(g, [[str0]]) # Does not raise
+
+ def test_register_external_llfakeimpl(self):
+ def a(i):
+ return i
+ def a_llimpl(i):
+ return i * 2
+ def a_llfakeimpl(i):
+ return i * 3
+ register_external(a, [int], int, llimpl=a_llimpl,
+ llfakeimpl=a_llfakeimpl)
+ def f(i):
+ return a(i)
+
+ res = interpret(f, [7])
+ assert res == 21
+
+ from rpython.translator.c.test.test_genc import compile
+ fc = compile(f, [int])
+ assert fc(7) == 14
diff --git a/rpython/rtyper/test/test_llinterp.py b/rpython/rtyper/test/test_llinterp.py
index 9c8796ac6c..479a1de0a9 100644
--- a/rpython/rtyper/test/test_llinterp.py
+++ b/rpython/rtyper/test/test_llinterp.py
@@ -13,7 +13,7 @@ from rpython.rtyper.llannotation import lltype_to_annotation
from rpython.rlib.rarithmetic import r_uint, ovfcheck
from rpython.tool import leakfinder
from rpython.conftest import option
-
+from rpython.rtyper.rtyper import llinterp_backend
# switch on logging of interp to show more info on failing tests
@@ -39,6 +39,7 @@ def gengraph(func, argtypes=[], viewbefore='auto', policy=None,
t.view()
global typer # we need it for find_exception
typer = t.buildrtyper()
+ typer.backend = llinterp_backend
typer.specialize()
#t.view()
t.checkgraphs()
diff --git a/rpython/rtyper/test/test_rbuiltin.py b/rpython/rtyper/test/test_rbuiltin.py
index 5531dcaedc..767fd1d7ed 100644
--- a/rpython/rtyper/test/test_rbuiltin.py
+++ b/rpython/rtyper/test/test_rbuiltin.py
@@ -3,8 +3,7 @@ import os
import py
-from rpython.rlib.debug import llinterpcall
-from rpython.rlib.objectmodel import instantiate, running_on_llinterp, compute_unique_id, current_object_addr_as_int
+from rpython.rlib.objectmodel import instantiate, compute_unique_id, current_object_addr_as_int
from rpython.rlib.rarithmetic import (intmask, longlongmask, r_int64, is_valid_int,
r_int, r_uint, r_longlong, r_ulonglong)
from rpython.rlib.rstring import StringBuilder, UnicodeBuilder
@@ -456,26 +455,6 @@ class TestRbuiltin(BaseRtypingTest):
res = self.interpret(fn, [3.25])
assert res == 7.25
- def test_debug_llinterpcall(self):
- S = lltype.Struct('S', ('m', lltype.Signed))
- SPTR = lltype.Ptr(S)
- def foo(n):
- "NOT_RPYTHON"
- s = lltype.malloc(S, immortal=True)
- s.m = eval("n*6", locals())
- return s
- def fn(n):
- if running_on_llinterp:
- return llinterpcall(SPTR, foo, n).m
- else:
- return 321
- res = self.interpret(fn, [7])
- assert res == 42
- from rpython.translator.c.test.test_genc import compile
- f = compile(fn, [int])
- res = f(7)
- assert res == 321
-
def test_id(self):
class A:
pass
diff --git a/rpython/rtyper/test/test_rdict.py b/rpython/rtyper/test/test_rdict.py
index f71aaf8174..e9a8b1c718 100644
--- a/rpython/rtyper/test/test_rdict.py
+++ b/rpython/rtyper/test/test_rdict.py
@@ -1,13 +1,63 @@
+import sys
+from contextlib import contextmanager
+import signal
+
from rpython.translator.translator import TranslationContext
+from rpython.annotator.model import (
+ SomeInteger, SomeString, SomeChar, SomeUnicodeString, SomeUnicodeCodePoint)
+from rpython.annotator.dictdef import DictKey, DictValue
from rpython.rtyper.lltypesystem import lltype, rffi
-from rpython.rtyper import rint
-from rpython.rtyper.lltypesystem import rdict, rstr
+from rpython.rtyper.lltypesystem import rdict
from rpython.rtyper.test.tool import BaseRtypingTest
from rpython.rlib.objectmodel import r_dict
from rpython.rlib.rarithmetic import r_int, r_uint, r_longlong, r_ulonglong
import py
-py.log.setconsumer("rtyper", py.log.STDOUT)
+from hypothesis import settings
+from hypothesis.strategies import (
+ builds, sampled_from, binary, just, integers, text, characters, tuples)
+from hypothesis.stateful import GenericStateMachine, run_state_machine_as_test
+
+def ann2strategy(s_value):
+ if isinstance(s_value, SomeChar):
+ return builds(chr, integers(min_value=0, max_value=255))
+ elif isinstance(s_value, SomeString):
+ if s_value.can_be_None:
+ return binary() | just(None)
+ else:
+ return binary()
+ elif isinstance(s_value, SomeUnicodeCodePoint):
+ return characters()
+ elif isinstance(s_value, SomeUnicodeString):
+ if s_value.can_be_None:
+ return text() | just(None)
+ else:
+ return text()
+ elif isinstance(s_value, SomeInteger):
+ return integers(min_value=~sys.maxint, max_value=sys.maxint)
+ else:
+ raise TypeError("Cannot convert annotation %s to a strategy" % s_value)
+
+
+if hasattr(signal, 'alarm'):
+ @contextmanager
+ def signal_timeout(n):
+ """A flaky context manager that throws an exception if the body of the
+ `with` block runs for longer than `n` seconds.
+ """
+ def handler(signum, frame):
+ raise RuntimeError('timeout')
+ signal.signal(signal.SIGALRM, handler)
+ signal.alarm(n)
+ try:
+ yield
+ finally:
+ signal.alarm(0)
+else:
+ @contextmanager
+ def signal_timeout(n):
+ yield
+
def not_really_random():
"""A random-ish generator, which also generates nice patterns from time to time.
@@ -199,9 +249,8 @@ class BaseTestRDict(BaseRtypingTest):
def test_dict_copy(self):
def func():
- # XXX this does not work if we use chars, only!
dic = self.newdict()
- dic['ab'] = 1
+ dic['a'] = 1
dic['b'] = 2
d2 = dic.copy()
ok = 1
@@ -1004,28 +1053,6 @@ class TestRDict(BaseTestRDict):
assert r_AB_dic.lowleveltype == r_BA_dic.lowleveltype
- def test_dict_resize(self):
- py.test.skip("test written for non-ordered dicts, update or kill")
- # XXX we no longer automatically resize on 'del'. We need to
- # hack a bit in this test to trigger a resize by continuing to
- # fill the dict's table while keeping the actual size very low
- # in order to force a resize to shrink the table back
- def func(want_empty):
- d = self.newdict()
- for i in range(rdict.DICT_INITSIZE << 1):
- d[chr(ord('a') + i)] = i
- if want_empty:
- for i in range(rdict.DICT_INITSIZE << 1):
- del d[chr(ord('a') + i)]
- for i in range(rdict.DICT_INITSIZE << 3):
- d[chr(ord('A') - i)] = i
- del d[chr(ord('A') - i)]
- return d
- res = self.interpret(func, [0])
- assert len(res.entries) > rdict.DICT_INITSIZE
- res = self.interpret(func, [1])
- assert len(res.entries) == rdict.DICT_INITSIZE
-
def test_opt_dummykeymarker(self):
def f():
d = {"hello": None}
@@ -1117,183 +1144,111 @@ class TestRDict(BaseTestRDict):
DICT = lltype.typeOf(llres.item1)
assert sorted(DICT.TO.entries.TO.OF._flds) == ['f_hash', 'key', 'value']
- def test_deleted_entry_reusage_with_colliding_hashes(self):
- py.test.skip("test written for non-ordered dicts, update or kill")
- def lowlevelhash(value):
- p = rstr.mallocstr(len(value))
- for i in range(len(value)):
- p.chars[i] = value[i]
- return rstr.LLHelpers.ll_strhash(p)
-
- def func(c1, c2):
- c1 = chr(c1)
- c2 = chr(c2)
- d = self.newdict()
- d[c1] = 1
- d[c2] = 2
- del d[c1]
- return d[c2]
-
- char_by_hash = {}
- base = rdict.DICT_INITSIZE
- for y in range(0, 256):
- y = chr(y)
- y_hash = lowlevelhash(y) % base
- char_by_hash.setdefault(y_hash, []).append(y)
-
- x, y = char_by_hash[0][:2] # find a collision
-
- res = self.interpret(func, [ord(x), ord(y)])
- assert res == 2
-
- def func2(c1, c2):
- c1 = chr(c1)
- c2 = chr(c2)
- d = self.newdict()
- d[c1] = 1
- d[c2] = 2
- del d[c1]
- d[c1] = 3
- return d
-
- res = self.interpret(func2, [ord(x), ord(y)])
- for i in range(len(res.entries)):
- assert not (res.entries.everused(i) and not res.entries.valid(i))
- def func3(c0, c1, c2, c3, c4, c5, c6, c7):
- d = self.newdict()
- c0 = chr(c0) ; d[c0] = 1; del d[c0]
- c1 = chr(c1) ; d[c1] = 1; del d[c1]
- c2 = chr(c2) ; d[c2] = 1; del d[c2]
- c3 = chr(c3) ; d[c3] = 1; del d[c3]
- c4 = chr(c4) ; d[c4] = 1; del d[c4]
- c5 = chr(c5) ; d[c5] = 1; del d[c5]
- c6 = chr(c6) ; d[c6] = 1; del d[c6]
- c7 = chr(c7) ; d[c7] = 1; del d[c7]
- return d
-
- if rdict.DICT_INITSIZE != 8:
- py.test.skip("make dict tests more indepdent from initsize")
- res = self.interpret(func3, [ord(char_by_hash[i][0])
- for i in range(rdict.DICT_INITSIZE)])
- count_frees = 0
- for i in range(len(res.entries)):
- if not res.entries.everused(i):
- count_frees += 1
- assert count_frees >= 3
-
-class TestStress:
-
- def test_stress(self):
- from rpython.annotator.dictdef import DictKey, DictValue
- from rpython.annotator import model as annmodel
- dictrepr = rdict.DictRepr(None, rint.signed_repr, rint.signed_repr,
- DictKey(None, annmodel.SomeInteger()),
- DictValue(None, annmodel.SomeInteger()))
+class Action(object):
+ def __init__(self, method, args):
+ self.method = method
+ self.args = args
+
+ def execute(self, space):
+ getattr(space, self.method)(*self.args)
+
+ def __repr__(self):
+ return "space.%s(%s)" % (self.method, ', '.join(map(repr, self.args)))
+
+class PseudoRTyper:
+ cache_dummy_values = {}
+
+# XXX: None keys crash the test, but translation sort-of allows it
+keytypes_s = [
+ SomeString(), SomeInteger(), SomeChar(),
+ SomeUnicodeString(), SomeUnicodeCodePoint()]
+st_keys = sampled_from(keytypes_s)
+st_values = sampled_from(keytypes_s + [SomeString(can_be_None=True)])
+
+class Space(object):
+ def __init__(self, s_key, s_value):
+ self.s_key = s_key
+ self.s_value = s_value
+ rtyper = PseudoRTyper()
+ r_key = s_key.rtyper_makerepr(rtyper)
+ r_value = s_value.rtyper_makerepr(rtyper)
+ dictrepr = rdict.DictRepr(rtyper, r_key, r_value,
+ DictKey(None, s_key),
+ DictValue(None, s_value))
dictrepr.setup()
- l_dict = rdict.ll_newdict(dictrepr.DICT)
- referencetable = [None] * 400
- referencelength = 0
- value = 0
-
- def complete_check():
- for n, refvalue in zip(range(len(referencetable)), referencetable):
- try:
- gotvalue = rdict.ll_dict_getitem(l_dict, n)
- except KeyError:
- assert refvalue is None
- else:
- assert gotvalue == refvalue
-
- for x in not_really_random():
- n = int(x*100.0) # 0 <= x < 400
- op = repr(x)[-1]
- if op <= '2' and referencetable[n] is not None:
- rdict.ll_dict_delitem(l_dict, n)
- referencetable[n] = None
- referencelength -= 1
- elif op <= '6':
- rdict.ll_dict_setitem(l_dict, n, value)
- if referencetable[n] is None:
- referencelength += 1
- referencetable[n] = value
- value += 1
- else:
- try:
- gotvalue = rdict.ll_dict_getitem(l_dict, n)
- except KeyError:
- assert referencetable[n] is None
- else:
- assert gotvalue == referencetable[n]
- if 1.38 <= x <= 1.39:
- complete_check()
- print 'current dict length:', referencelength
- assert l_dict.num_items == referencelength
- complete_check()
-
- def test_stress_2(self):
- yield self.stress_combination, True, False
- yield self.stress_combination, False, True
- yield self.stress_combination, False, False
- yield self.stress_combination, True, True
-
- def stress_combination(self, key_can_be_none, value_can_be_none):
- from rpython.rtyper.lltypesystem.rstr import string_repr
- from rpython.annotator.dictdef import DictKey, DictValue
- from rpython.annotator import model as annmodel
-
- print
- print "Testing combination with can_be_None: keys %s, values %s" % (
- key_can_be_none, value_can_be_none)
-
- class PseudoRTyper:
- cache_dummy_values = {}
- dictrepr = rdict.DictRepr(PseudoRTyper(), string_repr, string_repr,
- DictKey(None, annmodel.SomeString(key_can_be_none)),
- DictValue(None, annmodel.SomeString(value_can_be_none)))
- dictrepr.setup()
- print dictrepr.lowleveltype
- for key, value in dictrepr.DICTENTRY._adtmeths.items():
- print ' %s = %s' % (key, value)
- l_dict = rdict.ll_newdict(dictrepr.DICT)
- referencetable = [None] * 400
- referencelength = 0
- values = not_really_random()
- keytable = [string_repr.convert_const("foo%d" % n)
- for n in range(len(referencetable))]
-
- def complete_check():
- for n, refvalue in zip(range(len(referencetable)), referencetable):
- try:
- gotvalue = rdict.ll_dict_getitem(l_dict, keytable[n])
- except KeyError:
- assert refvalue is None
- else:
- assert gotvalue == refvalue
-
- for x in not_really_random():
- n = int(x*100.0) # 0 <= x < 400
- op = repr(x)[-1]
- if op <= '2' and referencetable[n] is not None:
- rdict.ll_dict_delitem(l_dict, keytable[n])
- referencetable[n] = None
- referencelength -= 1
- elif op <= '6':
- ll_value = string_repr.convert_const(str(values.next()))
- rdict.ll_dict_setitem(l_dict, keytable[n], ll_value)
- if referencetable[n] is None:
- referencelength += 1
- referencetable[n] = ll_value
- else:
- try:
- gotvalue = rdict.ll_dict_getitem(l_dict, keytable[n])
- except KeyError:
- assert referencetable[n] is None
- else:
- assert gotvalue == referencetable[n]
- if 1.38 <= x <= 1.39:
- complete_check()
- print 'current dict length:', referencelength
- assert l_dict.num_items == referencelength
- complete_check()
-
+ self.l_dict = rdict.ll_newdict(dictrepr.DICT)
+ self.reference = {}
+ self.ll_key = r_key.convert_const
+ self.ll_value = r_value.convert_const
+
+ def setitem(self, key, value):
+ ll_key = self.ll_key(key)
+ ll_value = self.ll_value(value)
+ rdict.ll_dict_setitem(self.l_dict, ll_key, ll_value)
+ self.reference[key] = value
+ assert rdict.ll_contains(self.l_dict, ll_key)
+
+ def delitem(self, key):
+ ll_key = self.ll_key(key)
+ rdict.ll_dict_delitem(self.l_dict, ll_key)
+ del self.reference[key]
+ assert not rdict.ll_contains(self.l_dict, ll_key)
+
+ def copydict(self):
+ self.l_dict = rdict.ll_copy(self.l_dict)
+
+ def cleardict(self):
+ rdict.ll_clear(self.l_dict)
+ self.reference.clear()
+ assert rdict.ll_dict_len(self.l_dict) == 0
+
+ def fullcheck(self):
+ assert rdict.ll_dict_len(self.l_dict) == len(self.reference)
+ for key, value in self.reference.iteritems():
+ assert (rdict.ll_dict_getitem(self.l_dict, self.ll_key(key)) ==
+ self.ll_value(value))
+
+class StressTest(GenericStateMachine):
+ def __init__(self):
+ self.space = None
+
+ def st_setitem(self):
+ return builds(Action,
+ just('setitem'), tuples(self.st_keys, self.st_values))
+
+ def st_updateitem(self):
+ return builds(Action,
+ just('setitem'),
+ tuples(sampled_from(self.space.reference), self.st_values))
+
+ def st_delitem(self):
+ return builds(Action,
+ just('delitem'), tuples(sampled_from(self.space.reference)))
+
+ def steps(self):
+ if not self.space:
+ return builds(Action, just('setup'), tuples(st_keys, st_values))
+ global_actions = [Action('copydict', ()), Action('cleardict', ())]
+ if self.space.reference:
+ return (
+ self.st_setitem() | sampled_from(global_actions) |
+ self.st_updateitem() | self.st_delitem())
+ else:
+ return (self.st_setitem() | sampled_from(global_actions))
+
+ def execute_step(self, action):
+ if action.method == 'setup':
+ self.space = Space(*action.args)
+ self.st_keys = ann2strategy(self.space.s_key)
+ self.st_values = ann2strategy(self.space.s_value)
+ return
+ with signal_timeout(1): # catches infinite loops
+ action.execute(self.space)
+
+ def teardown(self):
+ if self.space:
+ self.space.fullcheck()
+
+def test_hypothesis():
+ run_state_machine_as_test(StressTest, settings(max_examples=500, stateful_step_count=100))
diff --git a/rpython/rtyper/test/test_rfloat.py b/rpython/rtyper/test/test_rfloat.py
index e563d797c4..c87f3a4599 100644
--- a/rpython/rtyper/test/test_rfloat.py
+++ b/rpython/rtyper/test/test_rfloat.py
@@ -204,6 +204,14 @@ class TestRfloat(BaseRtypingTest):
res = self.ll_to_string(self.interpret(f, [10/3.0]))
assert res == '3.33'
+ def test_formatd_g(self):
+ from rpython.rlib import rfloat
+ for flags in [0, rfloat.DTSF_ADD_DOT_0]:
+ def f(y):
+ return rfloat.formatd(y, 'g', 2, flags)
+
+ assert self.ll_to_string(self.interpret(f, [3.0])) == f(3.0)
+
def test_formatd_repr(self):
from rpython.rlib.rfloat import formatd
def f(x):
diff --git a/rpython/rtyper/test/test_rpbc.py b/rpython/rtyper/test/test_rpbc.py
index 0c80338eb5..0123ca3ef8 100644
--- a/rpython/rtyper/test/test_rpbc.py
+++ b/rpython/rtyper/test/test_rpbc.py
@@ -1,7 +1,7 @@
import py
from rpython.annotator import model as annmodel
-from rpython.annotator import policy, specialize
+from rpython.annotator import specialize
from rpython.rtyper.lltypesystem.lltype import typeOf
from rpython.rtyper.test.tool import BaseRtypingTest
from rpython.rtyper.llannotation import SomePtr, lltype_to_annotation
@@ -1690,59 +1690,6 @@ class TestRPBC(BaseRtypingTest):
# ____________________________________________________________
-class TestRPBCExtra(BaseRtypingTest):
-
- def test_folding_specialize_support(self):
-
- class S(object):
-
- def w(s, x):
- if isinstance(x, int):
- return x
- if isinstance(x, str):
- return len(x)
- return -1
- w._annspecialcase_ = "specialize:w"
-
- def _freeze_(self):
- return True
-
- s = S()
-
- def f(i, n):
- w = s.w
- if i == 0:
- return w(0)
- elif i == 1:
- return w("abc")
- elif i == 2:
- return w(3*n)
- elif i == 3:
- return w(str(n))
- return -1
-
- class P(policy.AnnotatorPolicy):
- def specialize__w(pol, funcdesc, args_s):
- typ = args_s[1].knowntype
- if args_s[0].is_constant() and args_s[1].is_constant():
- x = args_s[1].const
- v = s.w(x)
- builder = specialize.make_constgraphbuilder(2, v)
- return funcdesc.cachedgraph(x, builder=builder)
- return funcdesc.cachedgraph(typ)
-
- p = P()
-
- res = self.interpret(f, [0, 66], policy=p)
- assert res == 0
- res = self.interpret(f, [1, 66], policy=p)
- assert res == 3
- res = self.interpret(f, [2, 4], policy=p)
- assert res == 12
- res = self.interpret(f, [3, 5555], policy=p)
- assert res == 4
-
-
def test_hlinvoke_simple():
def f(a,b):
return a + b
@@ -1998,7 +1945,7 @@ class TestSmallFuncSets(TestRPBC):
def interpret(self, fn, args, **kwds):
kwds['config'] = self.config
- return TestRPBC.interpret(self, fn, args, **kwds)
+ return TestRPBC.interpret(fn, args, **kwds)
def test_smallfuncsets_basic():
from rpython.translator.translator import TranslationContext, graphof
diff --git a/rpython/rtyper/test/tool.py b/rpython/rtyper/test/tool.py
index d565f1ecc7..079abaa9c7 100644
--- a/rpython/rtyper/test/tool.py
+++ b/rpython/rtyper/test/tool.py
@@ -5,22 +5,27 @@ from rpython.rtyper.test.test_llinterp import gengraph, interpret, interpret_rai
class BaseRtypingTest(object):
FLOAT_PRECISION = 8
- def gengraph(self, func, argtypes=[], viewbefore='auto', policy=None,
+ @staticmethod
+ def gengraph(func, argtypes=[], viewbefore='auto', policy=None,
backendopt=False, config=None):
return gengraph(func, argtypes, viewbefore, policy,
backendopt=backendopt, config=config)
- def interpret(self, fn, args, **kwds):
+ @staticmethod
+ def interpret(fn, args, **kwds):
return interpret(fn, args, **kwds)
- def interpret_raises(self, exc, fn, args, **kwds):
+ @staticmethod
+ def interpret_raises(exc, fn, args, **kwds):
return interpret_raises(exc, fn, args, **kwds)
- def float_eq(self, x, y):
+ @staticmethod
+ def float_eq(x, y):
return x == y
- def float_eq_approx(self, x, y):
- maxError = 10**-self.FLOAT_PRECISION
+ @classmethod
+ def float_eq_approx(cls, x, y):
+ maxError = 10**-cls.FLOAT_PRECISION
if abs(x-y) < maxError:
return True
@@ -31,45 +36,66 @@ class BaseRtypingTest(object):
return relativeError < maxError
- def is_of_type(self, x, type_):
+ @staticmethod
+ def is_of_type(x, type_):
return type(x) is type_
- def _skip_llinterpreter(self, reason):
+ @staticmethod
+ def _skip_llinterpreter(reason):
py.test.skip("lltypesystem doesn't support %s, yet" % reason)
- def ll_to_string(self, s):
+ @staticmethod
+ def ll_to_string(s):
if not s:
return None
return ''.join(s.chars)
- def ll_to_unicode(self, s):
+ @staticmethod
+ def ll_to_unicode(s):
return u''.join(s.chars)
- def string_to_ll(self, s):
- from rpython.rtyper.module.support import LLSupport
- return LLSupport.to_rstr(s)
-
- def unicode_to_ll(self, s):
- from rpython.rtyper.module.support import LLSupport
- return LLSupport.to_runicode(s)
-
- def ll_to_list(self, l):
+ @staticmethod
+ def string_to_ll(s):
+ from rpython.rtyper.lltypesystem.rstr import STR, mallocstr
+ if s is None:
+ return lltype.nullptr(STR)
+ p = mallocstr(len(s))
+ for i in range(len(s)):
+ p.chars[i] = s[i]
+ return p
+
+ @staticmethod
+ def unicode_to_ll(s):
+ from rpython.rtyper.lltypesystem.rstr import UNICODE, mallocunicode
+ if s is None:
+ return lltype.nullptr(UNICODE)
+ p = mallocunicode(len(s))
+ for i in range(len(s)):
+ p.chars[i] = s[i]
+ return p
+
+ @staticmethod
+ def ll_to_list(l):
r = []
items = l.ll_items()
for i in range(l.ll_length()):
r.append(items[i])
return r
- def ll_unpack_tuple(self, t, length):
+ @staticmethod
+ def ll_unpack_tuple(t, length):
return tuple([getattr(t, 'item%d' % i) for i in range(length)])
- def get_callable(self, fnptr):
+ @staticmethod
+ def get_callable(fnptr):
return fnptr._obj._callable
- def class_name(self, value):
+ @staticmethod
+ def class_name(value):
return ''.join(value.super.typeptr.name.chars)
- def read_attr(self, value, attr_name):
+ @staticmethod
+ def read_attr(value, attr_name):
value = value._obj
while value is not None:
attr = getattr(value, "inst_" + attr_name, None)
@@ -79,6 +105,7 @@ class BaseRtypingTest(object):
return attr
raise AttributeError()
- def is_of_instance_type(self, val):
+ @staticmethod
+ def is_of_instance_type(val):
T = lltype.typeOf(val)
return isinstance(T, lltype.Ptr) and isinstance(T.TO, lltype.GcStruct)
diff --git a/rpython/rtyper/tool/rffi_platform.py b/rpython/rtyper/tool/rffi_platform.py
index 0366c88454..eade70e2bd 100755
--- a/rpython/rtyper/tool/rffi_platform.py
+++ b/rpython/rtyper/tool/rffi_platform.py
@@ -263,10 +263,11 @@ class Struct(CConfigEntry):
"""An entry in a CConfig class that stands for an externally
defined structure.
"""
- def __init__(self, name, interesting_fields, ifdef=None):
+ def __init__(self, name, interesting_fields, ifdef=None, adtmeths={}):
self.name = name
self.interesting_fields = interesting_fields
self.ifdef = ifdef
+ self.adtmeths = adtmeths
def prepare_code(self):
if self.ifdef is not None:
@@ -313,7 +314,9 @@ class Struct(CConfigEntry):
offset = info['fldofs ' + fieldname]
size = info['fldsize ' + fieldname]
sign = info.get('fldunsigned ' + fieldname, False)
- if (size, sign) != rffi.size_and_sign(fieldtype):
+ if is_array_nolength(fieldtype):
+ pass # ignore size and sign
+ elif (size, sign) != rffi.size_and_sign(fieldtype):
fieldtype = fixup_ctype(fieldtype, fieldname, (size, sign))
layout_addfield(layout, offset, fieldtype, fieldname)
@@ -353,7 +356,7 @@ class Struct(CConfigEntry):
name = name[7:]
else:
hints['typedef'] = True
- kwds = {'hints': hints}
+ kwds = {'hints': hints, 'adtmeths': self.adtmeths}
return rffi.CStruct(name, *fields, **kwds)
class SimpleType(CConfigEntry):
@@ -682,8 +685,14 @@ class Field(object):
def __repr__(self):
return '<field %s: %s>' % (self.name, self.ctype)
+def is_array_nolength(TYPE):
+ return isinstance(TYPE, lltype.Array) and TYPE._hints.get('nolength', False)
+
def layout_addfield(layout, offset, ctype, prefix):
- size = _sizeof(ctype)
+ if is_array_nolength(ctype):
+ size = len(layout) - offset # all the rest of the struct
+ else:
+ size = _sizeof(ctype)
name = prefix
i = 0
while name in layout:
diff --git a/rpython/rtyper/tool/test/test_rffi_platform.py b/rpython/rtyper/tool/test/test_rffi_platform.py
index 3945167464..bf7db60286 100644
--- a/rpython/rtyper/tool/test/test_rffi_platform.py
+++ b/rpython/rtyper/tool/test/test_rffi_platform.py
@@ -270,6 +270,19 @@ def test_array():
[("d_name", lltype.FixedSizeArray(rffi.CHAR, 1))])
assert dirent.c_d_name.length == 32
+def test_array_varsized_struct():
+ dirent = rffi_platform.getstruct("struct dirent",
+ """
+ struct dirent /* for this example only, not the exact dirent */
+ {
+ int d_off;
+ char d_name[1];
+ };
+ """,
+ [("d_name", rffi.CArray(rffi.CHAR))])
+ assert rffi.offsetof(dirent, 'c_d_name') == 4
+ assert dirent.c_d_name == rffi.CArray(rffi.CHAR)
+
def test_has_0001():
assert rffi_platform.has("x", "int x = 3;")
assert not rffi_platform.has("x", "")
diff --git a/rpython/translator/c/funcgen.py b/rpython/translator/c/funcgen.py
index dc13fcdc10..1015118990 100644
--- a/rpython/translator/c/funcgen.py
+++ b/rpython/translator/c/funcgen.py
@@ -842,6 +842,12 @@ class FunctionCodeGenerator(object):
def OP_JIT_FFI_SAVE_RESULT(self, op):
return '/* JIT_FFI_SAVE_RESULT %s */' % op
+ def OP_JIT_ENTER_PORTAL_FRAME(self, op):
+ return '/* JIT_ENTER_PORTAL_FRAME %s */' % op
+
+ def OP_JIT_LEAVE_PORTAL_FRAME(self, op):
+ return '/* JIT_LEAVE_PORTAL_FRAME %s */' % op
+
def OP_GET_GROUP_MEMBER(self, op):
typename = self.db.gettype(op.result.concretetype)
return '%s = (%s)_OP_GET_GROUP_MEMBER(%s, %s);' % (
diff --git a/rpython/translator/c/node.py b/rpython/translator/c/node.py
index c8bd2a2f05..dd604b86e3 100644
--- a/rpython/translator/c/node.py
+++ b/rpython/translator/c/node.py
@@ -546,7 +546,7 @@ class StructNode(ContainerNode):
if needs_gcheader(T):
gct = self.db.gctransformer
if gct is not None:
- self.gc_init = gct.gcheader_initdata(self)
+ self.gc_init = gct.gcheader_initdata(self.obj)
db.getcontainernode(self.gc_init)
else:
self.gc_init = None
@@ -677,7 +677,7 @@ class ArrayNode(ContainerNode):
if needs_gcheader(T):
gct = self.db.gctransformer
if gct is not None:
- self.gc_init = gct.gcheader_initdata(self)
+ self.gc_init = gct.gcheader_initdata(self.obj)
db.getcontainernode(self.gc_init)
else:
self.gc_init = None
@@ -913,6 +913,7 @@ class ExternalFuncNode(FuncNodeBase):
return []
def new_funcnode(db, T, obj, forcename=None):
+ from rpython.rtyper.rtyper import llinterp_backend
if db.sandbox:
if (getattr(obj, 'external', None) is not None and
not obj._safe_not_sandboxed):
@@ -934,6 +935,9 @@ def new_funcnode(db, T, obj, forcename=None):
return ExternalFuncNode(db, T, obj, name)
elif hasattr(obj._callable, "c_name"):
return ExternalFuncNode(db, T, obj, name) # this case should only be used for entrypoints
+ elif db.translator.rtyper.backend is llinterp_backend:
+ # on llinterp, anything goes
+ return ExternalFuncNode(db, T, obj, name)
else:
raise ValueError("don't know how to generate code for %r" % (obj,))
diff --git a/rpython/translator/sandbox/test/test_sandbox.py b/rpython/translator/sandbox/test/test_sandbox.py
index 58e70667d8..ed46da595f 100644
--- a/rpython/translator/sandbox/test/test_sandbox.py
+++ b/rpython/translator/sandbox/test/test_sandbox.py
@@ -292,6 +292,21 @@ def test_unsafe_mmap():
rescode = pipe.wait()
assert rescode == 0
+def test_environ_items():
+ def entry_point(argv):
+ print os.environ.items()
+ return 0
+
+ exe = compile(entry_point)
+ g, f = run_in_subprocess(exe)
+ expect(f, g, "ll_os.ll_os_envitems", (), [])
+ expect(f, g, "ll_os.ll_os_write", (1, "[]\n"), 3)
+ g.close()
+ tail = f.read()
+ f.close()
+ assert tail == ""
+
+
class TestPrintedResults:
def run(self, entry_point, args, expected):