[pypy-commit] pypy reflex-support: merge default into branch
wlav
noreply at buildbot.pypy.org
Mon May 30 19:25:45 CEST 2011
Author: Wim Lavrijsen <WLavrijsen at lbl.gov>
Branch: reflex-support
Changeset: r44595:bc02a095ea11
Date: 2011-05-30 10:38 -0700
http://bitbucket.org/pypy/pypy/changeset/bc02a095ea11/
Log: merge default into branch
diff --git a/lib-python/TODO b/lib-python/TODO
deleted file mode 100644
--- a/lib-python/TODO
+++ /dev/null
@@ -1,100 +0,0 @@
-TODO list for 2.7.0
-===================
-
-You can find the results of the most recent buildbot run at:
-http://buildbot.pypy.org/
-
-
-Probably easy tasks
--------------------
-
-- (unicode|bytearray).(index|find) should accept None as indices (see
- test_unicode.py)
-
-- missing posix.confstr and posix.confstr_names
-
-- remove code duplication: bit_length() and _count_bits() in rlib/rbigint.py,
- objspace/std/longobject.py and objspace/std/longtype.py.
-
-- missing module pyexpat.errors
-
-- support for PYTHONIOENCODING, this needs a way to update file.encoding
-
-- implement format__Complex_ANY() in pypy/objspace/std/complexobject.py
-
-- Code like this does not work, for two reasons::
-
- \
- from __future__ import (with_statement,
- unicode_literals)
- assert type("") is unicode
-
-- Code like::
-
- assert(x is not None, "error message")
-
- should emit a SyntaxWarning when compiled (the tuple is always true)
-
-
-Medium tasks
-------------
-
-- socket module has a couple of changes (including AF_TIPC packet range)
-
-Longer tasks
-------------
-
-- Fix usage of __cmp__ in subclasses::
-
- class badint(int):
- def __cmp__(self, other):
- raise RuntimeError
- raises(RuntimeError, cmp, 0, badint(1))
-
-- Fix comparison of objects layout: if two classes have the same __slots__, it
- should be possible to change the instances __class__::
-
- class A(object): __slots__ = ('a', 'b')
- class B(object): __slots__ = ('b', 'a')
- a = A()
- a.__class__ = B
-
-- Show a ResourceWarning when a file/socket is not explicitely closed, like
- CPython did for 3.2: http://svn.python.org/view?view=rev&revision=85920
- in PyPy this should be enabled by default
-
-Won't do for this release
--------------------------
-
-Note: when you give up with a missing feature, please mention it here, as well
-as the various skips added to the test suite.
-
-- py3k warnings
-
- * the -3 flag is accepted on the command line, but displays a warning (see
- `translator/goal/app_main.py`)
-
-- CJK codecs.
-
- * In `./conftest.py`, skipped all `test_codecencodings_*.py` and
- `test_codecmaps_*.py`.
-
- * In test_codecs, commented out various items in `all_unicode_encodings`.
-
-- Error messages about ill-formed calls (like "argument after ** must be a
- mapping") don't always show the function name. That's hard to fix for
- the case of errors raised when the Argument object is created (as opposed
- to when parsing for a given target function, which occurs later).
-
- * Some "..." were added to doctests in test_extcall.py
-
-- CPython's builtin methods are both functions and unbound methods (for
- example, `str.upper is dict(str.__dict__)['upper']`). This is not the case
- in pypy, and assertions like `object.__str__ is object.__str__` are False
- with pypy. Use the `==` operator instead.
-
- * pprint.py, _threading_local.py
-
-- When importing a nested module fails, the ImportError message mentions the
- name of the package up to the component that could not be imported (CPython
- prefers to display the names starting with the failing part).
diff --git a/lib_pypy/stackless.py b/lib_pypy/stackless.py
--- a/lib_pypy/stackless.py
+++ b/lib_pypy/stackless.py
@@ -200,14 +200,15 @@
# I can't think of a better solution without a real transform.
def rewrite_stackless_primitive(coro_state, alive, tempval):
- flags, state, thunk, parent = coro_state
- for i, frame in enumerate(state):
+ flags, frame, thunk, parent = coro_state
+ while frame is not None:
retval_expr = _stackless_primitive_registry.get(frame.f_code)
if retval_expr:
# this tasklet needs to stop pickling here and return its value.
tempval = eval(retval_expr, globals(), frame.f_locals)
- state = state[:i]
- coro_state = flags, state, thunk, parent
+ coro_state = flags, frame, thunk, parent
+ break
+ frame = frame.f_back
return coro_state, alive, tempval
#
@@ -492,23 +493,22 @@
assert two == ()
# we want to get rid of the parent thing.
# for now, we just drop it
- a, b, c, d = coro_state
-
+ a, frame, c, d = coro_state
+
# Removing all frames related to stackless.py.
# They point to stuff we don't want to be pickled.
- frame_list = list(b)
- new_frame_list = []
- for frame in frame_list:
+
+ pickleframe = frame
+ while frame is not None:
if frame.f_code == schedule.func_code:
# Removing everything including and after the
# call to stackless.schedule()
+ pickleframe = frame.f_back
break
- new_frame_list.append(frame)
- b = tuple(new_frame_list)
-
+ frame = frame.f_back
if d:
assert isinstance(d, coroutine)
- coro_state = a, b, c, None
+ coro_state = a, pickleframe, c, None
coro_state, alive, tempval = rewrite_stackless_primitive(coro_state, self.alive, self.tempval)
inst_dict = self.__dict__.copy()
inst_dict.pop('tempval', None)
diff --git a/pypy/annotation/description.py b/pypy/annotation/description.py
--- a/pypy/annotation/description.py
+++ b/pypy/annotation/description.py
@@ -565,7 +565,7 @@
if self.is_exception_class():
if self.pyobj.__module__ == 'exceptions':
return True
- if self.pyobj is py.code._AssertionError:
+ if issubclass(self.pyobj, AssertionError):
return True
return False
diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -242,6 +242,10 @@
"(the empty string and potentially single-char strings)",
default=False),
+ BoolOption("withsmalltuple",
+ "use small tuples",
+ default=False),
+
BoolOption("withrope", "use ropes as the string implementation",
default=False,
requires=[("objspace.std.withstrslice", False),
diff --git a/pypy/doc/config/objspace.std.withsmalltuple.txt b/pypy/doc/config/objspace.std.withsmalltuple.txt
new file mode 100644
--- /dev/null
+++ b/pypy/doc/config/objspace.std.withsmalltuple.txt
@@ -0,0 +1,1 @@
+Use small tuple objects for sizes from 1 to 3
diff --git a/pypy/interpreter/astcompiler/misc.py b/pypy/interpreter/astcompiler/misc.py
--- a/pypy/interpreter/astcompiler/misc.py
+++ b/pypy/interpreter/astcompiler/misc.py
@@ -31,11 +31,12 @@
future_lineno = 0
future_column = 0
have_docstring = False
+ body = None
if isinstance(tree, ast.Module):
body = tree.body
elif isinstance(tree, ast.Interactive):
body = tree.body
- else:
+ if body is None:
return 0, 0
for stmt in body:
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Str):
diff --git a/pypy/interpreter/eval.py b/pypy/interpreter/eval.py
--- a/pypy/interpreter/eval.py
+++ b/pypy/interpreter/eval.py
@@ -2,6 +2,7 @@
This module defines the abstract base classes that support execution:
Code and Frame.
"""
+from pypy.rlib import jit
from pypy.interpreter.error import OperationError
from pypy.interpreter.baseobjspace import Wrappable
@@ -97,6 +98,7 @@
"Abstract. Get the expected number of locals."
raise TypeError, "abstract"
+ @jit.dont_look_inside
def fast2locals(self):
# Copy values from self.fastlocals_w to self.w_locals
if self.w_locals is None:
@@ -110,6 +112,7 @@
w_name = self.space.wrap(name)
self.space.setitem(self.w_locals, w_name, w_value)
+ @jit.dont_look_inside
def locals2fast(self):
# Copy values from self.w_locals to self.fastlocals_w
assert self.w_locals is not None
diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -102,18 +102,16 @@
# the following interface is for pickling and unpickling
def getstate(self, space):
- # XXX we could just save the top frame, which brings
- # the whole frame stack, but right now we get the whole stack
- items = [space.wrap(f) for f in self.getframestack()]
- return space.newtuple(items)
+ if self.topframe is None:
+ return space.w_None
+ return self.topframe
def setstate(self, space, w_state):
from pypy.interpreter.pyframe import PyFrame
- frames_w = space.unpackiterable(w_state)
- if len(frames_w) > 0:
- self.topframe = space.interp_w(PyFrame, frames_w[-1])
+ if space.is_w(w_state, space.w_None):
+ self.topframe = None
else:
- self.topframe = None
+ self.topframe = space.interp_w(PyFrame, w_state)
def getframestack(self):
lst = []
diff --git a/pypy/interpreter/nestedscope.py b/pypy/interpreter/nestedscope.py
--- a/pypy/interpreter/nestedscope.py
+++ b/pypy/interpreter/nestedscope.py
@@ -127,6 +127,7 @@
if self.cells is not None:
self.cells[:ncellvars] = cellvars
+ @jit.dont_look_inside
def fast2locals(self):
super_fast2locals(self)
# cellvars are values exported to inner scopes
@@ -145,6 +146,7 @@
w_name = self.space.wrap(name)
self.space.setitem(self.w_locals, w_name, w_value)
+ @jit.dont_look_inside
def locals2fast(self):
super_locals2fast(self)
freevarnames = self.pycode.co_cellvars + self.pycode.co_freevars
diff --git a/pypy/interpreter/pycompiler.py b/pypy/interpreter/pycompiler.py
--- a/pypy/interpreter/pycompiler.py
+++ b/pypy/interpreter/pycompiler.py
@@ -101,9 +101,9 @@
"""
def __init__(self, space, override_version=None):
PyCodeCompiler.__init__(self, space)
- self.parser = pyparse.PythonParser(space)
+ self.future_flags = future.futureFlags_2_7
+ self.parser = pyparse.PythonParser(space, self.future_flags)
self.additional_rules = {}
- self.future_flags = future.futureFlags_2_7
self.compiler_flags = self.future_flags.allowed_flags
def compile_ast(self, node, filename, mode, flags):
@@ -140,9 +140,6 @@
def _compile_to_ast(self, source, info):
space = self.space
try:
- f_flags, future_info = future.get_futures(self.future_flags, source)
- info.last_future_import = future_info
- info.flags |= f_flags
parse_tree = self.parser.parse_source(source, info)
mod = astbuilder.ast_from_node(space, parse_tree, info)
except parseerror.IndentationError, e:
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -11,7 +11,7 @@
from pypy.rlib.jit import hint
from pypy.rlib.debug import make_sure_not_resized
from pypy.rlib.rarithmetic import intmask
-from pypy.rlib import jit, rstack
+from pypy.rlib import jit
from pypy.tool import stdlib_opcode
from pypy.tool.stdlib_opcode import host_bytecode_spec
@@ -157,8 +157,6 @@
try:
w_exitvalue = self.dispatch(self.pycode, next_instr,
executioncontext)
- rstack.resume_point("execute_frame", self, executioncontext,
- returns=w_exitvalue)
except Exception:
executioncontext.return_trace(self, self.space.w_None)
raise
@@ -415,6 +413,7 @@
"Get the fast locals as a list."
return self.fastlocals_w
+ @jit.dont_look_inside
def setfastscope(self, scope_w):
"""Initialize the fast locals from a list of values,
where the order is according to self.pycode.signature()."""
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -11,7 +11,7 @@
from pypy.interpreter.pycode import PyCode
from pypy.tool.sourcetools import func_with_new_name
from pypy.rlib.objectmodel import we_are_translated
-from pypy.rlib import jit, rstackovf, rstack
+from pypy.rlib import jit, rstackovf
from pypy.rlib.rarithmetic import r_uint, intmask
from pypy.rlib.unroll import unrolling_iterable
from pypy.rlib.debug import check_nonneg
@@ -83,16 +83,12 @@
try:
while True:
next_instr = self.handle_bytecode(co_code, next_instr, ec)
- rstack.resume_point("dispatch", self, co_code, ec,
- returns=next_instr)
except ExitFrame:
return self.popvalue()
def handle_bytecode(self, co_code, next_instr, ec):
try:
next_instr = self.dispatch_bytecode(co_code, next_instr, ec)
- rstack.resume_point("handle_bytecode", self, co_code, ec,
- returns=next_instr)
except OperationError, operr:
next_instr = self.handle_operation_error(ec, operr)
except Reraise:
@@ -248,9 +244,6 @@
# dispatch to the opcode method
meth = getattr(self, opdesc.methodname)
res = meth(oparg, next_instr)
- if opdesc.index == self.opcodedesc.CALL_FUNCTION.index:
- rstack.resume_point("dispatch_call", self, co_code,
- next_instr, ec)
# !! warning, for the annotator the next line is not
# comparing an int and None - you can't do that.
# Instead, it's constant-folded to either True or False
@@ -997,7 +990,6 @@
args)
else:
w_result = self.space.call_args(w_function, args)
- rstack.resume_point("call_function", self, returns=w_result)
self.pushvalue(w_result)
def CALL_FUNCTION(self, oparg, next_instr):
@@ -1008,8 +1000,6 @@
w_function = self.peekvalue(nargs)
try:
w_result = self.space.call_valuestack(w_function, nargs, self)
- rstack.resume_point("CALL_FUNCTION", self, nargs,
- returns=w_result)
finally:
self.dropvalues(nargs + 1)
self.pushvalue(w_result)
@@ -1099,6 +1089,7 @@
w_dict = self.space.newdict()
self.pushvalue(w_dict)
+ @jit.unroll_safe
def BUILD_SET(self, itemcount, next_instr):
w_set = self.space.call_function(self.space.w_set)
if itemcount:
diff --git a/pypy/interpreter/pyparser/pyparse.py b/pypy/interpreter/pyparser/pyparse.py
--- a/pypy/interpreter/pyparser/pyparse.py
+++ b/pypy/interpreter/pyparser/pyparse.py
@@ -1,6 +1,6 @@
from pypy.interpreter import gateway
from pypy.interpreter.error import OperationError
-from pypy.interpreter.pyparser import parser, pytokenizer, pygram, error
+from pypy.interpreter.pyparser import future, parser, pytokenizer, pygram, error
from pypy.interpreter.astcompiler import consts
@@ -88,9 +88,11 @@
class PythonParser(parser.Parser):
- def __init__(self, space, grammar=pygram.python_grammar):
+ def __init__(self, space, future_flags=future.futureFlags_2_7,
+ grammar=pygram.python_grammar):
parser.Parser.__init__(self, grammar)
self.space = space
+ self.future_flags = future_flags
def parse_source(self, textsrc, compile_info):
"""Main entry point for parsing Python source.
@@ -133,6 +135,10 @@
raise error.SyntaxError(space.str_w(w_message))
raise
+ f_flags, future_info = future.get_futures(self.future_flags, textsrc)
+ compile_info.last_future_import = future_info
+ compile_info.flags |= f_flags
+
flags = compile_info.flags
if flags & consts.CO_FUTURE_PRINT_FUNCTION:
diff --git a/pypy/interpreter/test/test_compiler.py b/pypy/interpreter/test/test_compiler.py
--- a/pypy/interpreter/test/test_compiler.py
+++ b/pypy/interpreter/test/test_compiler.py
@@ -714,6 +714,12 @@
class AppTestCompiler:
+ def test_bom_with_future(self):
+ s = '\xef\xbb\xbffrom __future__ import division\nx = 1/2'
+ ns = {}
+ exec s in ns
+ assert ns["x"] == .5
+
def test_values_of_different_types(self):
exec "a = 0; b = 0L; c = 0.0; d = 0j"
assert type(a) is int
diff --git a/pypy/jit/backend/llsupport/llmodel.py b/pypy/jit/backend/llsupport/llmodel.py
--- a/pypy/jit/backend/llsupport/llmodel.py
+++ b/pypy/jit/backend/llsupport/llmodel.py
@@ -143,11 +143,11 @@
STACK_CHECK_SLOWPATH = lltype.Ptr(lltype.FuncType([lltype.Signed],
lltype.Void))
def insert_stack_check():
- startaddr = rstack._stack_get_start_adr()
- length = rstack._stack_get_length()
+ endaddr = rstack._stack_get_end_adr()
+ lengthaddr = rstack._stack_get_length_adr()
f = llhelper(STACK_CHECK_SLOWPATH, rstack.stack_check_slowpath)
slowpathaddr = rffi.cast(lltype.Signed, f)
- return startaddr, length, slowpathaddr
+ return endaddr, lengthaddr, slowpathaddr
self.pos_exception = pos_exception
self.pos_exc_value = pos_exc_value
diff --git a/pypy/jit/backend/x86/assembler.py b/pypy/jit/backend/x86/assembler.py
--- a/pypy/jit/backend/x86/assembler.py
+++ b/pypy/jit/backend/x86/assembler.py
@@ -137,7 +137,8 @@
self.current_clt = looptoken.compiled_loop_token
self.pending_guard_tokens = []
self.mc = codebuf.MachineCodeBlockWrapper()
- assert self.datablockwrapper is None
+ #assert self.datablockwrapper is None --- but obscure case
+ # possible, e.g. getting MemoryError and continuing
allblocks = self.get_asmmemmgr_blocks(looptoken)
self.datablockwrapper = MachineDataBlockWrapper(self.cpu.asmmemmgr,
allblocks)
@@ -620,11 +621,11 @@
if self.stack_check_slowpath == 0:
pass # no stack check (e.g. not translated)
else:
- startaddr, length, _ = self.cpu.insert_stack_check()
- self.mc.MOV(eax, esp) # MOV eax, current
- self.mc.SUB(eax, heap(startaddr)) # SUB eax, [startaddr]
- self.mc.CMP(eax, imm(length)) # CMP eax, length
- self.mc.J_il8(rx86.Conditions['B'], 0) # JB .skip
+ endaddr, lengthaddr, _ = self.cpu.insert_stack_check()
+ self.mc.MOV(eax, heap(endaddr)) # MOV eax, [start]
+ self.mc.SUB(eax, esp) # SUB eax, current
+ self.mc.CMP(eax, heap(lengthaddr)) # CMP eax, [length]
+ self.mc.J_il8(rx86.Conditions['BE'], 0) # JBE .skip
jb_location = self.mc.get_relative_pos()
self.mc.CALL(imm(self.stack_check_slowpath))# CALL slowpath
# patch the JB above # .skip:
diff --git a/pypy/jit/codewriter/jtransform.py b/pypy/jit/codewriter/jtransform.py
--- a/pypy/jit/codewriter/jtransform.py
+++ b/pypy/jit/codewriter/jtransform.py
@@ -209,7 +209,6 @@
def rewrite_op_cast_int_to_unichar(self, op): pass
def rewrite_op_cast_int_to_uint(self, op): pass
def rewrite_op_cast_uint_to_int(self, op): pass
- def rewrite_op_resume_point(self, op): pass
def _rewrite_symmetric(self, op):
"""Rewrite 'c1+v2' into 'v2+c1' in an attempt to avoid generating
diff --git a/pypy/jit/codewriter/policy.py b/pypy/jit/codewriter/policy.py
--- a/pypy/jit/codewriter/policy.py
+++ b/pypy/jit/codewriter/policy.py
@@ -63,12 +63,27 @@
contains_loop = contains_loop and not getattr(
func, '_jit_unroll_safe_', False)
- res = see_function and not contains_unsupported_variable_type(graph,
- self.supports_floats,
- self.supports_longlong)
+ unsupported = contains_unsupported_variable_type(graph,
+ self.supports_floats,
+ self.supports_longlong)
+ res = see_function and not unsupported
if res and contains_loop:
self.unsafe_loopy_graphs.add(graph)
- return res and not contains_loop
+ res = res and not contains_loop
+ if (see_function and not res and
+ getattr(graph, "access_directly", False)):
+ # This happens when we have a function which has an argument with
+ # the access_directly flag, and the annotator has determined we will
+ # see the function. (See
+ # pypy/annotation/specialize.py:default_specialize) However,
+ # look_inside_graph just decided that we will not see it. (It has a
+ # loop or unsupported variables.) If we return False, the call will
+ # be turned into a residual call, but the graph is access_directly!
+ # If such a function is called and accesses a virtualizable, the JIT
+ # will not notice, and the virtualizable will fall out of sync. So,
+ # we fail loudly now.
+ raise ValueError("access_directly on a function which we don't see %s" % graph)
+ return res
def contains_unsupported_variable_type(graph, supports_floats,
supports_longlong):
diff --git a/pypy/jit/codewriter/test/test_policy.py b/pypy/jit/codewriter/test/test_policy.py
--- a/pypy/jit/codewriter/test/test_policy.py
+++ b/pypy/jit/codewriter/test/test_policy.py
@@ -1,4 +1,5 @@
import sys
+import py
from pypy.jit.codewriter.policy import contains_unsupported_variable_type
from pypy.jit.codewriter.policy import JitPolicy
from pypy.jit.codewriter import support
@@ -107,3 +108,19 @@
mod = called_graph.func.__module__
assert (mod == 'pypy.rpython.rlist' or
mod == 'pypy.rpython.lltypesystem.rlist')
+
+def test_access_directly_but_not_seen():
+ class X:
+ _virtualizable2_ = ["a"]
+ def h(x, y):
+ w = 0
+ for i in range(y):
+ w += 4
+ return w
+ def f(y):
+ x = jit.hint(X(), access_directly=True)
+ h(x, y)
+ rtyper = support.annotate(f, [3])
+ h_graph = rtyper.annotator.translator.graphs[1]
+ assert h_graph.func is h
+ py.test.raises(ValueError, JitPolicy().look_inside_graph, h_graph)
diff --git a/pypy/jit/metainterp/test/test_list.py b/pypy/jit/metainterp/test/test_list.py
--- a/pypy/jit/metainterp/test/test_list.py
+++ b/pypy/jit/metainterp/test/test_list.py
@@ -236,4 +236,8 @@
return a * b
res = self.meta_interp(f, [37])
assert res == f(37)
- self.check_loops(getfield_gc=1, everywhere=True)
+ # There is the one actual field on a, plus 2 getfield's from the list
+ # itself, 1 to get the length (which is then incremented and passed to
+ # the resize func), and then a read of the items field to actually
+ # perform the setarrayitem on
+ self.check_loops(getfield_gc=5, everywhere=True)
diff --git a/pypy/jit/metainterp/test/test_tl.py b/pypy/jit/metainterp/test/test_tl.py
--- a/pypy/jit/metainterp/test/test_tl.py
+++ b/pypy/jit/metainterp/test/test_tl.py
@@ -58,7 +58,7 @@
exit:
RETURN
''')
-
+
codes = [code, code2]
def main(n, inputarg):
code = codes[n]
@@ -116,7 +116,7 @@
codes = [code, '']
def main(num, arg):
return interp(codes[num], inputarg=arg)
-
+
res = self.meta_interp(main, [0, 20], enable_opts='',
listops=listops, backendopt=True, policy=policy)
assert res == 0
@@ -128,7 +128,6 @@
from pypy.jit.tl.tl import Stack
methods = [Stack.put,
Stack.pick,
- Stack.roll,
Stack.append,
Stack.pop]
for meth in methods:
diff --git a/pypy/jit/tl/tl.py b/pypy/jit/tl/tl.py
--- a/pypy/jit/tl/tl.py
+++ b/pypy/jit/tl/tl.py
@@ -40,6 +40,7 @@
assert n >= 0
self.stack[n] = elem
+ @dont_look_inside
def roll(self, r):
if r < -1:
i = self.stackpos + r
diff --git a/pypy/module/_ast/test/test_ast.py b/pypy/module/_ast/test/test_ast.py
--- a/pypy/module/_ast/test/test_ast.py
+++ b/pypy/module/_ast/test/test_ast.py
@@ -128,6 +128,9 @@
assert ns["x"] == ns["lemon"] == 3
assert ns["apple"] == 4
+ def test_empty_module(self):
+ compile(self.ast.Module([]), "<test>", "exec")
+
def test_ast_types(self):
ast = self.ast
expr = ast.Expr()
diff --git a/pypy/module/_file/interp_file.py b/pypy/module/_file/interp_file.py
--- a/pypy/module/_file/interp_file.py
+++ b/pypy/module/_file/interp_file.py
@@ -43,7 +43,11 @@
# assume that the file and stream objects are only visible in the
# thread that runs __del__, so no race condition should be possible
self.clear_all_weakrefs()
- self.direct_close()
+ try:
+ self.direct_close()
+ except StreamErrors, e:
+ operr = wrap_streamerror(self.space, e, self.w_name)
+ operr.write_unraisable(self.space, '__del__ of ', self)
def fdopenstream(self, stream, fd, mode, w_name=None):
self.fd = fd
@@ -553,4 +557,4 @@
@unwrap_spec(file=W_File, encoding="str_or_None", errors="str_or_None")
def set_file_encoding(space, file, encoding=None, errors=None):
file.encoding = encoding
- file.errors = errors
\ No newline at end of file
+ file.errors = errors
diff --git a/pypy/module/_file/test/test_file.py b/pypy/module/_file/test/test_file.py
--- a/pypy/module/_file/test/test_file.py
+++ b/pypy/module/_file/test/test_file.py
@@ -232,6 +232,29 @@
data = f.read()
assert data == "15"
+ def test_exception_from_close(self):
+ import os
+ f = self.file(self.temppath, 'w')
+ os.close(f.fileno())
+ raises(IOError, f.close) # bad file descriptor
+
+ def test_exception_from_del(self):
+ import os, gc, sys, cStringIO
+ f = self.file(self.temppath, 'w')
+ g = cStringIO.StringIO()
+ preverr = sys.stderr
+ try:
+ sys.stderr = g
+ os.close(f.fileno())
+ del f
+ gc.collect() # bad file descriptor in f.__del__()
+ finally:
+ sys.stderr = preverr
+ import errno
+ assert os.strerror(errno.EBADF) in g.getvalue()
+ # the following is a "nice to have" feature that CPython doesn't have
+ if '__pypy__' in sys.builtin_module_names:
+ assert self.temppath in g.getvalue()
class AppTestConcurrency(object):
diff --git a/pypy/module/_stackless/interp_coroutine.py b/pypy/module/_stackless/interp_coroutine.py
--- a/pypy/module/_stackless/interp_coroutine.py
+++ b/pypy/module/_stackless/interp_coroutine.py
@@ -28,7 +28,7 @@
from pypy.module.exceptions.interp_exceptions import W_SystemExit, _new_exception
-from pypy.rlib import rstack # for resume points
+from pypy.rlib import rstack, jit # for resume points
from pypy.tool import stdlib_opcode as pythonopcode
class _AppThunk(AbstractThunk):
@@ -47,9 +47,19 @@
def call(self):
costate = self.costate
w_result = self.space.call_args(self.w_func, self.args)
- rstack.resume_point("appthunk", costate, returns=w_result)
costate.w_tempval = w_result
+class _ResumeThunk(AbstractThunk):
+ def __init__(self, space, costate, w_frame):
+ self.space = space
+ self.costate = costate
+ self.w_frame = w_frame
+
+ def call(self):
+ w_result = resume_frame(self.space, self.w_frame)
+ # costate.w_tempval = w_result #XXX?
+
+
W_CoroutineExit = _new_exception('CoroutineExit', W_SystemExit,
"""Coroutine killed manually.""")
@@ -97,7 +107,6 @@
"cannot switch to an unbound Coroutine"))
state = self.costate
self.switch()
- rstack.resume_point("w_switch", state, space)
w_ret, state.w_tempval = state.w_tempval, space.w_None
return w_ret
@@ -217,75 +226,17 @@
self.parent = space.interp_w(AppCoroutine, w_parent)
ec = self.space.getexecutioncontext()
self.subctx.setstate(space, w_state)
- self.reconstruct_framechain()
if space.is_w(w_thunk, space.w_None):
- self.thunk = None
+ if space.is_w(w_state, space.w_None):
+ self.thunk = None
+ else:
+ self.bind(_ResumeThunk(space, self.costate, self.subctx.topframe))
else:
w_func, w_args, w_kwds = space.unpackiterable(w_thunk,
expected_length=3)
args = Arguments.frompacked(space, w_args, w_kwds)
self.bind(_AppThunk(space, self.costate, w_func, args))
- def reconstruct_framechain(self):
- from pypy.interpreter.pyframe import PyFrame
- from pypy.rlib.rstack import resume_state_create
- if self.subctx.topframe is None:
- self.frame = None
- return
-
- space = self.space
- ec = space.getexecutioncontext()
- costate = self.costate
- # now the big fun of recreating tiny things...
- bottom = resume_state_create(None, "yield_current_frame_to_caller_1")
- # ("coroutine__bind", state)
- _bind_frame = resume_state_create(bottom, "coroutine__bind", costate)
- # ("appthunk", costate, returns=w_result)
- appthunk_frame = resume_state_create(_bind_frame, "appthunk", costate)
- chain = appthunk_frame
- for frame in self.subctx.getframestack():
- assert isinstance(frame, PyFrame)
- # ("execute_frame", self, executioncontext, returns=w_exitvalue)
- chain = resume_state_create(chain, "execute_frame", frame, ec)
- code = frame.pycode.co_code
- # ("dispatch", self, co_code, ec, returns=next_instr)
- chain = resume_state_create(chain, "dispatch", frame, code, ec)
- # ("handle_bytecode", self, co_code, ec, returns=next_instr)
- chain = resume_state_create(chain, "handle_bytecode", frame, code,
- ec)
- instr = frame.last_instr
- opcode = ord(code[instr])
- map = pythonopcode.opmap
- call_ops = [map['CALL_FUNCTION'], map['CALL_FUNCTION_KW'], map['CALL_FUNCTION_VAR'],
- map['CALL_FUNCTION_VAR_KW'], map['CALL_METHOD']]
- assert opcode in call_ops
- # ("dispatch_call", self, co_code, next_instr, ec)
- chain = resume_state_create(chain, "dispatch_call", frame, code,
- instr+3, ec)
- instr += 1
- oparg = ord(code[instr]) | ord(code[instr + 1]) << 8
- nargs = oparg & 0xff
- nkwds = (oparg >> 8) & 0xff
- if space.config.objspace.opcodes.CALL_METHOD and opcode == map['CALL_METHOD']:
- if nkwds == 0: # only positional arguments
- chain = resume_state_create(chain, 'CALL_METHOD', frame,
- nargs)
- else: # includes keyword arguments
- chain = resume_state_create(chain, 'CALL_METHOD_KW', frame)
- elif opcode == map['CALL_FUNCTION'] and nkwds == 0:
- # Only positional arguments
- # case1: ("CALL_FUNCTION", f, nargs, returns=w_result)
- chain = resume_state_create(chain, 'CALL_FUNCTION', frame,
- nargs)
- else:
- # case2: ("call_function", f, returns=w_result)
- chain = resume_state_create(chain, 'call_function', frame)
-
- # ("w_switch", state, space)
- w_switch_frame = resume_state_create(chain, 'w_switch', costate, space)
- # ("coroutine_switch", state, returns=incoming_frame)
- switch_frame = resume_state_create(w_switch_frame, "coroutine_switch", costate)
- self.frame = switch_frame
# _mixin_ did not work
for methname in StacklessFlags.__dict__:
@@ -411,3 +362,45 @@
@unwrap_spec(limit=int)
def set_stack_depth_limit(space, limit):
rstack.set_stack_depth_limit(limit)
+
+
+# ___________________________________________________________________
+# unpickling trampoline
+
+def resume_frame(space, w_frame):
+ from pypy.interpreter.pyframe import PyFrame
+ frame = space.interp_w(PyFrame, w_frame, can_be_None=True)
+ w_result = space.w_None
+ operr = None
+ executioncontext = frame.space.getexecutioncontext()
+ while frame is not None:
+ code = frame.pycode.co_code
+ instr = frame.last_instr
+ opcode = ord(code[instr])
+ map = pythonopcode.opmap
+ call_ops = [map['CALL_FUNCTION'], map['CALL_FUNCTION_KW'], map['CALL_FUNCTION_VAR'],
+ map['CALL_FUNCTION_VAR_KW'], map['CALL_METHOD']]
+ assert opcode in call_ops
+ instr += 1
+ oparg = ord(code[instr]) | ord(code[instr + 1]) << 8
+ nargs = oparg & 0xff
+ nkwds = (oparg >> 8) & 0xff
+ if nkwds == 0: # only positional arguments
+ # fast paths leaves things on the stack, pop them
+ if space.config.objspace.opcodes.CALL_METHOD and opcode == map['CALL_METHOD']:
+ frame.dropvalues(nargs + 2)
+ elif opcode == map['CALL_FUNCTION']:
+ frame.dropvalues(nargs + 1)
+
+ # small hack: unlink frame out of the execution context, because
+ # execute_frame will add it there again
+ executioncontext.topframeref = jit.non_virtual_ref(frame.f_backref())
+ frame.last_instr = instr + 1 # continue after the call
+ try:
+ w_result = frame.execute_frame(w_result, operr)
+ except OperationError, operr:
+ pass
+ frame = frame.f_backref()
+ if operr:
+ raise operr
+ return w_result
diff --git a/pypy/module/_stackless/test/test_coroutine.py b/pypy/module/_stackless/test/test_coroutine.py
--- a/pypy/module/_stackless/test/test_coroutine.py
+++ b/pypy/module/_stackless/test/test_coroutine.py
@@ -8,33 +8,6 @@
space = gettestobjspace(usemodules=('_stackless',))
cls.space = space
- def test_pickle_coroutine_empty(self):
- # this test is limited to basic pickling.
- # real stacks can only tested with a stackless pypy build.
- import _stackless as stackless
- co = stackless.coroutine()
- import pickle
- pckl = pickle.dumps(co)
- co2 = pickle.loads(pckl)
- # the empty unpickled coroutine can still be used:
- result = []
- co2.bind(result.append, 42)
- co2.switch()
- assert result == [42]
-
- def test_pickle_coroutine_bound(self):
- import pickle
- import _stackless
- lst = [4]
- co = _stackless.coroutine()
- co.bind(lst.append, 2)
- pckl = pickle.dumps((co, lst))
-
- (co2, lst2) = pickle.loads(pckl)
- assert lst2 == [4]
- co2.switch()
- assert lst2 == [4, 2]
-
def test_raise_propagate(self):
import _stackless as stackless
co = stackless.coroutine()
diff --git a/pypy/module/_stackless/test/test_pickle.py b/pypy/module/_stackless/test/test_pickle.py
--- a/pypy/module/_stackless/test/test_pickle.py
+++ b/pypy/module/_stackless/test/test_pickle.py
@@ -19,9 +19,35 @@
class AppTestPickle:
def setup_class(cls):
- if not option.runappdirect:
- py.test.skip('pure appdirect test (run with -A)')
- cls.space = gettestobjspace(usemodules=('_stackless',))
+ cls.space = gettestobjspace(usemodules=('_stackless',), CALL_METHOD=True)
+
+ def test_pickle_coroutine_empty(self):
+ # this test is limited to basic pickling.
+ # real stacks can only tested with a stackless pypy build.
+ import _stackless as stackless
+ co = stackless.coroutine()
+ import pickle
+ pckl = pickle.dumps(co)
+ co2 = pickle.loads(pckl)
+ # the empty unpickled coroutine can still be used:
+ result = []
+ co2.bind(result.append, 42)
+ co2.switch()
+ assert result == [42]
+
+ def test_pickle_coroutine_bound(self):
+ import pickle
+ import _stackless
+ lst = [4]
+ co = _stackless.coroutine()
+ co.bind(lst.append, 2)
+ pckl = pickle.dumps((co, lst))
+
+ (co2, lst2) = pickle.loads(pckl)
+ assert lst2 == [4]
+ co2.switch()
+ assert lst2 == [4, 2]
+
def test_simple_ish(self):
@@ -58,6 +84,113 @@
finally:
del sys.modules['mod']
+ def test_pickle_again(self):
+
+ import new, sys
+
+ mod = new.module('mod')
+ sys.modules['mod'] = mod
+ try:
+ exec '''
+output = []
+import _stackless
+def f(coro, n, x):
+ if n == 0:
+ coro.switch()
+ return
+ f(coro, n-1, 2*x)
+ output.append(x)
+
+def example():
+ main_coro = _stackless.coroutine.getcurrent()
+ sub_coro = _stackless.coroutine()
+ sub_coro.bind(f, main_coro, 5, 1)
+ sub_coro.switch()
+
+ import pickle
+ pckl = pickle.dumps(sub_coro)
+ new_coro = pickle.loads(pckl)
+ pckl = pickle.dumps(new_coro)
+ newer_coro = pickle.loads(pckl)
+
+ newer_coro.switch()
+
+example()
+assert output == [16, 8, 4, 2, 1]
+''' in mod.__dict__
+ finally:
+ del sys.modules['mod']
+
+ def test_kwargs(self):
+
+ import new, sys
+
+ mod = new.module('mod')
+ sys.modules['mod'] = mod
+ try:
+ exec '''
+output = []
+import _stackless
+def f(coro, n, x, step=4):
+ if n == 0:
+ coro.switch()
+ return
+ f(coro, n-1, 2*x, step=1)
+ output.append(x)
+
+def example():
+ main_coro = _stackless.coroutine.getcurrent()
+ sub_coro = _stackless.coroutine()
+ sub_coro.bind(f, main_coro, 5, 1, 1)
+ sub_coro.switch()
+
+ import pickle
+ pckl = pickle.dumps(sub_coro)
+ new_coro = pickle.loads(pckl)
+
+ new_coro.switch()
+
+example()
+assert output == [16, 8, 4, 2, 1]
+''' in mod.__dict__
+ finally:
+ del sys.modules['mod']
+
+ def test_starstarargs(self):
+
+ import new, sys
+
+ mod = new.module('mod')
+ sys.modules['mod'] = mod
+ try:
+ exec '''
+output = []
+import _stackless
+def f(coro, n, x, step=4):
+ if n == 0:
+ coro.switch()
+ return
+ f(coro, n-1, 2*x, **{'step': 1})
+ output.append(x)
+
+def example():
+ main_coro = _stackless.coroutine.getcurrent()
+ sub_coro = _stackless.coroutine()
+ sub_coro.bind(f, main_coro, 5, 1, 1)
+ sub_coro.switch()
+
+ import pickle
+ pckl = pickle.dumps(sub_coro)
+ new_coro = pickle.loads(pckl)
+
+ new_coro.switch()
+
+example()
+assert output == [16, 8, 4, 2, 1]
+''' in mod.__dict__
+ finally:
+ del sys.modules['mod']
+
def test_closure(self):
import new, sys
@@ -130,8 +263,55 @@
finally:
del sys.modules['mod']
+ def test_exception_after_unpickling(self):
+
+ import new, sys
+
+ mod = new.module('mod')
+ sys.modules['mod'] = mod
+ try:
+ exec '''
+output = []
+import _stackless
+def f(coro, n, x):
+ if n == 0:
+ coro.switch()
+ raise ValueError
+ try:
+ f(coro, n-1, 2*x)
+ finally:
+ output.append(x)
+
+def example():
+ main_coro = _stackless.coroutine.getcurrent()
+ sub_coro = _stackless.coroutine()
+ sub_coro.bind(f, main_coro, 5, 1)
+ sub_coro.switch()
+
+ import pickle
+ pckl = pickle.dumps(sub_coro)
+ new_coro = pickle.loads(pckl)
+
+ try:
+ sub_coro.switch()
+ except ValueError:
+ pass
+ else:
+ assert 0
+ try:
+ new_coro.switch()
+ except ValueError:
+ pass
+ else:
+ assert 0
+
+example()
+assert output == [16, 8, 4, 2, 1] * 2
+''' in mod.__dict__
+ finally:
+ del sys.modules['mod']
+
def test_loop(self):
- #skip("happily segfaulting")
import new, sys
mod = new.module('mod')
diff --git a/pypy/module/_stackless/test/test_pickle_infrastructure.py b/pypy/module/_stackless/test/test_pickle_infrastructure.py
deleted file mode 100644
--- a/pypy/module/_stackless/test/test_pickle_infrastructure.py
+++ /dev/null
@@ -1,301 +0,0 @@
-from pypy.conftest import gettestobjspace
-from py.test import skip
-
-
-class BaseAppTestPicklePrerequisites(object):
- OPTIONS = {}
- def setup_class(cls):
- space = gettestobjspace(usemodules=('_stackless',), **cls.OPTIONS)
- cls.space = space
-
- def test_pickle_switch_function(object):
- import _stackless, pickle
-
- sw = _stackless.coroutine.switch.im_func
- dump = pickle.dumps(sw)
- res = pickle.loads(dump)
-
- assert res is sw
- assert res.func_code is sw.func_code
- assert res.func_doc is sw.func_doc
- assert res.func_globals is sw.func_globals
-
- def test_pickle_switch_function_code(object):
- import _stackless, pickle
-
- sw = _stackless.coroutine.switch.im_func.func_code
- dump = pickle.dumps(sw)
- res = pickle.loads(dump)
-
- assert res is sw
-
-class AppTestPicklePrerequisites(BaseAppTestPicklePrerequisites):
- pass
-
-class AppTestPicklePrerequisitesBuiltinShortcut(BaseAppTestPicklePrerequisites):
- OPTIONS = {"objspace.std.builtinshortcut": True}
-
-class FrameCheck(object):
-
- def __init__(self, name):
- self.name = name
-
- def __eq__(self, frame):
- return frame.pycode.co_name == self.name
-
-class BytecodeCheck(object):
-
- def __init__(self, code, op, arg):
- self.code = code
- self.op = chr(op)+chr(arg & 0xff) + chr(arg >> 8 & 0xff)
-
- def __eq__(self, pos):
- return self.code[pos-3:pos] == self.op
-
-class BaseTestReconstructFrameChain(object):
- OPTIONS = {}
-
- def setup_class(cls):
- space = gettestobjspace(usemodules=('_stackless',), **cls.OPTIONS)
- cls.space = space
-
- from pypy.rlib import rstack
- cls.old_resume_state_create = rstack.resume_state_create
-
- def tr(prevstate, label, *args):
- if prevstate is None:
- prevstate = []
- return prevstate+[(label, args)]
- rstack.resume_state_create = tr
-
- w_opmap = space.appexec([], """():
- import opcode
-
- return opcode.opmap
- """)
-
- opmap = space.unwrap(w_opmap)
- cls.CALL_FUNCTION = opmap['CALL_FUNCTION']
- cls.CALL_FUNCTION_VAR = opmap['CALL_FUNCTION_VAR']
- cls.CALL_METHOD = opmap['CALL_METHOD']
-
- cls.callmethod = getattr(cls, cls.callmethod_label)
-
- def teardown_class(cls):
- from pypy.rlib import rstack
- rstack.resume_state_create = cls.old_resume_state_create
-
- def start(self, w_coro):
- self.i = 0
- self.frame_to_check = w_coro.frame
- w_coro.frame = None # avoid exploding in kill > __del__
-
- def end(self):
- assert self.i == len(self.frame_to_check)
-
- def check_entry(self, label, *args):
- frame = self.frame_to_check
- assert frame[self.i] == (label, args)
- self.i += 1
-
-
- def test_two_frames_simple(self):
- space = self.space
-
- w_res = space.appexec([], """():
- import _stackless as stackless
- import pickle
-
- main = stackless.coroutine.getcurrent()
- d = {'main': main}
-
- exec \"\"\"
-def f():
- g(1)
-
-def g(x):
- main.switch()
-\"\"\" in d
- f = d['f']
- g = d['g']
-
- co = stackless.coroutine()
- co.bind(f)
- co.switch()
-
- s = pickle.dumps(co)
- co = pickle.loads(s)
-
- return co, f, g
- """)
-
- w_co, w_f, w_g = space.fixedview(w_res)
-
- ec = space.getexecutioncontext()
- fcode = w_f.code.co_code
- gcode = w_g.code.co_code
-
- self.start(w_co)
- e = self.check_entry
- e('yield_current_frame_to_caller_1')
- e('coroutine__bind', w_co.costate)
- e('appthunk', w_co.costate)
- # f
- e('execute_frame', FrameCheck('f'), ec)
- e('dispatch', FrameCheck('f'), fcode, ec)
- e('handle_bytecode', FrameCheck('f'), fcode, ec)
- e('dispatch_call', FrameCheck('f'), fcode,
- BytecodeCheck(fcode, self.CALL_FUNCTION, 1), ec)
- e('CALL_FUNCTION', FrameCheck('f'), 1)
- # g
- e('execute_frame', FrameCheck('g'), ec)
- e('dispatch', FrameCheck('g'), gcode, ec)
- e('handle_bytecode', FrameCheck('g'), gcode, ec)
- e('dispatch_call', FrameCheck('g'), gcode,
- BytecodeCheck(gcode, self.callmethod, 0), ec)
- e(self.callmethod_label, FrameCheck('g'), 0)
- e('w_switch', w_co.costate, space)
- e('coroutine_switch', w_co.costate)
- self.end()
-
- def test_two_frames_stararg(self):
- space = self.space
-
- w_res = space.appexec([], """():
- import _stackless as stackless
- import pickle
-
- main = stackless.coroutine.getcurrent()
- d = {'main': main}
-
- exec \"\"\"
-def f():
- g(4, 3, d=2, *(1,))
-
-def g(a, b, c, d):
- main.switch()
-\"\"\" in d
- f = d['f']
- g = d['g']
-
- co = stackless.coroutine()
- co.bind(f)
- co.switch()
-
- s = pickle.dumps(co)
- co = pickle.loads(s)
-
- return co, f, g
- """)
-
- w_co, w_f, w_g = space.fixedview(w_res)
-
- ec = space.getexecutioncontext()
- fcode = w_f.code.co_code
- gcode = w_g.code.co_code
-
- self.start(w_co)
- e = self.check_entry
- e('yield_current_frame_to_caller_1')
- e('coroutine__bind', w_co.costate)
- e('appthunk', w_co.costate)
- # f
- e('execute_frame', FrameCheck('f'), ec)
- e('dispatch', FrameCheck('f'), fcode, ec)
- e('handle_bytecode', FrameCheck('f'), fcode, ec)
- e('dispatch_call', FrameCheck('f'), fcode,
- BytecodeCheck(fcode, self.CALL_FUNCTION_VAR, 2+(1<<8)), ec)
- e('call_function', FrameCheck('f'))
- # g
- e('execute_frame', FrameCheck('g'), ec)
- e('dispatch', FrameCheck('g'), gcode, ec)
- e('handle_bytecode', FrameCheck('g'), gcode, ec)
- e('dispatch_call', FrameCheck('g'), gcode,
- BytecodeCheck(gcode, self.callmethod, 0), ec)
- e(self.callmethod_label, FrameCheck('g'), 0)
- e('w_switch', w_co.costate, space)
- e('coroutine_switch', w_co.costate)
- self.end()
-
- def test_two_frames_method(self):
- space = self.space
-
- w_res = space.appexec([], """():
- import _stackless as stackless
- import pickle
- import new, sys
-
- mod = new.module('mod')
- sys.modules['mod'] = mod
-
- main = stackless.coroutine.getcurrent()
- d = {'main': main}
-
- exec \"\"\"
-def f():
- a = A()
- a.m(1)
-
-def g(_, x):
- main.switch()
-
-class A(object):
- m = g
-\"\"\" in d
- f = d['f']
- g = d['g']
- A = d['A']
-
- # to make pickling work
- mod.A = A
- A.__module__ = 'mod'
-
- co = stackless.coroutine()
- co.bind(f)
- co.switch()
-
- s = pickle.dumps(co)
- co = pickle.loads(s)
-
- return co, f, g
- """)
-
- w_co, w_f, w_g = space.fixedview(w_res)
-
- ec = space.getexecutioncontext()
- fcode = w_f.code.co_code
- gcode = w_g.code.co_code
-
- self.start(w_co)
- e = self.check_entry
- e('yield_current_frame_to_caller_1')
- e('coroutine__bind', w_co.costate)
- e('appthunk', w_co.costate)
- # f
- e('execute_frame', FrameCheck('f'), ec)
- e('dispatch', FrameCheck('f'), fcode, ec)
- e('handle_bytecode', FrameCheck('f'), fcode, ec)
- e('dispatch_call', FrameCheck('f'), fcode,
- BytecodeCheck(fcode, self.callmethod, 1), ec)
- e(self.callmethod_label, FrameCheck('f'), 1)
- # g
- e('execute_frame', FrameCheck('g'), ec)
- e('dispatch', FrameCheck('g'), gcode, ec)
- e('handle_bytecode', FrameCheck('g'), gcode, ec)
- e('dispatch_call', FrameCheck('g'), gcode,
- BytecodeCheck(gcode, self.callmethod, 0), ec)
- e(self.callmethod_label, FrameCheck('g'), 0)
- e('w_switch', w_co.costate, space)
- e('coroutine_switch', w_co.costate)
- self.end()
-
-class TestReconstructFrameChain(BaseTestReconstructFrameChain):
- callmethod_label = 'CALL_FUNCTION'
-
-class TestReconstructFrameChain_CALL_METHOD(BaseTestReconstructFrameChain):
- OPTIONS = {"objspace.opcodes.CALL_METHOD": True,
- }
-
- callmethod_label = 'CALL_METHOD'
-
-
diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py
--- a/pypy/module/cpyext/methodobject.py
+++ b/pypy/module/cpyext/methodobject.py
@@ -73,29 +73,29 @@
rffi.charp2str(self.ml.c_ml_name) + "() takes no keyword arguments"))
func = rffi.cast(PyCFunction, self.ml.c_ml_meth)
+ length = space.int_w(space.len(w_args))
if flags & METH_KEYWORDS:
func = rffi.cast(PyCFunctionKwArgs, self.ml.c_ml_meth)
return generic_cpy_call(space, func, w_self, w_args, w_kw)
elif flags & METH_NOARGS:
- if len(w_args.wrappeditems) == 0:
+ if length == 0:
return generic_cpy_call(space, func, w_self, None)
raise OperationError(space.w_TypeError, space.wrap(
rffi.charp2str(self.ml.c_ml_name) + "() takes no arguments"))
elif flags & METH_O:
- assert isinstance(w_args, W_TupleObject)
- if len(w_args.wrappeditems) != 1:
+ if length != 1:
raise OperationError(space.w_TypeError,
space.wrap("%s() takes exactly one argument (%d given)" % (
rffi.charp2str(self.ml.c_ml_name),
- len(w_args.wrappeditems))))
- w_arg = w_args.wrappeditems[0]
+ length)))
+ w_arg = space.getitem(w_args, space.wrap(0))
return generic_cpy_call(space, func, w_self, w_arg)
elif flags & METH_VARARGS:
return generic_cpy_call(space, func, w_self, w_args)
else: # METH_OLDARGS, the really old style
- size = len(w_args.wrappeditems)
+ size = length
if size == 1:
- w_arg = w_args.wrappeditems[0]
+ w_arg = space.getitem(w_args, space.wrap(0))
elif size == 0:
w_arg = None
else:
diff --git a/pypy/module/cpyext/test/test_tupleobject.py b/pypy/module/cpyext/test/test_tupleobject.py
--- a/pypy/module/cpyext/test/test_tupleobject.py
+++ b/pypy/module/cpyext/test/test_tupleobject.py
@@ -3,8 +3,10 @@
from pypy.module.cpyext.pyobject import PyObject, PyObjectP, make_ref, from_ref
from pypy.module.cpyext.test.test_api import BaseApiTest
from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.conftest import gettestobjspace
class TestTupleObject(BaseApiTest):
+
def test_tupleobject(self, space, api):
assert not api.PyTuple_Check(space.w_None)
assert api.PyTuple_SetItem(space.w_None, 0, space.w_None) == -1
@@ -20,11 +22,23 @@
ar[0] = rffi.cast(PyObject, make_ref(space, py_tuple))
api._PyTuple_Resize(ar, 2)
py_tuple = from_ref(space, ar[0])
- assert len(py_tuple.wrappeditems) == 2
+ assert space.int_w(space.len(py_tuple)) == 2
api._PyTuple_Resize(ar, 10)
py_tuple = from_ref(space, ar[0])
- assert len(py_tuple.wrappeditems) == 10
+ assert space.int_w(space.len(py_tuple)) == 10
api.Py_DecRef(ar[0])
lltype.free(ar, flavor='raw')
+
+ def test_setitem(self, space, api):
+ atuple = space.newtuple([space.wrap(0), space.wrap("hello")])
+ assert api.PyTuple_Size(atuple) == 2
+ assert space.eq_w(space.getitem(atuple, space.wrap(0)), space.wrap(0))
+ assert space.eq_w(space.getitem(atuple, space.wrap(1)), space.wrap("hello"))
+ w_obj = space.wrap(1)
+ api.Py_IncRef(w_obj)
+ api.PyTuple_SetItem(atuple, 1, w_obj)
+ assert api.PyTuple_Size(atuple) == 2
+ assert space.eq_w(space.getitem(atuple, space.wrap(0)), space.wrap(0))
+ assert space.eq_w(space.getitem(atuple, space.wrap(1)), space.wrap(1))
diff --git a/pypy/module/cpyext/tupleobject.py b/pypy/module/cpyext/tupleobject.py
--- a/pypy/module/cpyext/tupleobject.py
+++ b/pypy/module/cpyext/tupleobject.py
@@ -6,7 +6,7 @@
borrow_from, make_ref, from_ref)
from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
from pypy.objspace.std.tupleobject import W_TupleObject
-
+from pypy.objspace.std.smalltupleobject import W_SmallTupleObject
PyTuple_Check, PyTuple_CheckExact = build_type_checkers("Tuple")
@@ -19,25 +19,30 @@
if not PyTuple_Check(space, w_t):
# XXX this should also steal a reference, test it!!!
PyErr_BadInternalCall(space)
- assert isinstance(w_t, W_TupleObject)
- w_t.wrappeditems[pos] = w_obj
+ _setitem_tuple(w_t, pos, w_obj)
Py_DecRef(space, w_obj) # SetItem steals a reference!
return 0
+def _setitem_tuple(w_t, pos, w_obj):
+ if isinstance(w_t, W_TupleObject):
+ w_t.wrappeditems[pos] = w_obj
+ elif isinstance(w_t, W_SmallTupleObject):
+ w_t.setitem(pos, w_obj)
+ else:
+ assert False
+
@cpython_api([PyObject, Py_ssize_t], PyObject)
def PyTuple_GetItem(space, w_t, pos):
if not PyTuple_Check(space, w_t):
PyErr_BadInternalCall(space)
- assert isinstance(w_t, W_TupleObject)
- w_obj = w_t.wrappeditems[pos]
+ w_obj = space.getitem(w_t, space.wrap(pos))
return borrow_from(w_t, w_obj)
@cpython_api([PyObject], Py_ssize_t, error=CANNOT_FAIL)
def PyTuple_GET_SIZE(space, w_t):
"""Return the size of the tuple p, which must be non-NULL and point to a tuple;
no error checking is performed. """
- assert isinstance(w_t, W_TupleObject)
- return len(w_t.wrappeditems)
+ return space.int_w(space.len(w_t))
@cpython_api([PyObject], Py_ssize_t, error=-1)
def PyTuple_Size(space, ref):
@@ -63,15 +68,14 @@
py_tuple = from_ref(space, ref[0])
if not PyTuple_Check(space, py_tuple):
PyErr_BadInternalCall(space)
- assert isinstance(py_tuple, W_TupleObject)
py_newtuple = PyTuple_New(space, newsize)
to_cp = newsize
- oldsize = len(py_tuple.wrappeditems)
+ oldsize = space.int_w(space.len(py_tuple))
if oldsize < newsize:
to_cp = oldsize
for i in range(to_cp):
- py_newtuple.wrappeditems[i] = py_tuple.wrappeditems[i]
+ _setitem_tuple(py_newtuple, i, space.getitem(py_tuple, space.wrap(i)))
Py_DecRef(space, ref[0])
ref[0] = make_ref(space, py_newtuple)
return 0
diff --git a/pypy/module/oracle/interp_variable.py b/pypy/module/oracle/interp_variable.py
--- a/pypy/module/oracle/interp_variable.py
+++ b/pypy/module/oracle/interp_variable.py
@@ -601,6 +601,7 @@
def getValueProc(self, space, pos):
ptr = rffi.ptradd(self.data, pos * self.bufferSize)
length = rffi.cast(roci.Ptr(roci.ub4), ptr)[0]
+ length = rffi.cast(lltype.Signed, length)
ptr = rffi.ptradd(ptr, rffi.sizeof(roci.ub4))
return space.wrap(rffi.charpsize2str(ptr, length))
diff --git a/pypy/module/pypyjit/test_pypy_c/test_pypy_c_new.py b/pypy/module/pypyjit/test_pypy_c/test_pypy_c_new.py
--- a/pypy/module/pypyjit/test_pypy_c/test_pypy_c_new.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_pypy_c_new.py
@@ -270,9 +270,8 @@
i17 = int_add_ovf(i8, 1)
guard_no_overflow(descr=<Guard5>)
i18 = force_token()
- i20 = int_sub(i17, 1)
--TICK--
- jump(p0, p1, p2, p3, p4, p5, i20, p7, i17, i9, p10, p11, p12, descr=<Loop0>)
+ jump(p0, p1, p2, p3, p4, p5, i8, p7, i17, i9, p10, p11, p12, descr=<Loop0>)
""")
def test_default_and_kw(self):
@@ -481,10 +480,14 @@
assert log.result == (1000, 998)
loop, = log.loops_by_filename(self.filepath)
assert loop.match_by_id('append', """
- p14 = new_with_vtable(ConstClass(W_IntObject))
- setfield_gc(p14, i12, descr=<SignedFieldDescr .*W_IntObject.inst_intval .*>)
- call(ConstClass(ll_append__listPtr_objectPtr), p8, p14, descr=...)
+ i13 = getfield_gc(p8, descr=<SignedFieldDescr list.length .*>)
+ i15 = int_add(i13, 1)
+ call(ConstClass(_ll_list_resize_ge__listPtr_Signed), p8, i15, descr=<VoidCallDescr>)
guard_no_exception(descr=<Guard4>)
+ p17 = getfield_gc(p8, descr=<GcPtrFieldDescr list.items .*>)
+ p19 = new_with_vtable(ConstClass(W_IntObject))
+ setfield_gc(p19, i12, descr=<SignedFieldDescr .*W_IntObject.inst_intval .*>)
+ setarrayitem_gc(p17, i13, p19, descr=<GcPtrArrayDescr>)
""")
def test_range_iter(self):
@@ -1727,4 +1730,4 @@
# few instructions
loop.match_by_id("contains", """
i1 = int_add(i0, 1)
- """)
\ No newline at end of file
+ """)
diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py
--- a/pypy/module/sys/vm.py
+++ b/pypy/module/sys/vm.py
@@ -43,21 +43,20 @@
return space.wrap(f)
def setrecursionlimit(space, w_new_limit):
- """setrecursionlimit() is ignored (and not needed) on PyPy.
-
-On CPython it would set the maximum number of nested calls that can
-occur before a RuntimeError is raised. On PyPy overflowing the stack
-also causes RuntimeErrors, but the limit is checked at a lower level.
-(The limit is currenty hard-coded at 768 KB, corresponding to roughly
-1480 Python calls on Linux.)"""
+ """setrecursionlimit() sets the maximum number of nested calls that
+can occur before a RuntimeError is raised. On PyPy the limit is
+approximative and checked at a lower level. The default 1000
+reserves 768KB of stack space, which should suffice (on Linux,
+depending on the compiler settings) for ~1400 calls. Setting the
+value to N reserves N/1000 times 768KB of stack space.
+"""
+ from pypy.rlib.rstack import _stack_set_length_fraction
new_limit = space.int_w(w_new_limit)
if new_limit <= 0:
raise OperationError(space.w_ValueError,
space.wrap("recursion limit must be positive"))
- # for now, don't rewrite a warning but silently ignore the
- # recursion limit.
- #space.warn('setrecursionlimit() is ignored (and not needed) on PyPy', space.w_RuntimeWarning)
space.sys.recursionlimit = new_limit
+ _stack_set_length_fraction(new_limit * 0.001)
def getrecursionlimit(space):
"""Return the last value set by setrecursionlimit().
diff --git a/pypy/module/test_lib_pypy/test_stackless.py b/pypy/module/test_lib_pypy/test_stackless.py
--- a/pypy/module/test_lib_pypy/test_stackless.py
+++ b/pypy/module/test_lib_pypy/test_stackless.py
@@ -8,15 +8,12 @@
space = gettestobjspace(usemodules=('_stackless', '_socket'))
cls.space = space
# cannot test the unpickle part on top of py.py
- cls.w_can_unpickle = space.wrap(bool(option.runappdirect))
def test_pickle(self):
import new, sys
mod = new.module('mod')
sys.modules['mod'] = mod
- mod.can_unpickle = self.can_unpickle
- mod.skip = skip
try:
exec '''
import pickle, sys
@@ -45,8 +42,6 @@
t = stackless.tasklet(demo)(lev)
stackless.run()
assert seen == range(1, lev+1) + range(lev, 0, -1)
-if not can_unpickle:
- skip("cannot test the unpickling part on top of py.py")
print "now running the clone"
tt = pickle.loads(blob)
tt.insert()
@@ -64,8 +59,6 @@
mod = new.module('mod')
sys.modules['mod'] = mod
- mod.can_unpickle = self.can_unpickle
- mod.skip = skip
try:
exec '''
import pickle, sys
diff --git a/pypy/module/test_lib_pypy/test_tputil.py b/pypy/module/test_lib_pypy/test_tputil.py
--- a/pypy/module/test_lib_pypy/test_tputil.py
+++ b/pypy/module/test_lib_pypy/test_tputil.py
@@ -28,9 +28,9 @@
from tputil import make_proxy
l = []
tp = make_proxy(l.append, type=list)
- x = len(tp)
+ x = tp[0:1]
assert len(l) == 1
- assert l[0].opname == '__len__'
+ assert l[0].opname == '__getslice__'
def test_simple(self):
from tputil import make_proxy
diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py
--- a/pypy/objspace/descroperation.py
+++ b/pypy/objspace/descroperation.py
@@ -207,35 +207,51 @@
return space.get_and_call_function(w_descr, w_obj, w_name)
def is_true(space, w_obj):
- w_descr = space.lookup(w_obj, '__nonzero__')
+ method = "__nonzero__"
+ w_descr = space.lookup(w_obj, method)
if w_descr is None:
- w_descr = space.lookup(w_obj, '__len__')
+ method = "__len__"
+ w_descr = space.lookup(w_obj, method)
if w_descr is None:
return True
w_res = space.get_and_call_function(w_descr, w_obj)
# more shortcuts for common cases
- if w_res is space.w_False:
+ if space.is_w(w_res, space.w_False):
return False
- if w_res is space.w_True:
+ if space.is_w(w_res, space.w_True):
return True
w_restype = space.type(w_res)
- if (space.is_w(w_restype, space.w_bool) or
- space.is_w(w_restype, space.w_int) or
+ # Note there is no check for bool here because the only possible
+ # instances of bool are w_False and w_True, which are checked above.
+ if (space.is_w(w_restype, space.w_int) or
space.is_w(w_restype, space.w_long)):
return space.int_w(w_res) != 0
else:
- raise OperationError(space.w_TypeError,
- space.wrap('__nonzero__ should return '
- 'bool or integer'))
+ msg = "%s should return bool or integer" % (method,)
+ raise OperationError(space.w_TypeError, space.wrap(msg))
- def nonzero(self, w_obj):
- if self.is_true(w_obj):
- return self.w_True
+ def nonzero(space, w_obj):
+ if space.is_true(w_obj):
+ return space.w_True
else:
- return self.w_False
+ return space.w_False
-## def len(self, w_obj):
-## XXX needs to check that the result is an int (or long?) >= 0
+ def len(space, w_obj):
+ w_descr = space.lookup(w_obj, '__len__')
+ if w_descr is None:
+ name = space.type(w_obj).getname(space)
+ msg = "'%s' has no length" % (name,)
+ raise OperationError(space.w_TypeError, space.wrap(msg))
+ w_res = space.get_and_call_function(w_descr, w_obj)
+ space._check_len_result(w_res)
+ return w_res
+
+ def _check_len_result(space, w_obj):
+ # Will complain if result is too big.
+ result = space.int_w(w_obj)
+ if result < 0:
+ raise OperationError(space.w_ValueError,
+ space.wrap("__len__() should return >= 0"))
def iter(space, w_obj):
w_descr = space.lookup(w_obj, '__iter__')
diff --git a/pypy/objspace/std/callmethod.py b/pypy/objspace/std/callmethod.py
--- a/pypy/objspace/std/callmethod.py
+++ b/pypy/objspace/std/callmethod.py
@@ -12,7 +12,7 @@
from pypy.interpreter import function
from pypy.objspace.descroperation import object_getattribute
-from pypy.rlib import jit, rstack # for resume points
+from pypy.rlib import jit
from pypy.objspace.std.mapdict import LOOKUP_METHOD_mapdict, \
LOOKUP_METHOD_mapdict_fill_cache_method
@@ -84,7 +84,6 @@
w_callable = f.peekvalue(n_args + (2 * n_kwargs) + 1)
try:
w_result = f.space.call_valuestack(w_callable, n, f)
- rstack.resume_point("CALL_METHOD", f, n_args, returns=w_result)
finally:
f.dropvalues(n_args + 2)
else:
@@ -109,7 +108,6 @@
w_result = f.space.call_args_and_c_profile(f, w_callable, args)
else:
w_result = f.space.call_args(w_callable, args)
- rstack.resume_point("CALL_METHOD_KW", f, returns=w_result)
f.pushvalue(w_result)
diff --git a/pypy/objspace/std/model.py b/pypy/objspace/std/model.py
--- a/pypy/objspace/std/model.py
+++ b/pypy/objspace/std/model.py
@@ -15,6 +15,7 @@
_registered_implementations.add(implcls)
option_to_typename = {
+ "withsmalltuple" : ["smalltupleobject.W_SmallTupleObject"],
"withsmallint" : ["smallintobject.W_SmallIntObject"],
"withsmalllong" : ["smalllongobject.W_SmallLongObject"],
"withstrslice" : ["strsliceobject.W_StringSliceObject"],
@@ -71,6 +72,7 @@
from pypy.objspace.std import smallintobject
from pypy.objspace.std import smalllongobject
from pypy.objspace.std import tupleobject
+ from pypy.objspace.std import smalltupleobject
from pypy.objspace.std import listobject
from pypy.objspace.std import dictmultiobject
from pypy.objspace.std import stringobject
@@ -253,6 +255,9 @@
(listobject.W_ListObject,
rangeobject.delegate_range2list),
]
+ if config.objspace.std.withsmalltuple:
+ self.typeorder[smalltupleobject.W_SmallTupleObject] += [
+ (tupleobject.W_TupleObject, smalltupleobject.delegate_SmallTuple2Tuple)]
# put W_Root everywhere
self.typeorder[W_Root] = []
diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py
--- a/pypy/objspace/std/objspace.py
+++ b/pypy/objspace/std/objspace.py
@@ -296,9 +296,10 @@
return newlong(self, val)
def newtuple(self, list_w):
+ from pypy.objspace.std.tupletype import wraptuple
assert isinstance(list_w, list)
make_sure_not_resized(list_w)
- return W_TupleObject(list_w)
+ return wraptuple(self, list_w)
def newlist(self, list_w):
return W_ListObject(list_w)
diff --git a/pypy/objspace/std/smalltupleobject.py b/pypy/objspace/std/smalltupleobject.py
new file mode 100644
--- /dev/null
+++ b/pypy/objspace/std/smalltupleobject.py
@@ -0,0 +1,157 @@
+from pypy.interpreter.error import OperationError
+from pypy.objspace.std.model import registerimplementation, W_Object
+from pypy.objspace.std.register_all import register_all
+from pypy.objspace.std.inttype import wrapint
+from pypy.objspace.std.multimethod import FailedToImplement
+from pypy.rlib.rarithmetic import intmask
+from pypy.objspace.std.sliceobject import W_SliceObject, normalize_simple_slice
+from pypy.objspace.std import slicetype
+from pypy.interpreter import gateway
+from pypy.rlib.debug import make_sure_not_resized
+from pypy.rlib.unroll import unrolling_iterable
+from pypy.objspace.std.tupleobject import W_TupleObject
+
+class W_SmallTupleObject(W_Object):
+ from pypy.objspace.std.tupletype import tuple_typedef as typedef
+
+ def tolist(self):
+ raise NotImplementedError
+
+ def length(self):
+ raise NotImplementedError
+
+ def getitem(self, index):
+ raise NotImplementedError
+
+ def hash(self, space):
+ raise NotImplementedError
+
+ def eq(self, space, w_other):
+ raise NotImplementedError
+
+ def setitem(self, index, w_item):
+ raise NotImplementedError
+
+ def unwrap(w_tuple, space):
+ items = [space.unwrap(w_item) for w_item in w_tuple.tolist()]
+ return tuple(items)
+
+def make_specialized_class(n):
+ iter_n = unrolling_iterable(range(n))
+ class cls(W_SmallTupleObject):
+
+ def __init__(self, values):
+ assert len(values) == n
+ for i in iter_n:
+ setattr(self, 'w_value%s' % i, values[i])
+
+ def tolist(self):
+ l = [None] * n
+ for i in iter_n:
+ l[i] = getattr(self, 'w_value%s' % i)
+ return l
+
+ def length(self):
+ return n
+
+ def getitem(self, index):
+ for i in iter_n:
+ if index == i:
+ return getattr(self,'w_value%s' % i)
+ raise IndexError
+
+ def setitem(self, index, w_item):
+ for i in iter_n:
+ if index == i:
+ setattr(self, 'w_value%s' % i, w_item)
+ return
+ raise IndexError
+
+ def eq(self, space, w_other):
+ if self.length() != w_other.length():
+ return space.w_False
+ for i in iter_n:
+ item1 = self.getitem(i)
+ item2 = w_other.getitem(i)
+ if not space.eq_w(item1, item2):
+ return space.w_False
+ return space.w_True
+
+ def hash(self, space):
+ mult = 1000003
+ x = 0x345678
+ z = self.length()
+ for i in iter_n:
+ w_item = self.getitem(i)
+ y = space.int_w(space.hash(w_item))
+ x = (x ^ y) * mult
+ z -= 1
+ mult += 82520 + z + z
+ x += 97531
+ return space.wrap(intmask(x))
+
+ cls.__name__ = "W_SmallTupleObject%s" % n
+ return cls
+
+W_SmallTupleObject2 = make_specialized_class(2)
+W_SmallTupleObject3 = make_specialized_class(3)
+W_SmallTupleObject4 = make_specialized_class(4)
+W_SmallTupleObject5 = make_specialized_class(5)
+W_SmallTupleObject6 = make_specialized_class(6)
+W_SmallTupleObject7 = make_specialized_class(7)
+W_SmallTupleObject8 = make_specialized_class(8)
+
+registerimplementation(W_SmallTupleObject)
+
+def delegate_SmallTuple2Tuple(space, w_small):
+ return W_TupleObject(w_small.tolist())
+
+def len__SmallTuple(space, w_tuple):
+ return space.wrap(w_tuple.length())
+
+def getitem__SmallTuple_ANY(space, w_tuple, w_index):
+ index = space.getindex_w(w_index, space.w_IndexError, "tuple index")
+ if index < 0:
+ index += w_tuple.length()
+ try:
+ return w_tuple.getitem(index)
+ except IndexError:
+ raise OperationError(space.w_IndexError,
+ space.wrap("tuple index out of range"))
+
+def getitem__SmallTuple_Slice(space, w_tuple, w_slice):
+ length = w_tuple.length()
+ start, stop, step, slicelength = w_slice.indices4(space, length)
+ assert slicelength >= 0
+ subitems = [None] * slicelength
+ for i in range(slicelength):
+ subitems[i] = w_tuple.getitem(start)
+ start += step
+ return space.newtuple(subitems)
+
+def mul_smalltuple_times(space, w_tuple, w_times):
+ try:
+ times = space.getindex_w(w_times, space.w_OverflowError)
+ except OperationError, e:
+ if e.match(space, space.w_TypeError):
+ raise FailedToImplement
+ raise
+ if times == 1 and space.type(w_tuple) == space.w_tuple:
+ return w_tuple
+ items = w_tuple.tolist()
+ return space.newtuple(items * times)
+
+def mul__SmallTuple_ANY(space, w_tuple, w_times):
+ return mul_smalltuple_times(space, w_tuple, w_times)
+
+def mul__ANY_SmallTuple(space, w_times, w_tuple):
+ return mul_smalltuple_times(space, w_tuple, w_times)
+
+def eq__SmallTuple_SmallTuple(space, w_tuple1, w_tuple2):
+ return w_tuple1.eq(space, w_tuple2)
+
+def hash__SmallTuple(space, w_tuple):
+ return w_tuple.hash(space)
+
+from pypy.objspace.std import tupletype
+register_all(vars(), tupletype)
diff --git a/pypy/objspace/std/stringobject.py b/pypy/objspace/std/stringobject.py
--- a/pypy/objspace/std/stringobject.py
+++ b/pypy/objspace/std/stringobject.py
@@ -252,15 +252,30 @@
res_w = []
start = 0
- while maxsplit != 0:
- next = value.find(by, start)
- if next < 0:
- break
- res_w.append(sliced(space, value, start, next, w_self))
- start = next + bylen
- maxsplit -= 1 # NB. if it's already < 0, it stays < 0
+ if bylen == 1 and maxsplit < 0:
+ # fast path: uses str.rfind(character) and str.count(character)
+ by = by[0] # annotator hack: string -> char
+ count = value.count(by)
+ res_w = [None] * (count + 1)
+ end = len(value)
+ while count >= 0:
+ assert end >= 0
+ prev = value.rfind(by, 0, end)
+ start = prev + 1
+ assert start >= 0
+ res_w[count] = sliced(space, value, start, end, w_self)
+ count -= 1
+ end = prev
+ else:
+ while maxsplit != 0:
+ next = value.find(by, start)
+ if next < 0:
+ break
+ res_w.append(sliced(space, value, start, next, w_self))
+ start = next + bylen
+ maxsplit -= 1 # NB. if it's already < 0, it stays < 0
+ res_w.append(sliced(space, value, start, len(value), w_self))
- res_w.append(sliced(space, value, start, len(value), w_self))
return space.newlist(res_w)
def str_rsplit__String_None_ANY(space, w_self, w_none, w_maxsplit=-1):
diff --git a/pypy/objspace/std/test/test_smalltupleobject.py b/pypy/objspace/std/test/test_smalltupleobject.py
new file mode 100644
--- /dev/null
+++ b/pypy/objspace/std/test/test_smalltupleobject.py
@@ -0,0 +1,86 @@
+from pypy.objspace.std.tupleobject import W_TupleObject
+from pypy.objspace.std.smalltupleobject import W_SmallTupleObject
+from pypy.interpreter.error import OperationError
+from pypy.objspace.std.test.test_tupleobject import AppTestW_TupleObject
+from pypy.conftest import gettestobjspace
+
+class AppTestW_SmallTupleObject(AppTestW_TupleObject):
+
+ def setup_class(cls):
+ cls.space = gettestobjspace(**{"objspace.std.withsmalltuple": True})
+ cls.w_issmall = cls.space.appexec([], """():
+ import __pypy__
+ def issmall(obj):
+ assert "SmallTuple" in __pypy__.internal_repr(obj)
+ return issmall
+ """)
+
+ def test_smalltuple(self):
+ self.issmall((1,2))
+ self.issmall((1,2,3))
+
+ def test_slicing_to_small(self):
+ self.issmall((1, 2, 3)[0:2]) # SmallTuple2
+ self.issmall((1, 2, 3)[0:2:1])
+
+ self.issmall((1, 2, 3, 4)[0:3]) # SmallTuple3
+ self.issmall((1, 2, 3, 4)[0:3:1])
+
+ def test_adding_to_small(self):
+ self.issmall((1,)+(2,)) # SmallTuple2
+ self.issmall((1,1)+(2,)) # SmallTuple3
+ self.issmall((1,)+(2,3))
+
+ def test_multiply_to_small(self):
+ self.issmall((1,)*2)
+ self.issmall((1,)*3)
+
+ def test_slicing_from_small(self):
+ assert (1,2)[0:1:1] == (1,)
+ assert (1,2,3)[0:2:1] == (1,2)
+
+ def test_eq(self):
+ a = (1,2,3)
+ b = (1,2,3)
+ assert a == b
+
+ c = (1,3,2)
+ assert a != c
+
+ def test_hash(self):
+ a = (1,2,3)
+ b = (1,2,3)
+ assert hash(a) == hash(b)
+
+ c = (1,3,2)
+ assert hash(a) != hash(c)
+
+class TestW_SmallTupleObject():
+
+ def setup_class(cls):
+ cls.space = gettestobjspace(**{"objspace.std.withsmalltuple": True})
+
+ def test_issmalltupleobject(self):
+ w_tuple = self.space.newtuple([self.space.wrap(1), self.space.wrap(2)])
+ assert isinstance(w_tuple, W_SmallTupleObject)
+
+ def test_hash_agains_normal_tuple(self):
+ normalspace = gettestobjspace(**{"objspace.std.withsmalltuple": False})
+ w_tuple = normalspace.newtuple([self.space.wrap(1), self.space.wrap(2)])
+
+ smallspace = gettestobjspace(**{"objspace.std.withsmalltuple": True})
+ w_smalltuple = smallspace.newtuple([self.space.wrap(1), self.space.wrap(2)])
+
+ assert isinstance(w_smalltuple, W_SmallTupleObject)
+ assert isinstance(w_tuple, W_TupleObject)
+ assert not normalspace.is_true(normalspace.eq(w_tuple, w_smalltuple))
+ assert smallspace.is_true(smallspace.eq(w_tuple, w_smalltuple))
+ assert smallspace.is_true(smallspace.eq(normalspace.hash(w_tuple), smallspace.hash(w_smalltuple)))
+
+ def test_setitem(self):
+ w_smalltuple = self.space.newtuple([self.space.wrap(1), self.space.wrap(2)])
+ w_smalltuple.setitem(0, self.space.wrap(5))
+ list_w = w_smalltuple.tolist()
+ assert len(list_w) == 2
+ assert self.space.eq_w(list_w[0], self.space.wrap(5))
+ assert self.space.eq_w(list_w[1], self.space.wrap(2))
diff --git a/pypy/objspace/std/tupleobject.py b/pypy/objspace/std/tupleobject.py
--- a/pypy/objspace/std/tupleobject.py
+++ b/pypy/objspace/std/tupleobject.py
@@ -23,7 +23,7 @@
return "%s(%s)" % (w_self.__class__.__name__, ', '.join(reprlist))
def unwrap(w_tuple, space):
- items = [space.unwrap(w_item) for w_item in w_tuple.wrappeditems] # XXX generic mixed types unwrap
+ items = [space.unwrap(w_item) for w_item in w_tuple.wrappeditems]
return tuple(items)
registerimplementation(W_TupleObject)
@@ -56,12 +56,12 @@
for i in range(slicelength):
subitems[i] = items[start]
start += step
- return W_TupleObject(subitems)
+ return space.newtuple(subitems)
def getslice__Tuple_ANY_ANY(space, w_tuple, w_start, w_stop):
length = len(w_tuple.wrappeditems)
start, stop = normalize_simple_slice(space, length, w_start, w_stop)
- return W_TupleObject(w_tuple.wrappeditems[start:stop])
+ return space.newtuple(w_tuple.wrappeditems[start:stop])
def contains__Tuple_ANY(space, w_tuple, w_obj):
for w_item in w_tuple.wrappeditems:
@@ -76,7 +76,7 @@
def add__Tuple_Tuple(space, w_tuple1, w_tuple2):
items1 = w_tuple1.wrappeditems
items2 = w_tuple2.wrappeditems
- return W_TupleObject(items1 + items2)
+ return space.newtuple(items1 + items2)
def mul_tuple_times(space, w_tuple, w_times):
try:
@@ -88,7 +88,7 @@
if times == 1 and space.type(w_tuple) == space.w_tuple:
return w_tuple
items = w_tuple.wrappeditems
- return W_TupleObject(items * times)
+ return space.newtuple(items * times)
def mul__Tuple_ANY(space, w_tuple, w_times):
return mul_tuple_times(space, w_tuple, w_times)
@@ -162,7 +162,7 @@
return intmask(x)
def getnewargs__Tuple(space, w_tuple):
- return space.newtuple([W_TupleObject(w_tuple.wrappeditems)])
+ return space.newtuple([space.newtuple(w_tuple.wrappeditems)])
def tuple_count__Tuple_ANY(space, w_tuple, w_obj):
count = 0
diff --git a/pypy/objspace/std/tupletype.py b/pypy/objspace/std/tupletype.py
--- a/pypy/objspace/std/tupletype.py
+++ b/pypy/objspace/std/tupletype.py
@@ -3,6 +3,31 @@
from pypy.objspace.std.register_all import register_all
from pypy.objspace.std.stdtypedef import StdTypeDef, SMM
+def wraptuple(space, list_w):
+ from pypy.objspace.std.tupleobject import W_TupleObject
+ from pypy.objspace.std.smalltupleobject import W_SmallTupleObject2
+ from pypy.objspace.std.smalltupleobject import W_SmallTupleObject3
+ from pypy.objspace.std.smalltupleobject import W_SmallTupleObject4
+ from pypy.objspace.std.smalltupleobject import W_SmallTupleObject5
+ from pypy.objspace.std.smalltupleobject import W_SmallTupleObject6
+ from pypy.objspace.std.smalltupleobject import W_SmallTupleObject7
+ from pypy.objspace.std.smalltupleobject import W_SmallTupleObject8
+ if space.config.objspace.std.withsmalltuple:
+ if len(list_w) == 2:
+ return W_SmallTupleObject2(list_w)
+ if len(list_w) == 3:
+ return W_SmallTupleObject3(list_w)
+ if len(list_w) == 4:
+ return W_SmallTupleObject4(list_w)
+ if len(list_w) == 5:
+ return W_SmallTupleObject5(list_w)
+ if len(list_w) == 6:
+ return W_SmallTupleObject6(list_w)
+ if len(list_w) == 7:
+ return W_SmallTupleObject7(list_w)
+ if len(list_w) == 8:
+ return W_SmallTupleObject8(list_w)
+ return W_TupleObject(list_w)
tuple_count = SMM("count", 2,
doc="count(obj) -> number of times obj appears in the tuple")
diff --git a/pypy/objspace/test/test_descroperation.py b/pypy/objspace/test/test_descroperation.py
--- a/pypy/objspace/test/test_descroperation.py
+++ b/pypy/objspace/test/test_descroperation.py
@@ -532,5 +532,23 @@
del X.__nonzero__
assert X()
+ def test_len_overflow(self):
+ import sys
+ class X(object):
+ def __len__(self):
+ return sys.maxsize + 1
+ raises(OverflowError, len, X())
+
+ def test_len_underflow(self):
+ import sys
+ class X(object):
+ def __len__(self):
+ return -1
+ raises(ValueError, len, X())
+ class Y(object):
+ def __len__(self):
+ return -1L
+ raises(ValueError, len, Y())
+
class AppTestWithBuiltinShortcut(AppTest_Descroperation):
OPTIONS = {'objspace.std.builtinshortcut': True}
diff --git a/pypy/rlib/rbigint.py b/pypy/rlib/rbigint.py
--- a/pypy/rlib/rbigint.py
+++ b/pypy/rlib/rbigint.py
@@ -1345,6 +1345,7 @@
# XXX make sure that we don't ignore this!
# YYY no, we decided to do ignore this!
+ at jit.dont_look_inside
def _AsDouble(n):
""" Get a C double from a bigint object. """
# This is a "correctly-rounded" version from Python 2.7.
diff --git a/pypy/rlib/rcoroutine.py b/pypy/rlib/rcoroutine.py
--- a/pypy/rlib/rcoroutine.py
+++ b/pypy/rlib/rcoroutine.py
@@ -29,7 +29,7 @@
The type of a switch is determined by the target's costate.
"""
-from pypy.rlib.rstack import yield_current_frame_to_caller, resume_point
+from pypy.rlib.rstack import yield_current_frame_to_caller
from pypy.rlib.objectmodel import we_are_translated
from pypy.interpreter.error import OperationError
@@ -228,7 +228,6 @@
self.thunk = None
syncstate.switched(incoming_frame)
thunk.call()
- resume_point("coroutine__bind", state)
except Exception, e:
exc = e
raise
@@ -257,7 +256,6 @@
raise CoroutineDamage
state = self.costate
incoming_frame = state.update(self).switch()
- resume_point("coroutine_switch", state, returns=incoming_frame)
syncstate.switched(incoming_frame)
def kill(self):
diff --git a/pypy/rlib/rstack.py b/pypy/rlib/rstack.py
--- a/pypy/rlib/rstack.py
+++ b/pypy/rlib/rstack.py
@@ -42,15 +42,19 @@
sandboxsafe=True, _nowrapper=True,
_callable=_callable)
-_stack_get_start = llexternal('LL_stack_get_start', [], lltype.Signed,
- lambda: 0)
+_stack_get_end = llexternal('LL_stack_get_end', [], lltype.Signed,
+ lambda: 0)
_stack_get_length = llexternal('LL_stack_get_length', [], lltype.Signed,
lambda: 1)
+_stack_set_length_fraction = llexternal('LL_stack_set_length_fraction',
+ [lltype.Float], lltype.Void,
+ lambda frac: None)
_stack_too_big_slowpath = llexternal('LL_stack_too_big_slowpath',
[lltype.Signed], lltype.Char,
lambda cur: '\x00')
# the following is used by the JIT
-_stack_get_start_adr = llexternal('LL_stack_get_start_adr', [], lltype.Signed)
+_stack_get_end_adr = llexternal('LL_stack_get_end_adr', [], lltype.Signed)
+_stack_get_length_adr= llexternal('LL_stack_get_length_adr',[], lltype.Signed)
def stack_check():
@@ -62,13 +66,13 @@
current = llop.stack_current(lltype.Signed)
#
# Load these variables from C code
- start = _stack_get_start()
+ end = _stack_get_end()
length = _stack_get_length()
#
- # Common case: if 'current' is within [start:start+length], everything
+ # Common case: if 'current' is within [end-length:end], everything
# is fine
- ofs = r_uint(current - start)
- if ofs < r_uint(length):
+ ofs = r_uint(end - current)
+ if ofs <= r_uint(length):
return
#
# Else call the slow path
@@ -140,111 +144,6 @@
return var
-def resume_point(label, *args, **kwds):
- pass
-
-
-
-class ResumePointFnEntry(ExtRegistryEntry):
- _about_ = resume_point
-
- def compute_result_annotation(self, s_label, *args_s, **kwds_s):
- from pypy.annotation import model as annmodel
- return annmodel.s_None
-
- def specialize_call(self, hop, **kwds_i):
- from pypy.rpython.lltypesystem import lltype
- from pypy.objspace.flow import model
-
- assert hop.args_s[0].is_constant()
- c_label = hop.inputconst(lltype.Void, hop.args_s[0].const)
- args_v = hop.args_v[1:]
- if 'i_returns' in kwds_i:
- assert len(kwds_i) == 1
- returns_index = kwds_i['i_returns']
- v_return = args_v.pop(returns_index-1)
- assert isinstance(v_return, model.Variable), \
- "resume_point returns= argument must be a Variable"
- else:
- assert not kwds_i
- v_return = hop.inputconst(lltype.Void, None)
-
- for v in args_v:
- assert isinstance(v, model.Variable), "resume_point arguments must be Variables"
-
- hop.exception_is_here()
- return hop.genop('resume_point', [c_label, v_return] + args_v,
- hop.r_result)
-
-def resume_state_create(prevstate, label, *args):
- raise RuntimeError("cannot resume states in non-translated versions")
-
-def concretify_argument(hop, index):
- from pypy.objspace.flow import model
-
- v_arg = hop.args_v[index]
- if isinstance(v_arg, model.Variable):
- return v_arg
-
- r_arg = hop.rtyper.bindingrepr(v_arg)
- return hop.inputarg(r_arg, arg=index)
-
-class ResumeStateCreateFnEntry(FrameStackTopReturningFnEntry):
- _about_ = resume_state_create
-
- def compute_result_annotation(self, s_prevstate, s_label, *args_s):
- return FrameStackTopReturningFnEntry.compute_result_annotation(self)
-
- def specialize_call(self, hop):
- from pypy.rpython.lltypesystem import lltype
-
- assert hop.args_s[1].is_constant()
- c_label = hop.inputconst(lltype.Void, hop.args_s[1].const)
-
- v_state = hop.inputarg(hop.r_result, arg=0)
-
- args_v = []
- for i in range(2, len(hop.args_v)):
- args_v.append(concretify_argument(hop, i))
-
- hop.exception_is_here()
- return hop.genop('resume_state_create', [v_state, c_label] + args_v,
- hop.r_result)
-
-def resume_state_invoke(type, state, **kwds):
- raise NotImplementedError("only works in translated versions")
-
-class ResumeStateInvokeFnEntry(ExtRegistryEntry):
- _about_ = resume_state_invoke
-
- def compute_result_annotation(self, s_type, s_state, **kwds):
- from pypy.annotation.bookkeeper import getbookkeeper
- assert s_type.is_constant()
- return getbookkeeper().valueoftype(s_type.const)
-
- def specialize_call(self, hop, **kwds_i):
- from pypy.rpython.lltypesystem import lltype
- v_state = hop.args_v[1]
-
- if 'i_returning' in kwds_i:
- assert len(kwds_i) == 1
- returning_index = kwds_i['i_returning']
- v_returning = concretify_argument(hop, returning_index)
- v_raising = hop.inputconst(lltype.Void, None)
- elif 'i_raising' in kwds_i:
- assert len(kwds_i) == 1
- raising_index = kwds_i['i_raising']
- v_returning = hop.inputconst(lltype.Void, None)
- v_raising = concretify_argument(hop, raising_index)
- else:
- assert not kwds_i
- v_returning = hop.inputconst(lltype.Void, None)
- v_raising = hop.inputconst(lltype.Void, None)
-
- hop.exception_is_here()
- return hop.genop('resume_state_invoke', [v_state, v_returning, v_raising],
- hop.r_result)
-
# ____________________________________________________________
def get_stack_depth_limit():
diff --git a/pypy/rpython/llinterp.py b/pypy/rpython/llinterp.py
--- a/pypy/rpython/llinterp.py
+++ b/pypy/rpython/llinterp.py
@@ -563,15 +563,6 @@
def op_hint(self, x, hints):
return x
- def op_resume_point(self, *args):
- pass
-
- def op_resume_state_create(self, *args):
- raise RuntimeError("resume_state_create can not be called.")
-
- def op_resume_state_invoke(self, *args):
- raise RuntimeError("resume_state_invoke can not be called.")
-
def op_decode_arg(self, fname, i, name, vargs, vkwds):
raise NotImplementedError("decode_arg")
diff --git a/pypy/rpython/lltypesystem/lloperation.py b/pypy/rpython/lltypesystem/lloperation.py
--- a/pypy/rpython/lltypesystem/lloperation.py
+++ b/pypy/rpython/lltypesystem/lloperation.py
@@ -521,10 +521,6 @@
RuntimeError)),
# can always unwind, not just if stackless gc
- 'resume_point': LLOp(canraise=(Exception,)),
- 'resume_state_create': LLOp(canraise=(MemoryError,), canunwindgc=True),
- 'resume_state_invoke': LLOp(canraise=(Exception, StackException,
- RuntimeError)),
'stack_frames_depth': LLOp(sideeffects=False, canraise=(StackException,
RuntimeError)),
'stack_switch': LLOp(canraise=(StackException, RuntimeError)),
diff --git a/pypy/rpython/lltypesystem/rlist.py b/pypy/rpython/lltypesystem/rlist.py
--- a/pypy/rpython/lltypesystem/rlist.py
+++ b/pypy/rpython/lltypesystem/rlist.py
@@ -237,6 +237,7 @@
l.length = newsize
else:
_ll_list_resize_really(l, newsize)
+_ll_list_resize_ge.oopspec = 'list._resize_ge(l, newsize)'
def _ll_list_resize_le(l, newsize):
if newsize >= (len(l.items) >> 1) - 5:
diff --git a/pypy/rpython/rlist.py b/pypy/rpython/rlist.py
--- a/pypy/rpython/rlist.py
+++ b/pypy/rpython/rlist.py
@@ -568,7 +568,6 @@
length = l.ll_length()
l._ll_resize_ge(length+1) # see "a note about overflows" above
l.ll_setitem_fast(length, newitem)
-ll_append.oopspec = 'list.append(l, newitem)'
# this one is for the special case of insert(0, x)
def ll_prepend(l, newitem):
@@ -793,7 +792,6 @@
raise MemoryError
l1._ll_resize_ge(newlength)
ll_arraycopy(l2, l1, 0, len1, len2)
-ll_extend.oopspec = 'list.extend(l1, l2)'
def ll_extend_with_str(lst, s, getstrlen, getstritem):
return ll_extend_with_str_slice_startonly(lst, s, getstrlen, getstritem, 0)
diff --git a/pypy/translator/backendopt/inline.py b/pypy/translator/backendopt/inline.py
--- a/pypy/translator/backendopt/inline.py
+++ b/pypy/translator/backendopt/inline.py
@@ -541,7 +541,6 @@
'cast_pointer': 0,
'malloc': 2,
'yield_current_frame_to_caller': sys.maxint, # XXX bit extreme
- 'resume_point': sys.maxint, # XXX bit extreme
'instrument_count': 0,
'debug_assert': -1,
}
diff --git a/pypy/translator/backendopt/removenoops.py b/pypy/translator/backendopt/removenoops.py
--- a/pypy/translator/backendopt/removenoops.py
+++ b/pypy/translator/backendopt/removenoops.py
@@ -81,8 +81,6 @@
num_removed += 1
else:
available[key] = op.result
- elif op.opname == 'resume_point':
- available.clear()
if num_removed:
remove_same_as(graph)
# remove casts with unused results
diff --git a/pypy/translator/c/src/debug_print.c b/pypy/translator/c/src/debug_print.c
--- a/pypy/translator/c/src/debug_print.c
+++ b/pypy/translator/c/src/debug_print.c
@@ -6,6 +6,8 @@
#include <stdio.h>
#ifndef _WIN32
#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
#else
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
diff --git a/pypy/translator/c/src/debug_print.h b/pypy/translator/c/src/debug_print.h
--- a/pypy/translator/c/src/debug_print.h
+++ b/pypy/translator/c/src/debug_print.h
@@ -53,8 +53,6 @@
# ifdef _WIN32
# define READ_TIMESTAMP(val) QueryPerformanceCounter((LARGE_INTEGER*)&(val))
# else
-# include <time.h>
-# include <sys/time.h>
long long pypy_read_timestamp();
diff --git a/pypy/translator/c/src/stack.h b/pypy/translator/c/src/stack.h
--- a/pypy/translator/c/src/stack.h
+++ b/pypy/translator/c/src/stack.h
@@ -11,15 +11,18 @@
* It is needed to have RPyThreadStaticTLS, too. */
#include "thread.h"
-extern char *_LLstacktoobig_stack_start;
+extern char *_LLstacktoobig_stack_end;
+extern long _LLstacktoobig_stack_length;
void LL_stack_unwind(void);
char LL_stack_too_big_slowpath(long); /* returns 0 (ok) or 1 (too big) */
+void LL_stack_set_length_fraction(double);
/* some macros referenced from pypy.rlib.rstack */
-#define LL_stack_get_start() ((long)_LLstacktoobig_stack_start)
-#define LL_stack_get_length() MAX_STACK_SIZE
-#define LL_stack_get_start_adr() ((long)&_LLstacktoobig_stack_start) /* JIT */
+#define LL_stack_get_end() ((long)_LLstacktoobig_stack_end)
+#define LL_stack_get_length() _LLstacktoobig_stack_length
+#define LL_stack_get_end_adr() ((long)&_LLstacktoobig_stack_end) /* JIT */
+#define LL_stack_get_length_adr() ((long)&_LLstacktoobig_stack_length)/* JIT */
#ifdef __GNUC__
@@ -32,93 +35,65 @@
#ifndef PYPY_NOT_MAIN_FILE
#include <stdio.h>
-#ifndef PYPY_NOINLINE
-# if defined __GNUC__
-# define PYPY_NOINLINE __attribute__((noinline))
-# else
-// add hints for other compilers here ...
-# define PYPY_NOINLINE
-# endif
-#endif
+/* the current stack is in the interval [end-length:end]. We assume a
+ stack that grows downward here. */
+char *_LLstacktoobig_stack_end = NULL;
+long _LLstacktoobig_stack_length = MAX_STACK_SIZE;
+static RPyThreadStaticTLS end_tls_key;
-long PYPY_NOINLINE _LL_stack_growing_direction(char *parent)
+void LL_stack_set_length_fraction(double fraction)
{
- char local;
- if (parent == NULL)
- return _LL_stack_growing_direction(&local);
- else
- return &local - parent;
+ _LLstacktoobig_stack_length = (long)(MAX_STACK_SIZE * fraction);
}
-char *_LLstacktoobig_stack_start = NULL;
-int stack_direction = 0;
-RPyThreadStaticTLS start_tls_key;
-
char LL_stack_too_big_slowpath(long current)
{
- long diff;
+ long diff, max_stack_size;
char *baseptr, *curptr = (char*)current;
- /* The stack_start variable is updated to match the current value
+ /* The stack_end variable is updated to match the current value
if it is still 0 or if we later find a 'curptr' position
- that is below it. The real stack_start pointer is stored in
+ that is above it. The real stack_end pointer is stored in
thread-local storage, but we try to minimize its overhead by
- keeping a local copy in _LLstacktoobig_stack_start. */
+ keeping a local copy in _LLstacktoobig_stack_end. */
- if (stack_direction == 0) {
+ if (_LLstacktoobig_stack_end == NULL) {
/* not initialized */
/* XXX We assume that initialization is performed early,
when there is still only one thread running. This
allows us to ignore race conditions here */
- char *errmsg = RPyThreadStaticTLS_Create(&start_tls_key);
+ char *errmsg = RPyThreadStaticTLS_Create(&end_tls_key);
if (errmsg) {
/* XXX should we exit the process? */
fprintf(stderr, "Internal PyPy error: %s\n", errmsg);
return 1;
}
- if (_LL_stack_growing_direction(NULL) > 0)
- stack_direction = +1;
- else
- stack_direction = -1;
}
- baseptr = (char *) RPyThreadStaticTLS_Get(start_tls_key);
- if (baseptr != NULL) {
- diff = curptr - baseptr;
- if (((unsigned long)diff) < (unsigned long)MAX_STACK_SIZE) {
+ baseptr = (char *) RPyThreadStaticTLS_Get(end_tls_key);
+ max_stack_size = _LLstacktoobig_stack_length;
+ if (baseptr == NULL) {
+ /* first time we see this thread */
+ }
+ else {
+ diff = baseptr - curptr;
+ if (((unsigned long)diff) <= (unsigned long)max_stack_size) {
/* within bounds, probably just had a thread switch */
- _LLstacktoobig_stack_start = baseptr;
+ _LLstacktoobig_stack_end = baseptr;
return 0;
}
-
- if (stack_direction > 0) {
- if (diff < 0 && diff > -MAX_STACK_SIZE)
- ; /* stack underflow */
- else
- return 1; /* stack overflow (probably) */
+ if (((unsigned long)-diff) <= (unsigned long)max_stack_size) {
+ /* stack underflowed: the initial estimation of
+ the stack base must be revised */
}
- else {
- if (diff >= MAX_STACK_SIZE && diff < 2*MAX_STACK_SIZE)
- ; /* stack underflow */
- else
- return 1; /* stack overflow (probably) */
- }
- /* else we underflowed the stack, which means that
- the initial estimation of the stack base must
- be revised */
+ else
+ return 1; /* stack overflow (probably) */
}
/* update the stack base pointer to the current value */
- if (stack_direction > 0) {
- /* the valid range is [curptr:curptr+MAX_STACK_SIZE] */
- baseptr = curptr;
- }
- else {
- /* the valid range is [curptr-MAX_STACK_SIZE+1:curptr+1] */
- baseptr = curptr - MAX_STACK_SIZE + 1;
- }
- RPyThreadStaticTLS_Set(start_tls_key, baseptr);
- _LLstacktoobig_stack_start = baseptr;
+ baseptr = curptr;
+ RPyThreadStaticTLS_Set(end_tls_key, baseptr);
+ _LLstacktoobig_stack_end = baseptr;
return 0;
}
diff --git a/pypy/translator/c/test/test_standalone.py b/pypy/translator/c/test/test_standalone.py
--- a/pypy/translator/c/test/test_standalone.py
+++ b/pypy/translator/c/test/test_standalone.py
@@ -689,6 +689,44 @@
out = cbuilder.cmdexec("")
assert out.strip() == "hi!"
+ def test_set_length_fraction(self):
+ # check for pypy.rlib.rstack._stack_set_length_fraction()
+ from pypy.rlib.rstack import _stack_set_length_fraction
+ from pypy.rlib.rstackovf import StackOverflow
+ class A:
+ n = 0
+ glob = A()
+ def f(n):
+ glob.n += 1
+ if n <= 0:
+ return 42
+ return f(n+1)
+ def entry_point(argv):
+ _stack_set_length_fraction(0.1)
+ try:
+ return f(1)
+ except StackOverflow:
+ glob.n = 0
+ _stack_set_length_fraction(float(argv[1]))
+ try:
+ return f(1)
+ except StackOverflow:
+ print glob.n
+ return 0
+ t, cbuilder = self.compile(entry_point, stackcheck=True)
+ counts = {}
+ for fraction in [0.1, 0.4, 1.0]:
+ out = cbuilder.cmdexec(str(fraction))
+ print 'counts[%s]: %r' % (fraction, out)
+ counts[fraction] = int(out.strip())
+ #
+ assert counts[1.0] >= 1000
+ # ^^^ should actually be much more than 1000 for this small test
+ assert counts[0.1] < counts[0.4] / 3
+ assert counts[0.4] < counts[1.0] / 2
+ assert counts[0.1] > counts[0.4] / 7
+ assert counts[0.4] > counts[1.0] / 4
+
class TestMaemo(TestStandalone):
def setup_class(cls):
py.test.skip("TestMaemo: tests skipped for now")
diff --git a/pypy/translator/cli/opcodes.py b/pypy/translator/cli/opcodes.py
--- a/pypy/translator/cli/opcodes.py
+++ b/pypy/translator/cli/opcodes.py
@@ -77,7 +77,6 @@
'cast_ptr_to_weakadr': [PushAllArgs, 'newobj instance void class %s::.ctor(object)' % WEAKREF],
'gc__collect': 'call void class [mscorlib]System.GC::Collect()',
'gc_set_max_heap_size': Ignore,
- 'resume_point': Ignore,
'debug_assert': Ignore,
'debug_start_traceback': Ignore,
'debug_record_traceback': Ignore,
@@ -85,6 +84,8 @@
'debug_reraise_traceback': Ignore,
'debug_print_traceback': Ignore,
'debug_print': [DebugPrint],
+ 'debug_flush': [PushAllArgs, 'call void [pypylib]pypy.runtime.DebugPrint::DEBUG_FLUSH()'],
+ 'debug_offset': [PushAllArgs, 'call int32 [pypylib]pypy.runtime.DebugPrint::DEBUG_OFFSET()'],
'debug_start': [PushAllArgs, 'call void [pypylib]pypy.runtime.DebugPrint::DEBUG_START(string)'],
'debug_stop': [PushAllArgs, 'call void [pypylib]pypy.runtime.DebugPrint::DEBUG_STOP(string)'],
'have_debug_prints': [PushAllArgs, 'call bool [pypylib]pypy.runtime.DebugPrint::HAVE_DEBUG_PRINTS()'],
diff --git a/pypy/translator/cli/src/debug.cs b/pypy/translator/cli/src/debug.cs
--- a/pypy/translator/cli/src/debug.cs
+++ b/pypy/translator/cli/src/debug.cs
@@ -38,6 +38,20 @@
return false;
}
+ public static void DEBUG_FLUSH()
+ {
+ if (debug_file != null)
+ debug_file.Flush();
+ }
+
+ public static int DEBUG_OFFSET()
+ {
+ StreamWriter sw = debug_file as StreamWriter;
+ if (sw == null)
+ return -1;
+ return (int)sw.BaseStream.Position; // XXX: the cast might be incorrect
+ }
+
public static bool HAVE_DEBUG_PRINTS()
{
if ((have_debug_prints & 1) != 0) {
diff --git a/pypy/translator/goal/targetpypystandalone.py b/pypy/translator/goal/targetpypystandalone.py
--- a/pypy/translator/goal/targetpypystandalone.py
+++ b/pypy/translator/goal/targetpypystandalone.py
@@ -105,7 +105,8 @@
return parser
def handle_config(self, config, translateconfig):
- if translateconfig._cfgimpl_value_owners['opt'] == 'default':
+ if (not translateconfig.help and
+ translateconfig._cfgimpl_value_owners['opt'] == 'default'):
raise Exception("You have to specify the --opt level.\n"
"Try --opt=2 or --opt=jit, or equivalently -O2 or -Ojit .")
self.translateconfig = translateconfig
diff --git a/pypy/translator/jvm/opcodes.py b/pypy/translator/jvm/opcodes.py
--- a/pypy/translator/jvm/opcodes.py
+++ b/pypy/translator/jvm/opcodes.py
@@ -95,7 +95,6 @@
'gc__collect': jvm.SYSTEMGC,
'gc_set_max_heap_size': Ignore,
- 'resume_point': Ignore,
'jit_marker': Ignore,
'jit_force_virtualizable': Ignore,
'jit_force_virtual': DoNothing,
diff --git a/pypy/translator/oosupport/test_template/operations.py b/pypy/translator/oosupport/test_template/operations.py
--- a/pypy/translator/oosupport/test_template/operations.py
+++ b/pypy/translator/oosupport/test_template/operations.py
@@ -107,12 +107,6 @@
return res
assert self.interpret(fn, [sys.maxint, 2]) == 1
- def test_ignore_resume_point(self):
- def fn(x):
- rstack.resume_point('hello world', x)
- return x
- assert self.interpret(fn, [42]) == 42
-
def test_rshift(self):
def fn(x, y):
return x >> y
diff --git a/pypy/translator/platform/posix.py b/pypy/translator/platform/posix.py
--- a/pypy/translator/platform/posix.py
+++ b/pypy/translator/platform/posix.py
@@ -129,7 +129,9 @@
m.cfiles = rel_cfiles
rel_includedirs = [pypyrel(incldir) for incldir in
- self._preprocess_include_dirs(eci.include_dirs)]
+ self.preprocess_include_dirs(eci.include_dirs)]
+ rel_libdirs = [pypyrel(libdir) for libdir in
+ self.preprocess_library_dirs(eci.library_dirs)]
m.comment('automatically generated makefile')
definitions = [
@@ -139,7 +141,7 @@
('SOURCES', rel_cfiles),
('OBJECTS', rel_ofiles),
('LIBS', self._libs(eci.libraries)),
- ('LIBDIRS', self._libdirs(eci.library_dirs)),
+ ('LIBDIRS', self._libdirs(rel_libdirs)),
('INCLUDEDIRS', self._includedirs(rel_includedirs)),
('CFLAGS', cflags),
('CFLAGSEXTRA', list(eci.compile_extra)),
diff --git a/pypy/translator/platform/test/test_posix.py b/pypy/translator/platform/test/test_posix.py
--- a/pypy/translator/platform/test/test_posix.py
+++ b/pypy/translator/platform/test/test_posix.py
@@ -3,7 +3,7 @@
from pypy.translator.tool.cbuild import ExternalCompilationInfo
from pypy.tool.udir import udir
from StringIO import StringIO
-import sys
+import sys, os
def test_echo():
res = host.execute('echo', '42 24')
@@ -49,6 +49,19 @@
mk.write()
assert 'LINKFILES = /foo/bar.a' in tmpdir.join('Makefile').read()
+ def test_preprocess_localbase(self):
+ tmpdir = udir.join('test_preprocess_localbase').ensure(dir=1)
+ eci = ExternalCompilationInfo()
+ os.environ['PYPY_LOCALBASE'] = '/foo/baz'
+ try:
+ mk = self.platform.gen_makefile(['blip.c'], eci, path=tmpdir)
+ mk.write()
+ finally:
+ del os.environ['PYPY_LOCALBASE']
+ Makefile = tmpdir.join('Makefile').read()
+ assert 'INCLUDEDIRS = -I/foo/baz/include' in Makefile
+ assert 'LIBDIRS = -L/foo/baz/lib' in Makefile
+
class TestMaemo(TestMakefile):
strict_on_stderr = False
diff --git a/pypy/translator/stackless/frame.py b/pypy/translator/stackless/frame.py
--- a/pypy/translator/stackless/frame.py
+++ b/pypy/translator/stackless/frame.py
@@ -104,10 +104,8 @@
class RestartInfo(object):
- """A RestartInfo is created (briefly) for each graph that contains
- a resume point.
-
- In addition, a RestartInfo is created for each function that needs
+ """
+ A RestartInfo is created for each function that needs
to do explicit stackless manipulations
(e.g. code.yield_current_frame_to_caller)."""
diff --git a/pypy/translator/stackless/test/test_coroutine_reconstruction.py b/pypy/translator/stackless/test/test_coroutine_reconstruction.py
deleted file mode 100644
--- a/pypy/translator/stackless/test/test_coroutine_reconstruction.py
+++ /dev/null
@@ -1,68 +0,0 @@
-from pypy.rlib import rcoroutine
-from pypy.rlib import rstack
-from pypy.rlib.rstack import resume_state_create
-from pypy.translator.stackless.test.test_transform import llinterp_stackless_function
-from pypy.rpython.lltypesystem.lloperation import llop
-from pypy.rpython.lltypesystem import lltype
-
-namespace = rcoroutine.make_coroutine_classes(object)
-syncstate = namespace['syncstate']
-AbstractThunk = namespace['AbstractThunk']
-Coroutine = namespace['Coroutine']
-
-class TestCoroutineReconstruction:
-
- def setup_meth(self):
- syncstate.reset()
-
- def test_simple_ish(self):
-
- output = []
- def f(coro, n, x):
- if n == 0:
- coro.switch()
- rstack.resume_point("f_0")
- assert rstack.stack_frames_depth() == 9
- return
- f(coro, n-1, 2*x)
- rstack.resume_point("f_1", coro, n, x)
- output.append(x)
-
- class T(AbstractThunk):
- def __init__(self, arg_coro, arg_n, arg_x):
- self.arg_coro = arg_coro
- self.arg_n = arg_n
- self.arg_x = arg_x
- def call(self):
- f(self.arg_coro, self.arg_n, self.arg_x)
-
- def example():
- main_coro = Coroutine.getcurrent()
- sub_coro = Coroutine()
- thunk_f = T(main_coro, 5, 1)
- sub_coro.bind(thunk_f)
- sub_coro.switch()
-
- new_coro = Coroutine()
- new_thunk_f = T(main_coro, 5, 1)
- new_coro.bind(new_thunk_f)
-
- costate = Coroutine._get_default_costate()
- bottom = resume_state_create(None, "yield_current_frame_to_caller_1")
- _bind_frame = resume_state_create(bottom, "coroutine__bind", costate)
- f_frame_1 = resume_state_create(_bind_frame, "f_1", main_coro, 5, 1)
- f_frame_2 = resume_state_create(f_frame_1, "f_1", main_coro, 4, 2)
- f_frame_3 = resume_state_create(f_frame_2, "f_1", main_coro, 3, 4)
- f_frame_4 = resume_state_create(f_frame_3, "f_1", main_coro, 2, 8)
- f_frame_5 = resume_state_create(f_frame_4, "f_1", main_coro, 1, 16)
- f_frame_0 = resume_state_create(f_frame_5, "f_0")
- switch_frame = resume_state_create(f_frame_0, "coroutine_switch", costate)
-
- new_coro.frame = switch_frame
-
- new_coro.switch()
- return output == [16, 8, 4, 2, 1]
-
- res = llinterp_stackless_function(example)
- assert res == 1
-
diff --git a/pypy/translator/stackless/test/test_resume_point.py b/pypy/translator/stackless/test/test_resume_point.py
deleted file mode 100644
--- a/pypy/translator/stackless/test/test_resume_point.py
+++ /dev/null
@@ -1,457 +0,0 @@
-from pypy.translator.stackless.transform import StacklessTransformer
-from pypy.translator.stackless.test.test_transform import llinterp_stackless_function, rtype_stackless_function, one, run_stackless_function
-from pypy import conftest
-import py
-from pypy.rlib import rstack
-
-def do_backendopt(t):
- from pypy.translator.backendopt import all
- all.backend_optimizations(t)
-
-def transform_stackless_function(fn, callback_for_transform=None):
- def wrapper(argv):
- return fn()
- t = rtype_stackless_function(wrapper)
- if callback_for_transform:
- callback_for_transform(t)
- if conftest.option.view:
- t.view()
- st = StacklessTransformer(t, wrapper, False)
- st.transform_all()
-
-def test_no_call():
- def f(x, y):
- x = x-1
- rstack.resume_point("rp0", x, y)
- r = x+y
- rstack.stack_unwind()
- return r
- def example():
- v1 = f(one(),one()+one())
- state = rstack.resume_state_create(None, "rp0", one(), one()+one()+one())
- v2 = rstack.resume_state_invoke(int, state)
- return v1*10 + v2
-## transform_stackless_function(example)
- res = llinterp_stackless_function(example, assert_unwind=False)
- assert res == 24
-
-def test_bogus_restart_state_create():
- def f(x, y):
- x = x-1
- rstack.resume_point("rp0", x, y)
- return x+y
- def example():
- v1 = f(one(),one()+one())
- state = rstack.resume_state_create(None, "rp0", one())
- return v1
- info = py.test.raises(AssertionError, "transform_stackless_function(example)")
- assert 'rp0' in str(info.value)
-
-
-def test_call():
- def g(x,y):
- return x*y
- def f(x, y):
- z = g(x,y)
- rstack.resume_point("rp1", y, returns=z)
- return z+y
- def example():
- v1 = f(one(),one()+one())
- s = rstack.resume_state_create(None, "rp1", 5*one())
- v2 = rstack.resume_state_invoke(int, s, returning=one()*7)
- return v1*100 + v2
- res = llinterp_stackless_function(example)
- assert res == 412
- res = run_stackless_function(example)
- assert res == 412
-
-def test_returns_with_instance():
- class C:
- def __init__(self, x):
- self.x = x
- def g(x):
- return C(x+1)
- def f(x, y):
- r = g(x)
- rstack.resume_point("rp1", y, returns=r)
- return r.x + y
- def example():
- v1 = f(one(),one()+one())
- s = rstack.resume_state_create(None, "rp1", 5*one())
- v2 = rstack.resume_state_invoke(int, s, returning=C(one()*3))
- return v1*100 + v2
- res = llinterp_stackless_function(example, assert_unwind=False)
- assert res == 408
- res = run_stackless_function(example)
- assert res == 408
-
-def test_call_uncovered():
- def g(x,y):
- return x*y
- def f(x, y):
- z = g(x,y)
- rstack.resume_point("rp1", y, returns=z)
- return z+y+x
- def example():
- f(one(),one()+one())
- return 0
- e = py.test.raises(Exception, transform_stackless_function, example)
- msg, = e.value.args
- assert msg.startswith('not covered needed value at resume_point') and 'rp1' in msg
-
-def test_chained_states():
- def g(x, y):
- x += 1
- rstack.resume_point("rp1", x, y)
- return x + y
- def f(x, y, z):
- y += 1
- r = g(x, y)
- rstack.resume_point("rp2", z, returns=r)
- return r + z
- def example():
- v1 = f(one(), 2*one(), 3*one())
- s2 = rstack.resume_state_create(None, "rp2", 2*one())
- s1 = rstack.resume_state_create(s2, "rp1", 4*one(), 5*one())
- return 100*v1 + rstack.resume_state_invoke(int, s1)
- res = llinterp_stackless_function(example)
- assert res == 811
- res = run_stackless_function(example)
- assert res == 811
-
-def test_return_instance():
- class C:
- pass
- def g(x):
- c = C()
- c.x = x + 1
- rstack.resume_point("rp1", c)
- return c
- def f(x, y):
- r = g(x)
- rstack.resume_point("rp2", y, returns=r)
- return r.x + y
- def example():
- v1 = f(one(), 2*one())
- s2 = rstack.resume_state_create(None, "rp2", 2*one())
- c = C()
- c.x = 4*one()
- s1 = rstack.resume_state_create(s2, "rp1", c)
- return v1*100 + rstack.resume_state_invoke(int, s1)
- res = llinterp_stackless_function(example)
- assert res == 406
- res = run_stackless_function(example)
- assert res == 406
-
-def test_really_return_instance():
- class C:
- pass
- def g(x):
- c = C()
- c.x = x + 1
- rstack.resume_point("rp1", c)
- return c
- def example():
- v1 = g(one()).x
- c = C()
- c.x = 4*one()
- s1 = rstack.resume_state_create(None, "rp1", c)
- return v1*100 + rstack.resume_state_invoke(C, s1).x
- res = llinterp_stackless_function(example)
- assert res == 204
- res = run_stackless_function(example)
- assert res == 204
-
-def test_resume_and_raise():
- def g(x):
- rstack.resume_point("rp0", x)
- if x == 0:
- raise KeyError
- return x + 1
- def example():
- v1 = g(one())
- s = rstack.resume_state_create(None, "rp0", one()-1)
- try:
- v2 = rstack.resume_state_invoke(int, s)
- except KeyError:
- v2 = 42
- return v1*100 + v2
- res = llinterp_stackless_function(example)
- assert res == 242
- res = run_stackless_function(example)
- assert res == 242
-
-def test_resume_and_raise_and_catch():
- def g(x):
- rstack.resume_point("rp0", x)
- if x == 0:
- raise KeyError
- return x + 1
- def f(x):
- x = x - 1
- try:
- r = g(x)
- rstack.resume_point("rp1", returns=r)
- except KeyError:
- r = 42
- return r - 1
- def example():
- v1 = f(one()+one())
- s1 = rstack.resume_state_create(None, "rp1")
- s0 = rstack.resume_state_create(s1, "rp0", one()-1)
- v2 = rstack.resume_state_invoke(int, s0)
- return v1*100 + v2
- res = llinterp_stackless_function(example)
- assert res == 141
- res = run_stackless_function(example)
- assert res == 141
-
-def test_invoke_raising():
- def g(x):
- rstack.resume_point("rp0", x)
- return x + 1
- def f(x):
- x = x - 1
- try:
- r = g(x)
- rstack.resume_point("rp1", returns=r)
- except KeyError:
- r = 42
- return r - 1
- def example():
- v1 = f(one()+one())
- s1 = rstack.resume_state_create(None, "rp1")
- s0 = rstack.resume_state_create(s1, "rp0", 0)
- v2 = rstack.resume_state_invoke(int, s0, raising=KeyError())
- return v1*100 + v2
- res = llinterp_stackless_function(example)
- assert res == 141
- res = run_stackless_function(example)
- assert res == 141
-
-
-def test_finally():
- def f(x):
- rstack.resume_point("rp1", x)
- return 1/x
- def in_finally(x):
- rstack.resume_point("rp1.5", x)
- return 2/x
- def g(x):
- r = y = 0
- r += f(x)
- try:
- y = f(x)
- rstack.resume_point("rp0", x, r, returns=y)
- finally:
- r += in_finally(x)
- return r + y
- def example():
- return g(one())
- transform_stackless_function(example)
-
-def test_except():
- py.test.skip("please don't write code like this")
- def f(x):
- rstack.resume_point("rp1", x)
- return 1/x
- def g(x):
- r = y = 0
- r += f(x)
- try:
- y = f(x)
- rstack.resume_point("rp0", x, r, y, returns=y)
- except ZeroDivisionError:
- r += f(x)
- return r + y
- def example():
- return g(one())
- transform_stackless_function(example)
-
-def test_using_pointers():
- from pypy.interpreter.miscutils import FixedStack
- class Arguments:
- def __init__(self, a, b, c, d, e):
- pass
- class W_Root:
- pass
- class FakeFrame:
- def __init__(self, space):
- self.space = space
- self.valuestack = FixedStack()
- self.valuestack.setup(10)
- self.valuestack.push(W_Root())
- class FakeSpace:
- def call_args(self, args, kw):
- return W_Root()
- def str_w(self, ob):
- return 'a string'
- def call_function(f, oparg, w_star=None, w_starstar=None):
- n_arguments = oparg & 0xff
- n_keywords = (oparg>>8) & 0xff
- keywords = None
- if n_keywords:
- keywords = {}
- for i in range(n_keywords):
- w_value = f.valuestack.pop()
- w_key = f.valuestack.pop()
- key = f.space.str_w(w_key)
- keywords[key] = w_value
- arguments = [None] * n_arguments
- for i in range(n_arguments - 1, -1, -1):
- arguments[i] = f.valuestack.pop()
- args = Arguments(f.space, arguments, keywords, w_star, w_starstar)
- w_function = f.valuestack.pop()
- w_result = f.space.call_args(w_function, args)
- rstack.resume_point("call_function", f, returns=w_result)
- f.valuestack.push(w_result)
- def example():
- s = FakeSpace()
- f = FakeFrame(s)
- call_function(f, 100, W_Root(), W_Root())
- return one()
- transform_stackless_function(example, do_backendopt)
-
-def test_always_raising():
- def g(out):
- out.append(3)
- rstack.resume_point('g')
- raise KeyError
-
- def h(out):
- try:
- # g is always raising, good enough to put the resume point
- # before, instead of after!
- rstack.resume_point('h', out)
- g(out)
- except KeyError:
- return 0
- return -1
-
- def example():
- out = []
- x = h(out)
- l = len(out)
- chain = rstack.resume_state_create(None, 'h', out)
- chain = rstack.resume_state_create(chain, 'g')
- x += rstack.resume_state_invoke(int, chain)
- l += len(out)
- return l*100+x
-
- res = llinterp_stackless_function(example)
- assert res == 200
- res = run_stackless_function(example)
- assert res == 200
-
-def test_more_mess():
- from pypy.interpreter.miscutils import Stack
-
- def new_framestack():
- return Stack()
-
- class FakeFrame:
- pass
- class FakeSlpFrame:
- def switch(self):
- rstack.stack_unwind()
- return FakeSlpFrame()
-
- class FakeCoState:
- def update(self, new):
- self.last, self.current = self.current, new
- frame, new.frame = new.frame, None
- return frame
- def do_things_to_do(self):
- self.do_things_to_do()
-
- costate = FakeCoState()
- costate.current = None
-
- class FakeExecutionContext:
- def __init__(self):
- self.space = space
- self.framestack = new_framestack()
-
- def subcontext_new(coobj):
- coobj.framestack = new_framestack()
- subcontext_new = staticmethod(subcontext_new)
-
- def subcontext_enter(self, next):
- self.framestack = next.framestack
-
- def subcontext_leave(self, current):
- current.framestack = self.framestack
-
- class FakeSpace:
- def __init__(self):
- self.ec = None
- def getexecutioncontext(self):
- if self.ec is None:
- self.ec = FakeExecutionContext()
- return self.ec
-
- space = FakeSpace()
-
- class MainCoroutineGetter(object):
- def __init__(self):
- self.costate = None
- def _get_default_costate(self):
- if self.costate is None:
- costate = FakeCoState()
- self.costate = costate
- return costate
- return self.costate
-
- main_coroutine_getter = MainCoroutineGetter()
-
- class FakeCoroutine:
- def __init__(self):
- self.frame = None
- self.costate = costate
- space.getexecutioncontext().subcontext_new(self)
-
- def switch(self):
- if self.frame is None:
- raise RuntimeError
- state = self.costate
- incoming_frame = state.update(self).switch()
- rstack.resume_point("coroutine_switch", self, state, returns=incoming_frame)
- left = state.last
- left.frame = incoming_frame
- left.goodbye()
- self.hello()
- #main_coroutine_getter._get_default_costate().do_things_to_do()
-
- def hello(self):
- pass
-
- def goodbye(self):
- pass
-
- class FakeAppCoroutine(FakeCoroutine):
- def __init__(self):
- FakeCoroutine.__init__(self)
- self.space = space
-
- def hello(self):
- ec = self.space.getexecutioncontext()
- ec.subcontext_enter(self)
-
- def goodbye(self):
- ec = self.space.getexecutioncontext()
- ec.subcontext_leave(self)
-
- def example():
- coro = FakeAppCoroutine()
- othercoro = FakeCoroutine()
- othercoro.frame = FakeSlpFrame()
- if one():
- coro.frame = FakeSlpFrame()
- if one() - one():
- coro.costate = FakeCoState()
- coro.costate.last = coro.costate.current = othercoro
- space.getexecutioncontext().framestack.push(FakeFrame())
- coro.switch()
- return one()
-
- transform_stackless_function(example, do_backendopt)
diff --git a/pypy/translator/stackless/transform.py b/pypy/translator/stackless/transform.py
--- a/pypy/translator/stackless/transform.py
+++ b/pypy/translator/stackless/transform.py
@@ -112,19 +112,6 @@
# abort()
# return retval + x + 1
-class SymbolicRestartNumber(ComputedIntSymbolic):
- def __init__(self, label, value=None):
- ComputedIntSymbolic.__init__(self, self._getvalue)
- self.label = label
- self.value = value
-
- def _getvalue(self):
- # argh, we'd like to assert-fail if value is None here, but we
- # get called too early (during databasing) for this to be
- # valid. so we might return None and rely on the database
- # checking that this only happens before the database is
- # complete.
- return self.value
# the strategy for sharing parts of the resume code:
#
@@ -248,8 +235,7 @@
self.stackless_gc = stackless_gc
def analyze_simple_operation(self, op, graphinfo):
- if op.opname in ('yield_current_frame_to_caller', 'resume_point',
- 'resume_state_invoke', 'resume_state_create', 'stack_frames_depth',
+ if op.opname in ('yield_current_frame_to_caller', 'stack_frames_depth',
'stack_switch', 'stack_unwind', 'stack_capture',
'get_stack_depth_limit', 'set_stack_depth_limit'):
return True
@@ -458,24 +444,11 @@
self.is_finished = False
- # only for sanity checking, but still very very important
- self.explicit_resume_point_data = {}
-
- self.symbolic_restart_numbers = {}
-
- # register the prebuilt restartinfos & give them names for use
- # with resume_state_create
# the mauling of frame_typer internals should be a method on FrameTyper.
for restartinfo in frame.RestartInfo.prebuilt:
name = restartinfo.func_or_graph.__name__
for i in range(len(restartinfo.frame_types)):
- label = name + '_' + str(i)
- assert label not in self.symbolic_restart_numbers
- # XXX we think this is right:
- self.symbolic_restart_numbers[label] = SymbolicRestartNumber(
- label, len(self.masterarray1) + i)
frame_type = restartinfo.frame_types[i]
- self.explicit_resume_point_data[label] = frame_type
self.frametyper.ensure_frame_type_for_types(frame_type)
self.register_restart_info(restartinfo)
@@ -589,156 +562,6 @@
# yes
convertblock.exits[0].args[index] = newvar
# end ouch!
-
- def handle_resume_point(self, block, i):
- # in some circumstances we might be able to reuse
- # an already inserted resume point
- op = block.operations[i]
- if i == len(block.operations) - 1:
- link = block.exits[0]
- nextblock = None
- else:
- link = split_block(None, block, i+1)
- i = 0
- nextblock = link.target
-
- label = op.args[0].value
-
- parms = op.args[1:]
- if not isinstance(parms[0], model.Variable):
- assert parms[0].value is None
- parms[0] = None
- args = vars_to_save(block)
- for a in args:
- if a not in parms:
- raise Exception, "not covered needed value at resume_point %r"%(label,)
- if parms[0] is not None: # returns= case
- res = parms[0]
- args = [arg for arg in args if arg is not res]
- else:
- args = args
- res = op.result
-
- (FRAME_TYPE, varsforcall, saver) = self.frametyper.frame_type_for_vars(parms[1:])
-
- if label in self.explicit_resume_point_data:
- OTHER_TYPE = self.explicit_resume_point_data[label]
- assert FRAME_TYPE == OTHER_TYPE, "inconsistent types for label %r"%(label,)
- else:
- self.explicit_resume_point_data[label] = FRAME_TYPE
-
- self._make_resume_handling(FRAME_TYPE, varsforcall, res, block.exits)
-
- restart_number = len(self.masterarray1) + len(self.resume_blocks) - 1
-
- if label in self.symbolic_restart_numbers:
- symb = self.symbolic_restart_numbers[label]
- assert symb.value is None
- symb.value = restart_number
- else:
- symb = SymbolicRestartNumber(label, restart_number)
- self.symbolic_restart_numbers[label] = symb
-
- return nextblock
-
- def handle_resume_state_create(self, block, i):
- op = block.operations[i]
- llops = LowLevelOpList()
- label = op.args[1].value
- parms = op.args[2:]
- FRAME_TYPE, varsforcall, saver = self.frametyper.frame_type_for_vars(parms)
-
- if label in self.explicit_resume_point_data:
- OTHER_TYPE = self.explicit_resume_point_data[label]
- assert FRAME_TYPE == OTHER_TYPE, "inconsistent types for label %r"%(label,)
- else:
- self.explicit_resume_point_data[label] = FRAME_TYPE
-
- if label in self.symbolic_restart_numbers:
- symb = self.symbolic_restart_numbers[label]
- else:
- symb = SymbolicRestartNumber(label)
- self.symbolic_restart_numbers[label] = symb
-
- # this is rather insane: we create an exception object, pass
- # it to the saving function, then read the thus created state
- # out of and then clear global_state.top
- c_EXC = model.Constant(self.unwind_exception_type.TO, lltype.Void)
- c_flags = model.Constant({'flavor': 'gc'}, lltype.Void)
- v_exc = llops.genop('malloc', [c_EXC, c_flags],
- resulttype = self.unwind_exception_type)
- llops.genop('setfield', [v_exc,
- model.Constant('inst_depth', lltype.Void),
- model.Constant(0, lltype.Signed)])
-
- realvarsforcall = []
- for v in varsforcall:
- if v.concretetype != lltype.Void:
- realvarsforcall.append(gen_cast(llops, storage_type(v.concretetype), v))
-
- llops.genop('direct_call',
- [model.Constant(saver, lltype.typeOf(saver)), v_exc,
- model.Constant(symb, lltype.Signed)] + realvarsforcall,
- resulttype = lltype.Void)
- v_state = varoftype(lltype.Ptr(frame.STATE_HEADER))
- v_state_hdr = llops.genop("getfield",
- [self.ll_global_state, self.c_inst_top_name],
- resulttype=lltype.Ptr(STATE_HEADER))
- v_state = gen_cast(llops, lltype.Ptr(FRAME_TYPE), v_state_hdr)
- llops.genop("setfield",
- [self.ll_global_state, self.c_inst_top_name, self.c_null_state])
-
- v_prevstate = gen_cast(llops, lltype.Ptr(frame.STATE_HEADER), op.args[0])
- llops.genop('direct_call', [self.set_back_pointer_ptr,
- v_state_hdr, v_prevstate])
- llops.append(model.SpaceOperation('cast_opaque_ptr', [v_state_hdr], op.result))
- block.operations[i:i+1] = llops
-
- def handle_resume_state_invoke(self, block):
- op = block.operations[-1]
- assert op.opname == 'resume_state_invoke'
- # some commentary.
- #
- # we don't want to write 155 or so different versions of
- # resume_after_foo that appear to the annotator to return
- # different types. we take advantage of the fact that this
- # function always raises UnwindException and have it (appear
- # to) return Void. then to placate all the other machinery,
- # we pass a constant zero-of-the-appropriate-type along the
- # non-exceptional link (which we know will never be taken).
- # Nota Bene: only mutate a COPY of the non-exceptional link
- # because the non-exceptional link has been stored in
- # self.resume_blocks and we don't want a constant "zero" in
- # there.
- v_state = op.args[0]
- v_returning = op.args[1]
- v_raising = op.args[2]
- llops = LowLevelOpList()
-
- if v_raising.concretetype == lltype.Void:
- erased_type = storage_type(v_returning.concretetype)
- resume_after_ptr = self.resume_afters[erased_type]
- v_param = v_returning
- else:
- assert v_returning.concretetype == lltype.Void
- erased_type = self.exception_type
- resume_after_ptr = self.resume_after_raising_ptr
- v_param = v_raising
-
- if erased_type != v_param.concretetype:
- v_param = gen_cast(llops, erased_type, v_param)
- llops.genop('direct_call', [resume_after_ptr, v_state, v_param],
- resulttype=lltype.Void)
-
- del block.operations[-1]
- block.operations.extend(llops)
-
- noexclink = block.exits[0].copy()
- realrettype = op.result.concretetype
- for i, a in enumerate(noexclink.args):
- if a is op.result:
- noexclink.args[i] = model.Constant(realrettype._defl(), realrettype)
- block.recloseblock(*((noexclink,) + block.exits[1:]))
def insert_unwind_handling(self, block, i):
# for the case where we are resuming to an except:
@@ -821,19 +644,8 @@
op = replace_with_call(self.operation_replacement[op.opname])
stackless_op = True
- if op.opname == 'resume_state_create':
- self.handle_resume_state_create(block, i)
- continue # go back and look at that malloc
-
if (op.opname in ('direct_call', 'indirect_call')
or self.analyzer.analyze(op)):
- if op.opname == 'resume_point':
- block = self.handle_resume_point(block, i)
- if block is None:
- return
- else:
- i = 0
- continue
if not stackless_op and not self.analyzer.analyze(op):
i += 1
@@ -849,9 +661,7 @@
continue
nextblock = self.insert_unwind_handling(block, i)
- if op.opname == 'resume_state_invoke':
- self.handle_resume_state_invoke(block)
-
+
if nextblock is None:
return
More information about the pypy-commit
mailing list