[pypy-commit] pypy py3k: Merge default
amauryfa
noreply at buildbot.pypy.org
Mon Oct 17 19:57:44 CEST 2011
Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: py3k
Changeset: r48155:a0e3eb8d4121
Date: 2011-10-17 19:56 +0200
http://bitbucket.org/pypy/pypy/changeset/a0e3eb8d4121/
Log: Merge default
diff --git a/lib-python/modified-2.7/test/test_array.py b/lib-python/modified-2.7/test/test_array.py
--- a/lib-python/modified-2.7/test/test_array.py
+++ b/lib-python/modified-2.7/test/test_array.py
@@ -295,9 +295,10 @@
)
b = array.array(self.badtypecode())
- self.assertRaises(TypeError, "a + b")
-
- self.assertRaises(TypeError, "a + 'bad'")
+ with self.assertRaises(TypeError):
+ a + b
+ with self.assertRaises(TypeError):
+ a + 'bad'
def test_iadd(self):
a = array.array(self.typecode, self.example[::-1])
@@ -316,9 +317,10 @@
)
b = array.array(self.badtypecode())
- self.assertRaises(TypeError, "a += b")
-
- self.assertRaises(TypeError, "a += 'bad'")
+ with self.assertRaises(TypeError):
+ a += b
+ with self.assertRaises(TypeError):
+ a += 'bad'
def test_mul(self):
a = 5*array.array(self.typecode, self.example)
@@ -345,7 +347,8 @@
array.array(self.typecode)
)
- self.assertRaises(TypeError, "a * 'bad'")
+ with self.assertRaises(TypeError):
+ a * 'bad'
def test_imul(self):
a = array.array(self.typecode, self.example)
@@ -374,7 +377,8 @@
a *= -1
self.assertEqual(a, array.array(self.typecode))
- self.assertRaises(TypeError, "a *= 'bad'")
+ with self.assertRaises(TypeError):
+ a *= 'bad'
def test_getitem(self):
a = array.array(self.typecode, self.example)
diff --git a/lib_pypy/resource.py b/lib_pypy/resource.py
--- a/lib_pypy/resource.py
+++ b/lib_pypy/resource.py
@@ -7,7 +7,7 @@
from ctypes_support import standard_c_lib as libc
from ctypes_support import get_errno
-from ctypes import Structure, c_int, c_long, byref, sizeof, POINTER
+from ctypes import Structure, c_int, c_long, byref, POINTER
from errno import EINVAL, EPERM
import _structseq
@@ -165,7 +165,6 @@
@builtinify
def getpagesize():
- pagesize = 0
if _getpagesize:
return _getpagesize()
else:
diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -127,7 +127,7 @@
pypy_optiondescription = OptionDescription("objspace", "Object Space Options", [
ChoiceOption("name", "Object Space name",
- ["std", "flow", "thunk", "dump", "taint"],
+ ["std", "flow", "thunk", "dump"],
"std",
cmdline='--objspace -o'),
diff --git a/pypy/doc/__pypy__-module.rst b/pypy/doc/__pypy__-module.rst
--- a/pypy/doc/__pypy__-module.rst
+++ b/pypy/doc/__pypy__-module.rst
@@ -37,29 +37,6 @@
.. _`thunk object space docs`: objspace-proxies.html#thunk
.. _`interface section of the thunk object space docs`: objspace-proxies.html#thunk-interface
-.. broken:
-
- Taint Object Space Functionality
- ================================
-
- When the taint object space is used (choose with :config:`objspace.name`),
- the following names are put into ``__pypy__``:
-
- - ``taint``
- - ``is_tainted``
- - ``untaint``
- - ``taint_atomic``
- - ``_taint_debug``
- - ``_taint_look``
- - ``TaintError``
-
- Those are all described in the `interface section of the taint object space
- docs`_.
-
- For more detailed explanations and examples see the `taint object space docs`_.
-
- .. _`taint object space docs`: objspace-proxies.html#taint
- .. _`interface section of the taint object space docs`: objspace-proxies.html#taint-interface
Transparent Proxy Functionality
===============================
diff --git a/pypy/doc/config/objspace.name.txt b/pypy/doc/config/objspace.name.txt
--- a/pypy/doc/config/objspace.name.txt
+++ b/pypy/doc/config/objspace.name.txt
@@ -4,7 +4,6 @@
for normal usage):
* thunk_: The thunk object space adds lazy evaluation to PyPy.
- * taint_: The taint object space adds soft security features.
* dump_: Using this object spaces results in the dumpimp of all operations
to a log.
@@ -12,5 +11,4 @@
.. _`Object Space Proxies`: ../objspace-proxies.html
.. _`Standard Object Space`: ../objspace.html#standard-object-space
.. _thunk: ../objspace-proxies.html#thunk
-.. _taint: ../objspace-proxies.html#taint
.. _dump: ../objspace-proxies.html#dump
diff --git a/pypy/doc/index.rst b/pypy/doc/index.rst
--- a/pypy/doc/index.rst
+++ b/pypy/doc/index.rst
@@ -309,7 +309,6 @@
.. _`object space`: objspace.html
.. _FlowObjSpace: objspace.html#the-flow-object-space
.. _`trace object space`: objspace.html#the-trace-object-space
-.. _`taint object space`: objspace-proxies.html#taint
.. _`thunk object space`: objspace-proxies.html#thunk
.. _`transparent proxies`: objspace-proxies.html#tproxy
.. _`Differences between PyPy and CPython`: cpython_differences.html
diff --git a/pypy/doc/objspace-proxies.rst b/pypy/doc/objspace-proxies.rst
--- a/pypy/doc/objspace-proxies.rst
+++ b/pypy/doc/objspace-proxies.rst
@@ -129,297 +129,6 @@
function behaves lazily: all calls to it return a thunk object.
-.. broken right now:
-
- .. _taint:
-
- The Taint Object Space
- ======================
-
- Motivation
- ----------
-
- The Taint Object Space provides a form of security: "tainted objects",
- inspired by various sources, see [D12.1]_ for a more detailed discussion.
-
- The basic idea of this kind of security is not to protect against
- malicious code but to help with handling and boxing sensitive data.
- It covers two kinds of sensitive data: secret data which should not leak,
- and untrusted data coming from an external source and that must be
- validated before it is used.
-
- The idea is that, considering a large application that handles these
- kinds of sensitive data, there are typically only a small number of
- places that need to explicitly manipulate that sensitive data; all the
- other places merely pass it around, or do entirely unrelated things.
-
- Nevertheless, if a large application needs to be reviewed for security,
- it must be entirely carefully checked, because it is possible that a
- bug at some apparently unrelated place could lead to a leak of sensitive
- information in a way that an external attacker could exploit. For
- example, if any part of the application provides web services, an
- attacker might be able to issue unexpected requests with a regular web
- browser and deduce secret information from the details of the answers he
- gets. Another example is the common CGI attack where an attacker sends
- malformed inputs and causes the CGI script to do unintended things.
-
- An approach like that of the Taint Object Space allows the small parts
- of the program that manipulate sensitive data to be explicitly marked.
- The effect of this is that although these small parts still need a
- careful security review, the rest of the application no longer does,
- because even a bug would be unable to leak the information.
-
- We have implemented a simple two-level model: objects are either
- regular (untainted), or sensitive (tainted). Objects are marked as
- sensitive if they are secret or untrusted, and only declassified at
- carefully-checked positions (e.g. where the secret data is needed, or
- after the untrusted data has been fully validated).
-
- It would be simple to extend the code for more fine-grained scales of
- secrecy. For example it is typical in the literature to consider
- user-specified lattices of secrecy levels, corresponding to multiple
- "owners" that cannot access data belonging to another "owner" unless
- explicitly authorized to do so.
-
- Tainting and untainting
- -----------------------
-
- Start a py.py with the Taint Object Space and try the following example::
-
- $ py.py -o taint
- >>>> from __pypy__ import taint
- >>>> x = taint(6)
-
- # x is hidden from now on. We can pass it around and
- # even operate on it, but not inspect it. Taintness
- # is propagated to operation results.
-
- >>>> x
- TaintError
-
- >>>> if x > 5: y = 2 # see below
- TaintError
-
- >>>> y = x + 5 # ok
- >>>> lst = [x, y]
- >>>> z = lst.pop()
- >>>> t = type(z) # type() works too, tainted answer
- >>>> t
- TaintError
- >>>> u = t is int # even 'is' works
- >>>> u
- TaintError
-
- Notice that using a tainted boolean like ``x > 5`` in an ``if``
- statement is forbidden. This is because knowing which path is followed
- would give away a hint about ``x``; in the example above, if the
- statement ``if x > 5: y = 2`` was allowed to run, we would know
- something about the value of ``x`` by looking at the (untainted) value
- in the variable ``y``.
-
- Of course, there is a way to inspect tainted objects. The basic way is
- to explicitly "declassify" it with the ``untaint()`` function. In an
- application, the places that use ``untaint()`` are the places that need
- careful security review. To avoid unexpected objects showing up, the
- ``untaint()`` function must be called with the exact type of the object
- to declassify. It will raise ``TaintError`` if the type doesn't match::
-
- >>>> from __pypy__ import taint
- >>>> untaint(int, x)
- 6
- >>>> untaint(int, z)
- 11
- >>>> untaint(bool, x > 5)
- True
- >>>> untaint(int, x > 5)
- TaintError
-
-
- Taint Bombs
- -----------
-
- In this area, a common problem is what to do about failing operations.
- If an operation raises an exception when manipulating a tainted object,
- then the very presence of the exception can leak information about the
- tainted object itself. Consider::
-
- >>>> 5 / (x-6)
-
- By checking if this raises ``ZeroDivisionError`` or not, we would know
- if ``x`` was equal to 6 or not. The solution to this problem in the
- Taint Object Space is to introduce *Taint Bombs*. They are a kind of
- tainted object that doesn't contain a real object, but a pending
- exception. Taint Bombs are indistinguishable from normal tainted
- objects to unprivileged code. See::
-
- >>>> x = taint(6)
- >>>> i = 5 / (x-6) # no exception here
- >>>> j = i + 1 # nor here
- >>>> k = j + 5 # nor here
- >>>> untaint(int, k)
- TaintError
-
- In the above example, all of ``i``, ``j`` and ``k`` contain a Taint
- Bomb. Trying to untaint it raises an exception - a generic
- ``TaintError``. What we win is that the exception gives little away,
- and most importantly it occurs at the point where ``untaint()`` is
- called, not where the operation failed. This means that all calls to
- ``untaint()`` - but not the rest of the code - must be carefully
- reviewed for what occurs if they receive a Taint Bomb; they might catch
- the ``TaintError`` and give the user a generic message that something
- went wrong, if we are reasonably careful that the message or even its
- presence doesn't give information away. This might be a
- problem by itself, but there is no satisfying general solution here:
- it must be considered on a case-by-case basis. Again, what the
- Taint Object Space approach achieves is not solving these problems, but
- localizing them to well-defined small parts of the application - namely,
- around calls to ``untaint()``.
-
- The ``TaintError`` exception deliberately does not include any
- useful error messages, because they might give information away.
- Of course, this makes debugging quite a bit harder; a difficult
- problem to solve properly. So far we have implemented a way to peek in a Taint
- Box or Bomb, ``__pypy__._taint_look(x)``, and a "debug mode" that
- prints the exception as soon as a Bomb is created - both write
- information to the low-level stderr of the application, where we hope
- that it is unlikely to be seen by anyone but the application
- developer.
-
-
- Taint Atomic functions
- ----------------------
-
- Occasionally, a more complicated computation must be performed on a
- tainted object. This requires first untainting the object, performing the
- computations, and then carefully tainting the result again (including
- hiding all exceptions into Bombs).
-
- There is a built-in decorator that does this for you::
-
- >>>> @__pypy__.taint_atomic
- >>>> def myop(x, y):
- .... while x > 0:
- .... x -= y
- .... return x
- ....
- >>>> myop(42, 10)
- -8
- >>>> z = myop(taint(42), 10)
- >>>> z
- TaintError
- >>>> untaint(int, z)
- -8
-
- The decorator makes a whole function behave like a built-in operation.
- If no tainted argument is passed in, the function behaves normally. But
- if any of the arguments is tainted, it is automatically untainted - so
- the function body always sees untainted arguments - and the eventual
- result is tainted again (possibly in a Taint Bomb).
-
- It is important for the function marked as ``taint_atomic`` to have no
- visible side effects, as these could cause information leakage.
- This is currently not enforced, which means that all ``taint_atomic``
- functions have to be carefully reviewed for security (but not the
- callers of ``taint_atomic`` functions).
-
- A possible future extension would be to forbid side-effects on
- non-tainted objects from all ``taint_atomic`` functions.
-
- An example of usage: given a tainted object ``passwords_db`` that
- references a database of passwords, we can write a function
- that checks if a password is valid as follows::
-
- @taint_atomic
- def validate(passwords_db, username, password):
- assert type(passwords_db) is PasswordDatabase
- assert type(username) is str
- assert type(password) is str
- ...load username entry from passwords_db...
- return expected_password == password
-
- It returns a tainted boolean answer, or a Taint Bomb if something
- went wrong. A caller can do::
-
- ok = validate(passwords_db, 'john', '1234')
- ok = untaint(bool, ok)
-
- This can give three outcomes: ``True``, ``False``, or a ``TaintError``
- exception (with no information on it) if anything went wrong. If even
- this is considered giving too much information away, the ``False`` case
- can be made indistinguishable from the ``TaintError`` case (simply by
- raising an exception in ``validate()`` if the password is wrong).
-
- In the above example, the security results achieved are the following:
- as long as ``validate()`` does not leak information, no other part of
- the code can obtain more information about a passwords database than a
- Yes/No answer to a precise query.
-
- A possible extension of the ``taint_atomic`` decorator would be to check
- the argument types, as ``untaint()`` does, for the same reason: to
- prevent bugs where a function like ``validate()`` above is accidentally
- called with the wrong kind of tainted object, which would make it
- misbehave. For now, all ``taint_atomic`` functions should be
- conservative and carefully check all assumptions on their input
- arguments.
-
-
- .. _`taint-interface`:
-
- Interface
- ---------
-
- .. _`like a built-in operation`:
-
- The basic rule of the Tainted Object Space is that it introduces two new
- kinds of objects, Tainted Boxes and Tainted Bombs (which are not types
- in the Python sense). Each box internally contains a regular object;
- each bomb internally contains an exception object. An operation
- involving Tainted Boxes is performed on the objects contained in the
- boxes, and gives a Tainted Box or a Tainted Bomb as a result (such an
- operation does not let an exception be raised). An operation called
- with a Tainted Bomb argument immediately returns the same Tainted Bomb.
-
- In a PyPy running with (or translated with) the Taint Object Space,
- the ``__pypy__`` module exposes the following interface:
-
- * ``taint(obj)``
-
- Return a new Tainted Box wrapping ``obj``. Return ``obj`` itself
- if it is already tainted (a Box or a Bomb).
-
- * ``is_tainted(obj)``
-
- Check if ``obj`` is tainted (a Box or a Bomb).
-
- * ``untaint(type, obj)``
-
- Untaints ``obj`` if it is tainted. Raise ``TaintError`` if the type
- of the untainted object is not exactly ``type``, or if ``obj`` is a
- Bomb.
-
- * ``taint_atomic(func)``
-
- Return a wrapper function around the callable ``func``. The wrapper
- behaves `like a built-in operation`_ with respect to untainting the
- arguments, tainting the result, and returning a Bomb.
-
- * ``TaintError``
-
- Exception. On purpose, it provides no attribute or error message.
-
- * ``_taint_debug(level)``
-
- Set the debugging level to ``level`` (0=off). At level 1 or above,
- all Taint Bombs print a diagnostic message to stderr when they are
- created.
-
- * ``_taint_look(obj)``
-
- For debugging purposes: prints (to stderr) the type and address of
- the object in a Tainted Box, or prints the exception if ``obj`` is
- a Taint Bomb.
-
-
.. _dump:
The Dump Object Space
diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -391,8 +391,11 @@
def decrement_ticker(self, by):
value = self._ticker
if self.has_bytecode_counter: # this 'if' is constant-folded
- value -= by
- self._ticker = value
+ if jit.isconstant(by) and by == 0:
+ pass # normally constant-folded too
+ else:
+ value -= by
+ self._ticker = value
return value
diff --git a/pypy/interpreter/pyparser/pytokenizer.py b/pypy/interpreter/pyparser/pytokenizer.py
--- a/pypy/interpreter/pyparser/pytokenizer.py
+++ b/pypy/interpreter/pyparser/pytokenizer.py
@@ -226,7 +226,7 @@
parenlev = parenlev - 1
if parenlev < 0:
raise TokenError("unmatched '%s'" % initial, line,
- lnum-1, 0, token_list)
+ lnum, start + 1, token_list)
if token in python_opmap:
punct = python_opmap[token]
else:
diff --git a/pypy/interpreter/pyparser/test/test_pyparse.py b/pypy/interpreter/pyparser/test/test_pyparse.py
--- a/pypy/interpreter/pyparser/test/test_pyparse.py
+++ b/pypy/interpreter/pyparser/test/test_pyparse.py
@@ -87,6 +87,10 @@
assert exc.lineno == 1
assert exc.offset == 5
assert exc.lastlineno == 5
+ exc = py.test.raises(SyntaxError, parse, "abc)").value
+ assert exc.msg == "unmatched ')'"
+ assert exc.lineno == 1
+ assert exc.offset == 4
def test_is(self):
self.parse("x is y")
diff --git a/pypy/jit/backend/llgraph/llimpl.py b/pypy/jit/backend/llgraph/llimpl.py
--- a/pypy/jit/backend/llgraph/llimpl.py
+++ b/pypy/jit/backend/llgraph/llimpl.py
@@ -165,6 +165,7 @@
'unicodegetitem' : (('ref', 'int'), 'int'),
'unicodesetitem' : (('ref', 'int', 'int'), 'int'),
'cast_ptr_to_int' : (('ref',), 'int'),
+ 'cast_int_to_ptr' : (('int',), 'ref'),
'debug_merge_point': (('ref', 'int'), None),
'force_token' : ((), 'int'),
'call_may_force' : (('int', 'varargs'), 'intorptr'),
@@ -875,9 +876,6 @@
def op_new_array(self, arraydescr, count):
return do_new_array(arraydescr.ofs, count)
- def op_cast_ptr_to_int(self, descr, ptr):
- return cast_to_int(ptr)
-
def op_force_token(self, descr):
opaque_frame = _to_opaque(self)
return llmemory.cast_ptr_to_adr(opaque_frame)
diff --git a/pypy/jit/backend/llsupport/gc.py b/pypy/jit/backend/llsupport/gc.py
--- a/pypy/jit/backend/llsupport/gc.py
+++ b/pypy/jit/backend/llsupport/gc.py
@@ -45,6 +45,14 @@
def freeing_block(self, start, stop):
pass
+ def record_constptrs(self, op, gcrefs_output_list):
+ for i in range(op.numargs()):
+ v = op.getarg(i)
+ if isinstance(v, ConstPtr) and bool(v.value):
+ p = v.value
+ rgc._make_sure_does_not_move(p)
+ gcrefs_output_list.append(p)
+
# ____________________________________________________________
class GcLLDescr_boehm(GcLLDescription):
@@ -141,6 +149,14 @@
get_funcptr_for_newstr = None
get_funcptr_for_newunicode = None
+ def rewrite_assembler(self, cpu, operations, gcrefs_output_list):
+ # record all GCREFs too, because Boehm cannot see them and keep them
+ # alive if they end up as constants in the assembler
+ for op in operations:
+ self.record_constptrs(op, gcrefs_output_list)
+ return GcLLDescription.rewrite_assembler(self, cpu, operations,
+ gcrefs_output_list)
+
# ____________________________________________________________
# All code below is for the hybrid or minimark GC
@@ -757,14 +773,6 @@
funcptr(llmemory.cast_ptr_to_adr(gcref_struct),
llmemory.cast_ptr_to_adr(gcref_newptr))
- def record_constptrs(self, op, gcrefs_output_list):
- for i in range(op.numargs()):
- v = op.getarg(i)
- if isinstance(v, ConstPtr) and bool(v.value):
- p = v.value
- rgc._make_sure_does_not_move(p)
- gcrefs_output_list.append(p)
-
def rewrite_assembler(self, cpu, operations, gcrefs_output_list):
# Perform two kinds of rewrites in parallel:
#
diff --git a/pypy/jit/backend/test/runner_test.py b/pypy/jit/backend/test/runner_test.py
--- a/pypy/jit/backend/test/runner_test.py
+++ b/pypy/jit/backend/test/runner_test.py
@@ -1468,20 +1468,16 @@
return u''.join(u.chars)
- def test_casts(self):
- py.test.skip("xxx fix or kill")
- from pypy.rpython.lltypesystem import lltype, llmemory
- TP = lltype.GcStruct('x')
- x = lltype.malloc(TP)
- x = lltype.cast_opaque_ptr(llmemory.GCREF, x)
+ def test_cast_int_to_ptr(self):
+ res = self.execute_operation(rop.CAST_INT_TO_PTR,
+ [BoxInt(-17)], 'ref').value
+ assert lltype.cast_ptr_to_int(res) == -17
+
+ def test_cast_ptr_to_int(self):
+ x = lltype.cast_int_to_ptr(llmemory.GCREF, -19)
res = self.execute_operation(rop.CAST_PTR_TO_INT,
- [BoxPtr(x)], 'int').value
- expected = self.cpu.cast_adr_to_int(llmemory.cast_ptr_to_adr(x))
- assert rffi.get_real_int(res) == rffi.get_real_int(expected)
- res = self.execute_operation(rop.CAST_PTR_TO_INT,
- [ConstPtr(x)], 'int').value
- expected = self.cpu.cast_adr_to_int(llmemory.cast_ptr_to_adr(x))
- assert rffi.get_real_int(res) == rffi.get_real_int(expected)
+ [BoxPtr(x)], 'int').value
+ assert res == -19
def test_ooops_non_gc(self):
x = lltype.malloc(lltype.Struct('x'), flavor='raw')
@@ -2299,13 +2295,6 @@
#
cpu.bh_strsetitem(x, 4, ord('/'))
assert str.chars[4] == '/'
- #
-## x = cpu.bh_newstr(5)
-## y = cpu.bh_cast_ptr_to_int(x)
-## z = cpu.bh_cast_ptr_to_int(x)
-## y = rffi.get_real_int(y)
-## z = rffi.get_real_int(z)
-## assert type(y) == type(z) == int and y == z
def test_sorting_of_fields(self):
S = self.S
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
@@ -1387,7 +1387,8 @@
def genop_same_as(self, op, arglocs, resloc):
self.mov(arglocs[0], resloc)
- #genop_cast_ptr_to_int = genop_same_as
+ genop_cast_ptr_to_int = genop_same_as
+ genop_cast_int_to_ptr = genop_same_as
def genop_int_mod(self, op, arglocs, resloc):
if IS_X86_32:
diff --git a/pypy/jit/backend/x86/regalloc.py b/pypy/jit/backend/x86/regalloc.py
--- a/pypy/jit/backend/x86/regalloc.py
+++ b/pypy/jit/backend/x86/regalloc.py
@@ -1152,7 +1152,8 @@
self.possibly_free_var(op.getarg(0))
resloc = self.force_allocate_reg(op.result)
self.Perform(op, [argloc], resloc)
- #consider_cast_ptr_to_int = consider_same_as
+ consider_cast_ptr_to_int = consider_same_as
+ consider_cast_int_to_ptr = consider_same_as
def consider_strlen(self, op):
args = op.getarglist()
diff --git a/pypy/jit/backend/x86/tool/viewcode.py b/pypy/jit/backend/x86/tool/viewcode.py
--- a/pypy/jit/backend/x86/tool/viewcode.py
+++ b/pypy/jit/backend/x86/tool/viewcode.py
@@ -9,7 +9,12 @@
"""
import autopath
-import operator, sys, os, re, py, new
+import new
+import operator
+import py
+import re
+import sys
+import subprocess
from bisect import bisect_left
# don't use pypy.tool.udir here to avoid removing old usessions which
@@ -44,14 +49,16 @@
f = open(tmpfile, 'wb')
f.write(data)
f.close()
- g = os.popen(objdump % {
+ p = subprocess.Popen(objdump % {
'file': tmpfile,
'origin': originaddr,
'backend': objdump_backend_option[backend_name],
- }, 'r')
- result = g.readlines()
- g.close()
- lines = result[6:] # drop some objdump cruft
+ }, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ assert not p.returncode, ('Encountered an error running objdump: %s' %
+ stderr)
+ # drop some objdump cruft
+ lines = stdout.splitlines()[6:]
return format_code_dump_with_labels(originaddr, lines, label_list)
def format_code_dump_with_labels(originaddr, lines, label_list):
@@ -85,8 +92,12 @@
#
print 'loading symbols from %s...' % (filename,)
symbols = {}
- g = os.popen(symbollister % filename, "r")
- for line in g:
+ p = subprocess.Popen(symbollister % filename, shell=True,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ assert not p.returncode, ('Encountered an error running nm: %s' %
+ stderr)
+ for line in stdout.splitlines():
match = re_symbolentry.match(line)
if match:
addr = long(match.group(1), 16)
@@ -94,7 +105,6 @@
if name.startswith('pypy_g_'):
name = '\xb7' + name[7:]
symbols[addr] = name
- g.close()
print '%d symbols found' % (len(symbols),)
return symbols
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
@@ -455,6 +455,23 @@
# the special return value None forces op.result to be considered
# equal to op.args[0]
return [op0, op1, None]
+ if (hints.get('promote_string') and
+ op.args[0].concretetype is not lltype.Void):
+ S = lltype.Ptr(rstr.STR)
+ assert op.args[0].concretetype == S
+ self._register_extra_helper(EffectInfo.OS_STREQ_NONNULL,
+ "str.eq_nonnull",
+ [S, S],
+ lltype.Signed,
+ EffectInfo.EF_ELIDABLE_CANNOT_RAISE)
+ descr, p = self.callcontrol.callinfocollection.callinfo_for_oopspec(
+ EffectInfo.OS_STREQ_NONNULL)
+ # XXX this is fairly ugly way of creating a constant,
+ # however, callinfocollection has no better interface
+ c = Constant(p.adr.ptr, lltype.typeOf(p.adr.ptr))
+ op1 = SpaceOperation('str_guard_value', [op.args[0], c, descr],
+ op.result)
+ return [SpaceOperation('-live-', [], None), op1, None]
else:
log.WARNING('ignoring hint %r at %r' % (hints, self.graph))
@@ -783,8 +800,7 @@
def rewrite_op_cast_ptr_to_int(self, op):
if self._is_gc(op.args[0]):
- #return op
- raise NotImplementedError("cast_ptr_to_int")
+ return op
def rewrite_op_force_cast(self, op):
v_arg = op.args[0]
@@ -1526,6 +1542,10 @@
def rewrite_op_jit_force_virtual(self, op):
return self._do_builtin_call(op)
+ def rewrite_op_jit_is_virtual(self, op):
+ raise Exception, (
+ "'vref.virtual' should not be used from jit-visible code")
+
def rewrite_op_jit_force_virtualizable(self, op):
# this one is for virtualizables
vinfo = self.get_vinfo(op.args[0])
diff --git a/pypy/jit/codewriter/test/test_jtransform.py b/pypy/jit/codewriter/test/test_jtransform.py
--- a/pypy/jit/codewriter/test/test_jtransform.py
+++ b/pypy/jit/codewriter/test/test_jtransform.py
@@ -99,6 +99,12 @@
if i == oopspecindex:
return True
return False
+ def callinfo_for_oopspec(self, oopspecindex):
+ assert oopspecindex == effectinfo.EffectInfo.OS_STREQ_NONNULL
+ class c:
+ class adr:
+ ptr = 1
+ return ('calldescr', c)
class FakeBuiltinCallControl:
def __init__(self):
@@ -119,6 +125,7 @@
EI.OS_STR2UNICODE:([PSTR], PUNICODE),
EI.OS_STR_CONCAT: ([PSTR, PSTR], PSTR),
EI.OS_STR_SLICE: ([PSTR, INT, INT], PSTR),
+ EI.OS_STREQ_NONNULL: ([PSTR, PSTR], INT),
EI.OS_UNI_CONCAT: ([PUNICODE, PUNICODE], PUNICODE),
EI.OS_UNI_SLICE: ([PUNICODE, INT, INT], PUNICODE),
EI.OS_UNI_EQUAL: ([PUNICODE, PUNICODE], lltype.Bool),
@@ -844,6 +851,21 @@
assert op1.args[2] == ListOfKind('ref', [v1, v2])
assert op1.result == v3
+def test_str_promote():
+ PSTR = lltype.Ptr(rstr.STR)
+ v1 = varoftype(PSTR)
+ v2 = varoftype(PSTR)
+ op = SpaceOperation('hint',
+ [v1, Constant({'promote_string': True}, lltype.Void)],
+ v2)
+ tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
+ op0, op1, _ = tr.rewrite_operation(op)
+ assert op1.opname == 'str_guard_value'
+ assert op1.args[0] == v1
+ assert op1.args[2] == 'calldescr'
+ assert op1.result == v2
+ assert op0.opname == '-live-'
+
def test_unicode_concat():
# test that the oopspec is present and correctly transformed
PSTR = lltype.Ptr(rstr.UNICODE)
diff --git a/pypy/jit/metainterp/blackhole.py b/pypy/jit/metainterp/blackhole.py
--- a/pypy/jit/metainterp/blackhole.py
+++ b/pypy/jit/metainterp/blackhole.py
@@ -2,7 +2,7 @@
from pypy.rlib.rtimer import read_timestamp
from pypy.rlib.rarithmetic import intmask, LONG_BIT, r_uint, ovfcheck
from pypy.rlib.objectmodel import we_are_translated
-from pypy.rlib.debug import debug_start, debug_stop
+from pypy.rlib.debug import debug_start, debug_stop, ll_assert
from pypy.rlib.debug import make_sure_not_resized
from pypy.rpython.lltypesystem import lltype, llmemory, rclass
from pypy.rpython.lltypesystem.lloperation import llop
@@ -503,6 +503,15 @@
@arguments("r", returns="r")
def bhimpl_cast_opaque_ptr(a):
return a
+ @arguments("r", returns="i")
+ def bhimpl_cast_ptr_to_int(a):
+ i = lltype.cast_ptr_to_int(a)
+ ll_assert((i & 1) == 1, "bhimpl_cast_ptr_to_int: not an odd int")
+ return i
+ @arguments("i", returns="r")
+ def bhimpl_cast_int_to_ptr(i):
+ ll_assert((i & 1) == 1, "bhimpl_cast_int_to_ptr: not an odd int")
+ return lltype.cast_int_to_ptr(llmemory.GCREF, i)
@arguments("i", returns="i")
def bhimpl_int_copy(a):
@@ -523,6 +532,9 @@
@arguments("f")
def bhimpl_float_guard_value(a):
pass
+ @arguments("r", "i", "d", returns="r")
+ def bhimpl_str_guard_value(a, i, d):
+ return a
@arguments("self", "i")
def bhimpl_int_push(self, a):
diff --git a/pypy/jit/metainterp/graphpage.py b/pypy/jit/metainterp/graphpage.py
--- a/pypy/jit/metainterp/graphpage.py
+++ b/pypy/jit/metainterp/graphpage.py
@@ -12,8 +12,8 @@
def get_display_text(self):
return None
-def display_loops(loops, errmsg=None, highlight_loops=()):
- graphs = [(loop, loop in highlight_loops) for loop in loops]
+def display_loops(loops, errmsg=None, highlight_loops={}):
+ graphs = [(loop, highlight_loops.get(loop, 0)) for loop in loops]
for graph, highlight in graphs:
for op in graph.get_operations():
if is_interesting_guard(op):
@@ -65,8 +65,7 @@
def add_graph(self, graph, highlight=False):
graphindex = len(self.graphs)
self.graphs.append(graph)
- if highlight:
- self.highlight_graphs[graph] = True
+ self.highlight_graphs[graph] = highlight
for i, op in enumerate(graph.get_operations()):
self.all_operations[op] = graphindex, i
@@ -126,10 +125,13 @@
self.dotgen.emit('subgraph cluster%d {' % graphindex)
label = graph.get_display_text()
if label is not None:
- if self.highlight_graphs.get(graph):
- fillcolor = '#f084c2'
+ colorindex = self.highlight_graphs.get(graph, 0)
+ if colorindex == 1:
+ fillcolor = '#f084c2' # highlighted graph
+ elif colorindex == 2:
+ fillcolor = '#808080' # invalidated graph
else:
- fillcolor = '#84f0c2'
+ fillcolor = '#84f0c2' # normal color
self.dotgen.emit_node(graphname, shape="octagon",
label=label, fillcolor=fillcolor)
self.pendingedges.append((graphname,
diff --git a/pypy/jit/metainterp/history.py b/pypy/jit/metainterp/history.py
--- a/pypy/jit/metainterp/history.py
+++ b/pypy/jit/metainterp/history.py
@@ -9,6 +9,7 @@
from pypy.jit.metainterp.resoperation import ResOperation, rop
from pypy.jit.codewriter import heaptracker, longlong
+from pypy.rlib.objectmodel import compute_identity_hash
# ____________________________________________________________
@@ -104,7 +105,7 @@
getref._annspecialcase_ = 'specialize:arg(1)'
def _get_hash_(self):
- raise NotImplementedError
+ return compute_identity_hash(self)
def clonebox(self):
raise NotImplementedError
@@ -133,6 +134,9 @@
def _get_str(self):
raise NotImplementedError
+ def same_box(self, other):
+ return self is other
+
class AbstractDescr(AbstractValue):
__slots__ = ()
@@ -241,32 +245,15 @@
def constbox(self):
return self
+ def same_box(self, other):
+ return self.same_constant(other)
+
def same_constant(self, other):
raise NotImplementedError
def __repr__(self):
return 'Const(%s)' % self._getrepr_()
- def __eq__(self, other):
- "NOT_RPYTHON"
- # Remember that you should not compare Consts with '==' in RPython.
- # Consts have no special __hash__, in order to force different Consts
- # from being considered as different keys when stored in dicts
- # (as they always are after translation). Use a dict_equal_consts()
- # to get the other behavior (i.e. using this __eq__).
- if self.__class__ is not other.__class__:
- return False
- try:
- return self.value == other.value
- except TypeError:
- if (isinstance(self.value, Symbolic) and
- isinstance(other.value, Symbolic)):
- return self.value is other.value
- raise
-
- def __ne__(self, other):
- return not (self == other)
-
class ConstInt(Const):
type = INT
@@ -688,33 +675,6 @@
# ____________________________________________________________
-def dict_equal_consts():
- "NOT_RPYTHON"
- # Returns a dict in which Consts that compare as equal
- # are identified when used as keys.
- return r_dict(dc_eq, dc_hash)
-
-def dc_eq(c1, c2):
- return c1 == c2
-
-def dc_hash(c):
- "NOT_RPYTHON"
- # This is called during translation only. Avoid using identityhash(),
- # to avoid forcing a hash, at least on lltype objects.
- if not isinstance(c, Const):
- return hash(c)
- if isinstance(c.value, Symbolic):
- return id(c.value)
- try:
- if isinstance(c, ConstPtr):
- p = lltype.normalizeptr(c.value)
- if p is not None:
- return hash(p._obj)
- else:
- return 0
- return c._get_hash_()
- except lltype.DelayedPointer:
- return -2 # xxx risk of changing hash...
def make_hashable_int(i):
from pypy.rpython.lltypesystem.ll2ctypes import NotCtypesAllocatedStructure
@@ -772,6 +732,7 @@
failed_states = None
retraced_count = 0
terminating = False # see TerminatingLoopToken in compile.py
+ invalidated = False
outermost_jitdriver_sd = None
# and more data specified by the backend when the loop is compiled
number = -1
@@ -974,6 +935,7 @@
self.loops = []
self.locations = []
self.aborted_keys = []
+ self.invalidated_token_numbers = set()
def set_history(self, history):
self.operations = history.operations
@@ -1052,7 +1014,12 @@
if loop in loops:
loops.remove(loop)
loops.append(loop)
- display_loops(loops, errmsg, extraloops)
+ highlight_loops = dict.fromkeys(extraloops, 1)
+ for loop in loops:
+ if hasattr(loop, '_looptoken_number') and (
+ loop._looptoken_number in self.invalidated_token_numbers):
+ highlight_loops.setdefault(loop, 2)
+ display_loops(loops, errmsg, highlight_loops)
# ----------------------------------------------------------------
diff --git a/pypy/jit/metainterp/memmgr.py b/pypy/jit/metainterp/memmgr.py
--- a/pypy/jit/metainterp/memmgr.py
+++ b/pypy/jit/metainterp/memmgr.py
@@ -68,7 +68,8 @@
debug_print("Loop tokens before:", oldtotal)
max_generation = self.current_generation - (self.max_age-1)
for looptoken in self.alive_loops.keys():
- if 0 <= looptoken.generation < max_generation:
+ if (0 <= looptoken.generation < max_generation or
+ looptoken.invalidated):
del self.alive_loops[looptoken]
newtotal = len(self.alive_loops)
debug_print("Loop tokens freed: ", oldtotal - newtotal)
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
@@ -2329,7 +2329,7 @@
def _variables_equal(box, varname, strict):
if varname not in virtuals:
if strict:
- assert box == oparse.getvar(varname)
+ assert box.same_box(oparse.getvar(varname))
else:
assert box.value == oparse.getvar(varname).value
else:
diff --git a/pypy/jit/metainterp/optimizeopt/util.py b/pypy/jit/metainterp/optimizeopt/util.py
--- a/pypy/jit/metainterp/optimizeopt/util.py
+++ b/pypy/jit/metainterp/optimizeopt/util.py
@@ -90,14 +90,11 @@
for i in range(len(args1)):
arg1 = args1[i]
arg2 = args2[i]
- if isinstance(arg1, history.Const):
- if arg1.__class__ is not arg2.__class__:
+ if arg1 is None:
+ if arg2 is not None:
return False
- if not arg1.same_constant(arg2):
- return False
- else:
- if not arg1 is arg2:
- return False
+ elif not arg1.same_box(arg2):
+ return False
return True
def args_hash(args):
@@ -106,10 +103,8 @@
for arg in args:
if arg is None:
y = 17
- elif isinstance(arg, history.Const):
+ else:
y = arg._get_hash_()
- else:
- y = compute_identity_hash(arg)
res = intmask((1000003 * res) ^ y)
return res
@@ -145,9 +140,12 @@
for i in range(op1.numargs()):
x = op1.getarg(i)
y = op2.getarg(i)
- assert x == remap.get(y, y)
+ assert x.same_box(remap.get(y, y))
if op2.result in remap:
- assert op1.result == remap[op2.result]
+ if op2.result is None:
+ assert op1.result == remap[op2.result]
+ else:
+ assert op1.result.same_box(remap[op2.result])
else:
remap[op2.result] = op1.result
if op1.getopnum() != rop.JUMP: # xxx obscure
@@ -156,11 +154,20 @@
assert len(op1.getfailargs()) == len(op2.getfailargs())
if strict_fail_args:
for x, y in zip(op1.getfailargs(), op2.getfailargs()):
- assert x == remap.get(y, y)
+ if x is None:
+ assert remap.get(y, y) is None
+ else:
+ assert x.same_box(remap.get(y, y))
else:
fail_args1 = set(op1.getfailargs())
fail_args2 = set([remap.get(y, y) for y in op2.getfailargs()])
- assert fail_args1 == fail_args2
+ for x in fail_args1:
+ for y in fail_args2:
+ if x.same_box(y):
+ fail_args2.remove(y)
+ break
+ else:
+ assert False
assert len(oplist1) == len(oplist2)
print '-'*totwidth
return True
diff --git a/pypy/jit/metainterp/optimizeopt/vstring.py b/pypy/jit/metainterp/optimizeopt/vstring.py
--- a/pypy/jit/metainterp/optimizeopt/vstring.py
+++ b/pypy/jit/metainterp/optimizeopt/vstring.py
@@ -587,10 +587,7 @@
return True
#
if v1.is_nonnull() and v2.is_nonnull():
- if l1box is not None and l2box is not None and (
- l1box == l2box or (isinstance(l1box, ConstInt) and
- isinstance(l2box, ConstInt) and
- l1box.value == l2box.value)):
+ if l1box is not None and l2box is not None and l1box.same_box(l2box):
do = EffectInfo.OS_STREQ_LENGTHOK
else:
do = EffectInfo.OS_STREQ_NONNULL
diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -162,15 +162,18 @@
if registers[i] is oldbox:
registers[i] = newbox
if not we_are_translated():
- assert oldbox not in registers[count:]
+ for b in registers[count:]:
+ assert not oldbox.same_box(b)
+
def make_result_of_lastop(self, resultbox):
got_type = resultbox.type
- if not we_are_translated():
- typeof = {'i': history.INT,
- 'r': history.REF,
- 'f': history.FLOAT}
- assert typeof[self.jitcode._resulttypes[self.pc]] == got_type
+ # XXX disabled for now, conflicts with str_guard_value
+ #if not we_are_translated():
+ # typeof = {'i': history.INT,
+ # 'r': history.REF,
+ # 'f': history.FLOAT}
+ # assert typeof[self.jitcode._resulttypes[self.pc]] == got_type
target_index = ord(self.bytecode[self.pc-1])
if got_type == history.INT:
self.registers_i[target_index] = resultbox
@@ -219,6 +222,7 @@
'cast_float_to_int', 'cast_int_to_float',
'cast_float_to_singlefloat', 'cast_singlefloat_to_float',
'float_neg', 'float_abs',
+ 'cast_ptr_to_int', 'cast_int_to_ptr',
]:
exec py.code.Source('''
@arguments("box")
@@ -895,6 +899,21 @@
def _opimpl_guard_value(self, orgpc, box):
self.implement_guard_value(orgpc, box)
+ @arguments("orgpc", "box", "box", "descr")
+ def opimpl_str_guard_value(self, orgpc, box, funcbox, descr):
+ if isinstance(box, Const):
+ return box # no promotion needed, already a Const
+ else:
+ constbox = box.constbox()
+ resbox = self.do_residual_call(funcbox, descr, [box, constbox])
+ promoted_box = resbox.constbox()
+ # This is GUARD_VALUE because GUARD_TRUE assumes the existance
+ # of a label when computing resumepc
+ self.generate_guard(rop.GUARD_VALUE, resbox, [promoted_box],
+ resumepc=orgpc)
+ self.metainterp.replace_box(box, constbox)
+ return constbox
+
opimpl_int_guard_value = _opimpl_guard_value
opimpl_ref_guard_value = _opimpl_guard_value
opimpl_float_guard_value = _opimpl_guard_value
diff --git a/pypy/jit/metainterp/quasiimmut.py b/pypy/jit/metainterp/quasiimmut.py
--- a/pypy/jit/metainterp/quasiimmut.py
+++ b/pypy/jit/metainterp/quasiimmut.py
@@ -2,6 +2,7 @@
from pypy.rpython.lltypesystem import lltype, rclass
from pypy.rpython.annlowlevel import cast_base_ptr_to_instance
from pypy.jit.metainterp.history import AbstractDescr
+from pypy.rlib.objectmodel import we_are_translated
def get_mutate_field_name(fieldname):
@@ -50,13 +51,13 @@
class QuasiImmut(object):
llopaque = True
+ compress_limit = 30
def __init__(self, cpu):
self.cpu = cpu
# list of weakrefs to the LoopTokens that must be invalidated if
# this value ever changes
self.looptokens_wrefs = []
- self.compress_limit = 30
def hide(self):
qmut_ptr = self.cpu.ts.cast_instance_to_base_ref(self)
@@ -73,8 +74,12 @@
self.looptokens_wrefs.append(wref_looptoken)
def compress_looptokens_list(self):
- self.looptokens_wrefs = [wref for wref in self.looptokens_wrefs
- if wref() is not None]
+ newlist = []
+ for wref in self.looptokens_wrefs:
+ looptoken = wref()
+ if looptoken is not None and not looptoken.invalidated:
+ newlist.append(wref)
+ self.looptokens_wrefs = newlist
self.compress_limit = (len(self.looptokens_wrefs) + 15) * 2
def invalidate(self):
@@ -85,8 +90,12 @@
self.looptokens_wrefs = []
for wref in wrefs:
looptoken = wref()
- if looptoken is not None:
+ if looptoken is not None and not looptoken.invalidated:
+ looptoken.invalidated = True
self.cpu.invalidate_loop(looptoken)
+ if not we_are_translated():
+ self.cpu.stats.invalidated_token_numbers.add(
+ looptoken.number)
class QuasiImmutDescr(AbstractDescr):
diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py
--- a/pypy/jit/metainterp/resoperation.py
+++ b/pypy/jit/metainterp/resoperation.py
@@ -433,6 +433,8 @@
'INT_INVERT/1',
#
'SAME_AS/1', # gets a Const or a Box, turns it into another Box
+ 'CAST_PTR_TO_INT/1',
+ 'CAST_INT_TO_PTR/1',
#
'PTR_EQ/2b',
'PTR_NE/2b',
diff --git a/pypy/jit/metainterp/test/test_ajit.py b/pypy/jit/metainterp/test/test_ajit.py
--- a/pypy/jit/metainterp/test/test_ajit.py
+++ b/pypy/jit/metainterp/test/test_ajit.py
@@ -13,7 +13,7 @@
from pypy.rlib.jit import (JitDriver, we_are_jitted, hint, dont_look_inside,
loop_invariant, elidable, promote, jit_debug, assert_green,
AssertGreenFailed, unroll_safe, current_trace_length, look_inside_iff,
- isconstant, isvirtual)
+ isconstant, isvirtual, promote_string)
from pypy.rlib.rarithmetic import ovfcheck
from pypy.rpython.lltypesystem import lltype, llmemory, rffi
from pypy.rpython.ootypesystem import ootype
@@ -3440,7 +3440,6 @@
class TestLLtype(BaseLLtypeTests, LLJitMixin):
def test_tagged(self):
- py.test.skip("implement me")
from pypy.rlib.objectmodel import UnboxedValue
class Base(object):
__slots__ = ()
@@ -3492,3 +3491,35 @@
pc += 1
return pc
res = self.meta_interp(main, [False, 100, True], taggedpointers=True)
+
+ def test_rerased(self):
+ from pypy.rlib.rerased import erase_int, unerase_int, new_erasing_pair
+ eraseX, uneraseX = new_erasing_pair("X")
+ #
+ class X:
+ def __init__(self, a, b):
+ self.a = a
+ self.b = b
+ #
+ def f(i, j):
+ # 'j' should be 0 or 1, not other values
+ if j > 0:
+ e = eraseX(X(i, j))
+ else:
+ try:
+ e = erase_int(i)
+ except OverflowError:
+ return -42
+ if j & 1:
+ x = uneraseX(e)
+ return x.a - x.b
+ else:
+ return unerase_int(e)
+ #
+ x = self.interp_operations(f, [-128, 0], taggedpointers=True)
+ assert x == -128
+ bigint = sys.maxint//2 + 1
+ x = self.interp_operations(f, [bigint, 0], taggedpointers=True)
+ assert x == -42
+ x = self.interp_operations(f, [1000, 1], taggedpointers=True)
+ assert x == 999
diff --git a/pypy/jit/metainterp/test/test_memmgr.py b/pypy/jit/metainterp/test/test_memmgr.py
--- a/pypy/jit/metainterp/test/test_memmgr.py
+++ b/pypy/jit/metainterp/test/test_memmgr.py
@@ -18,6 +18,7 @@
class FakeLoopToken:
generation = 0
+ invalidated = False
class _TestMemoryManager:
diff --git a/pypy/jit/metainterp/test/test_quasiimmut.py b/pypy/jit/metainterp/test/test_quasiimmut.py
--- a/pypy/jit/metainterp/test/test_quasiimmut.py
+++ b/pypy/jit/metainterp/test/test_quasiimmut.py
@@ -48,6 +48,13 @@
class QuasiImmutTests(object):
+ def setup_method(self, meth):
+ self.prev_compress_limit = QuasiImmut.compress_limit
+ QuasiImmut.compress_limit = 1
+
+ def teardown_method(self, meth):
+ QuasiImmut.compress_limit = self.prev_compress_limit
+
def test_simple_1(self):
myjitdriver = JitDriver(greens=['foo'], reds=['x', 'total'])
class Foo:
@@ -289,7 +296,7 @@
return total
res = self.meta_interp(main, [])
- self.check_loop_count(9)
+ self.check_tree_loop_count(6)
assert res == main()
def test_change_during_running(self):
@@ -317,7 +324,7 @@
assert f(100, 15) == 3009
res = self.meta_interp(f, [100, 15])
assert res == 3009
- self.check_loops(guard_not_invalidated=2, getfield_gc=0,
+ self.check_loops(guard_not_invalidated=4, getfield_gc=0,
call_may_force=0, guard_not_forced=0)
def test_list_simple_1(self):
@@ -453,10 +460,30 @@
assert f(100, 15) == 3009
res = self.meta_interp(f, [100, 15])
assert res == 3009
- self.check_loops(guard_not_invalidated=2, getfield_gc=0,
+ self.check_loops(guard_not_invalidated=4, getfield_gc=0,
getarrayitem_gc=0, getarrayitem_gc_pure=0,
call_may_force=0, guard_not_forced=0)
+ def test_invalidated_loop_is_not_used_any_more_as_target(self):
+ myjitdriver = JitDriver(greens=['foo'], reds=['x'])
+ class Foo:
+ _immutable_fields_ = ['step?']
+ @dont_look_inside
+ def residual(x, foo):
+ if x == 20:
+ foo.step = 1
+ def f(x):
+ foo = Foo()
+ foo.step = 2
+ while x > 0:
+ myjitdriver.jit_merge_point(foo=foo, x=x)
+ residual(x, foo)
+ x -= foo.step
+ return foo.step
+ res = self.meta_interp(f, [60])
+ assert res == 1
+ self.check_tree_loop_count(4) # at least not 2 like before
+
class TestLLtypeGreenFieldsTests(QuasiImmutTests, LLJitMixin):
pass
diff --git a/pypy/jit/metainterp/test/test_resume.py b/pypy/jit/metainterp/test/test_resume.py
--- a/pypy/jit/metainterp/test/test_resume.py
+++ b/pypy/jit/metainterp/test/test_resume.py
@@ -1,3 +1,4 @@
+from __future__ import with_statement
import py
from pypy.rpython.lltypesystem import lltype, llmemory, rffi
from pypy.jit.metainterp.optimizeopt.optimizer import OptValue
@@ -180,22 +181,27 @@
reader.consume_boxes(info, bi, br, bf)
b1s = reader.liveboxes[0]
b2s = reader.liveboxes[1]
- assert bi == [b1s, ConstInt(111), b1s]
- assert br == [ConstPtr(gcrefnull), b2s]
+ assert_same(bi, [b1s, ConstInt(111), b1s])
+ assert_same(br, [ConstPtr(gcrefnull), b2s])
bi, br, bf = [None]*2, [None]*0, [None]*0
info = MyBlackholeInterp([lltype.Signed,
lltype.Signed]).get_current_position_info()
reader.consume_boxes(info, bi, br, bf)
- assert bi == [ConstInt(222), ConstInt(333)]
+ assert_same(bi, [ConstInt(222), ConstInt(333)])
bi, br, bf = [None]*2, [None]*1, [None]*0
info = MyBlackholeInterp([lltype.Signed, llmemory.GCREF,
lltype.Signed]).get_current_position_info()
reader.consume_boxes(info, bi, br, bf)
b3s = reader.liveboxes[2]
- assert bi == [b1s, b3s]
- assert br == [b2s]
+ assert_same(bi, [b1s, b3s])
+ assert_same(br, [b2s])
#
+def assert_same(list1, list2):
+ assert len(list1) == len(list2)
+ for b1, b2 in zip(list1, list2):
+ assert b1.same_box(b2)
+
def test_simple_read_tagged_ints():
storage = Storage()
storage.rd_consts = []
@@ -1023,11 +1029,11 @@
metainterp = MyMetaInterp()
reader = ResumeDataFakeReader(storage, newboxes, metainterp)
lst = reader.consume_boxes()
- assert lst == [b1t, b1t, b3t]
+ assert_same(lst, [b1t, b1t, b3t])
lst = reader.consume_boxes()
- assert lst == [ConstInt(2), ConstInt(3)]
+ assert_same(lst, [ConstInt(2), ConstInt(3)])
lst = reader.consume_boxes()
- assert lst == [b1t, ConstInt(1), b1t, b1t]
+ assert_same(lst, [b1t, ConstInt(1), b1t, b1t])
assert metainterp.trace == []
@@ -1044,11 +1050,11 @@
reader = ResumeDataFakeReader(storage, newboxes, metainterp)
lst = reader.consume_boxes()
c1t = ConstInt(111)
- assert lst == [c1t, b2t, b3t]
+ assert_same(lst, [c1t, b2t, b3t])
lst = reader.consume_boxes()
- assert lst == [ConstInt(2), ConstInt(3)]
+ assert_same(lst, [ConstInt(2), ConstInt(3)])
lst = reader.consume_boxes()
- assert lst == [c1t, ConstInt(1), c1t, b2t]
+ assert_same(lst, [c1t, ConstInt(1), c1t, b2t])
assert metainterp.trace == []
@@ -1114,9 +1120,11 @@
# check that we get the operations in 'expected', in a possibly different
# order.
assert len(trace) == len(expected)
- for x in trace:
- assert x in expected
- expected.remove(x)
+ with CompareableConsts():
+ for x in trace:
+ assert x in expected
+ expected.remove(x)
+
ptr = b2t.value._obj.container._as_ptr()
assert lltype.typeOf(ptr) == lltype.Ptr(LLtypeMixin.NODE)
assert ptr.value == 111
@@ -1126,6 +1134,18 @@
assert ptr2.parent.value == 33
assert ptr2.parent.next == ptr
+class CompareableConsts(object):
+ def __init__(self):
+ self.oldeq = None
+
+ def __enter__(self):
+ assert self.oldeq is None
+ self.oldeq = Const.__eq__
+ Const.__eq__ = Const.same_box
+
+ def __exit__(self, type, value, traceback):
+ Const.__eq__ = self.oldeq
+
def test_virtual_adder_make_varray():
b2s, b4s = [BoxPtr(), BoxInt(4)]
c1s = ConstInt(111)
@@ -1163,8 +1183,9 @@
(rop.SETARRAYITEM_GC, [b2t,ConstInt(1), c1s], None,
LLtypeMixin.arraydescr),
]
- for x, y in zip(expected, trace):
- assert x == y
+ with CompareableConsts():
+ for x, y in zip(expected, trace):
+ assert x == y
#
ptr = b2t.value._obj.container._as_ptr()
assert lltype.typeOf(ptr) == lltype.Ptr(lltype.GcArray(lltype.Signed))
@@ -1207,8 +1228,9 @@
(rop.SETFIELD_GC, [b2t, c1s], None, LLtypeMixin.adescr),
(rop.SETFIELD_GC, [b2t, b4t], None, LLtypeMixin.bdescr),
]
- for x, y in zip(expected, trace):
- assert x == y
+ with CompareableConsts():
+ for x, y in zip(expected, trace):
+ assert x == y
#
ptr = b2t.value._obj.container._as_ptr()
assert lltype.typeOf(ptr) == lltype.Ptr(LLtypeMixin.S)
diff --git a/pypy/jit/metainterp/test/test_string.py b/pypy/jit/metainterp/test/test_string.py
--- a/pypy/jit/metainterp/test/test_string.py
+++ b/pypy/jit/metainterp/test/test_string.py
@@ -3,7 +3,8 @@
from pypy.jit.codewriter.policy import StopAtXPolicy
from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
from pypy.rlib.debug import debug_print
-from pypy.rlib.jit import JitDriver, dont_look_inside, we_are_jitted
+from pypy.rlib.jit import JitDriver, dont_look_inside, we_are_jitted,\
+ promote_string
from pypy.rlib.rstring import StringBuilder
from pypy.rpython.ootypesystem import ootype
@@ -507,6 +508,18 @@
'jump': 1, 'int_is_true': 1,
'guard_not_invalidated': 1})
+ def test_promote_string(self):
+ driver = JitDriver(greens = [], reds = ['n'])
+
+ def f(n):
+ while n < 21:
+ driver.jit_merge_point(n=n)
+ promote_string(str(n % 3))
+ n += 1
+ return 0
+
+ self.meta_interp(f, [0])
+ self.check_loops(call=3 + 1) # one for int2str
#class TestOOtype(StringTests, OOJitMixin):
# CALL = "oosend"
diff --git a/pypy/jit/metainterp/test/test_virtualref.py b/pypy/jit/metainterp/test/test_virtualref.py
--- a/pypy/jit/metainterp/test/test_virtualref.py
+++ b/pypy/jit/metainterp/test/test_virtualref.py
@@ -3,6 +3,7 @@
from pypy.rpython.llinterp import LLException
from pypy.rlib.jit import JitDriver, dont_look_inside, vref_None
from pypy.rlib.jit import virtual_ref, virtual_ref_finish, InvalidVirtualRef
+from pypy.rlib.jit import non_virtual_ref
from pypy.rlib.objectmodel import compute_unique_id
from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin, _get_jitcodes
from pypy.jit.metainterp.resoperation import rop
@@ -595,6 +596,65 @@
res = self.meta_interp(fn, [10])
assert res == 6
+ def test_is_virtual(self):
+ myjitdriver = JitDriver(greens=[], reds=['n', 'res1'])
+ class X:
+ pass
+ @dont_look_inside
+ def residual(vref):
+ return vref.virtual
+ #
+ def f(n):
+ res1 = -42
+ while n > 0:
+ myjitdriver.jit_merge_point(n=n, res1=res1)
+ x = X()
+ vref = virtual_ref(x)
+ res1 = residual(vref)
+ virtual_ref_finish(vref, x)
+ n -= 1
+ return res1
+ #
+ res = self.meta_interp(f, [10])
+ assert res == 1
+
+ def test_is_not_virtual_none(self):
+ myjitdriver = JitDriver(greens=[], reds=['n', 'res1'])
+ @dont_look_inside
+ def residual(vref):
+ return vref.virtual
+ #
+ def f(n):
+ res1 = -42
+ while n > 0:
+ myjitdriver.jit_merge_point(n=n, res1=res1)
+ res1 = residual(vref_None)
+ n -= 1
+ return res1
+ #
+ res = self.meta_interp(f, [10])
+ assert res == 0
+
+ def test_is_not_virtual_non_none(self):
+ myjitdriver = JitDriver(greens=[], reds=['n', 'res1'])
+ class X:
+ pass
+ @dont_look_inside
+ def residual(vref):
+ return vref.virtual
+ #
+ def f(n):
+ res1 = -42
+ while n > 0:
+ myjitdriver.jit_merge_point(n=n, res1=res1)
+ x = X()
+ res1 = residual(non_virtual_ref(x))
+ n -= 1
+ return res1
+ #
+ res = self.meta_interp(f, [10])
+ assert res == 0
+
class TestLLtype(VRefTests, LLJitMixin):
pass
diff --git a/pypy/jit/metainterp/virtualref.py b/pypy/jit/metainterp/virtualref.py
--- a/pypy/jit/metainterp/virtualref.py
+++ b/pypy/jit/metainterp/virtualref.py
@@ -39,6 +39,7 @@
def replace_force_virtual_with_call(self, graphs):
# similar to rvirtualizable2.replace_force_virtualizable_with_call().
c_force_virtual_ptr = None
+ c_is_virtual_ptr = None
force_virtual_count = 0
for graph in graphs:
for block in graph.iterblocks():
@@ -52,6 +53,13 @@
op.opname = 'direct_call'
op.args = [c_force_virtual_ptr, op.args[0]]
force_virtual_count += 1
+ #
+ if op.opname == 'jit_is_virtual':
+ if c_is_virtual_ptr is None:
+ c_is_virtual_ptr = self.get_is_virtual_fnptr()
+ #
+ op.opname = 'direct_call'
+ op.args = [c_is_virtual_ptr, op.args[0]]
#
if c_force_virtual_ptr is not None:
log("replaced %d 'jit_force_virtual' with %r" % (force_virtual_count,
@@ -129,6 +137,17 @@
force_virtual_if_necessary)
return inputconst(lltype.typeOf(funcptr), funcptr)
+ def get_is_virtual_fnptr(self):
+ #
+ def is_virtual(inst):
+ if not inst:
+ return False
+ return inst.typeptr == self.jit_virtual_ref_vtable
+ #
+ FUNC = lltype.FuncType([rclass.OBJECTPTR], lltype.Bool)
+ funcptr = self.warmrunnerdesc.helper_func(lltype.Ptr(FUNC), is_virtual)
+ return inputconst(lltype.typeOf(funcptr), funcptr)
+
def force_virtual(self, inst):
vref = lltype.cast_pointer(lltype.Ptr(self.JIT_VIRTUAL_REF), inst)
token = vref.virtual_token
diff --git a/pypy/jit/metainterp/warmstate.py b/pypy/jit/metainterp/warmstate.py
--- a/pypy/jit/metainterp/warmstate.py
+++ b/pypy/jit/metainterp/warmstate.py
@@ -178,7 +178,7 @@
if self.compiled_merge_points_wref is not None:
for wref in self.compiled_merge_points_wref:
looptoken = wref()
- if looptoken is not None:
+ if looptoken is not None and not looptoken.invalidated:
result.append(looptoken)
return result
diff --git a/pypy/module/__builtin__/__init__.py b/pypy/module/__builtin__/__init__.py
--- a/pypy/module/__builtin__/__init__.py
+++ b/pypy/module/__builtin__/__init__.py
@@ -22,6 +22,9 @@
'any' : 'app_functional.any',
'all' : 'app_functional.all',
'sum' : 'app_functional.sum',
+ 'map' : 'app_functional.map',
+ 'reduce' : 'app_functional.reduce',
+ 'filter' : 'app_functional.filter',
'vars' : 'app_inspect.vars',
'dir' : 'app_inspect.dir',
@@ -85,11 +88,8 @@
'enumerate' : 'functional.W_Enumerate',
'min' : 'functional.min',
'max' : 'functional.max',
- 'map' : 'functional.map',
'zip' : 'functional.zip',
- 'reduce' : 'functional.reduce',
'reversed' : 'functional.reversed',
- 'filter' : 'functional.filter',
'super' : 'descriptor.W_Super',
'staticmethod' : 'descriptor.StaticMethod',
'classmethod' : 'descriptor.ClassMethod',
@@ -118,7 +118,7 @@
builtin = space.interpclass_w(w_builtin)
if isinstance(builtin, module.Module):
return builtin
- # no builtin! make a default one. Given them None, at least.
+ # no builtin! make a default one. Give them None, at least.
builtin = module.Module(space, None)
space.setitem(builtin.w_dict, space.wrap('None'), space.w_None)
return builtin
diff --git a/pypy/module/__builtin__/app_functional.py b/pypy/module/__builtin__/app_functional.py
--- a/pypy/module/__builtin__/app_functional.py
+++ b/pypy/module/__builtin__/app_functional.py
@@ -48,4 +48,118 @@
# Very intentionally *not* +=, that would have different semantics if
# start was a mutable type, such as a list
last = last + x
- return last
\ No newline at end of file
+ return last
+
+def map(func, *collections):
+ """map(function, sequence[, sequence, ...]) -> list
+
+Return a list of the results of applying the function to the items of
+the argument sequence(s). If more than one sequence is given, the
+function is called with an argument list consisting of the corresponding
+item of each sequence, substituting None for missing values when not all
+sequences have the same length. If the function is None, return a list of
+the items of the sequence (or a list of tuples if more than one sequence)."""
+ if not collections:
+ raise TypeError("map() requires at least two arguments")
+ num_collections = len(collections)
+ none_func = func is None
+ if num_collections == 1:
+ if none_func:
+ return list(collections[0])
+ else:
+ # Special case for the really common case of a single collection,
+ # this can be eliminated if we could unroll that loop that creates
+ # `args` based on whether or not len(collections) was constant
+ result = []
+ for item in collections[0]:
+ result.append(func(item))
+ return result
+ result = []
+ # Pair of (iterator, has_finished)
+ iterators = [(iter(seq), False) for seq in collections]
+ while True:
+ cont = False
+ args = []
+ for idx, (iterator, has_finished) in enumerate(iterators):
+ val = None
+ if not has_finished:
+ try:
+ val = next(iterator)
+ except StopIteration:
+ iterators[idx] = (None, True)
+ else:
+ cont = True
+ args.append(val)
+ args = tuple(args)
+ if cont:
+ if none_func:
+ result.append(args)
+ else:
+ result.append(func(*args))
+ else:
+ return result
+
+sentinel = object()
+
+def reduce(func, sequence, initial=sentinel):
+ """reduce(function, sequence[, initial]) -> value
+
+Apply a function of two arguments cumulatively to the items of a sequence,
+from left to right, so as to reduce the sequence to a single value.
+For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
+((((1+2)+3)+4)+5). If initial is present, it is placed before the items
+of the sequence in the calculation, and serves as a default when the
+sequence is empty."""
+ iterator = iter(sequence)
+ if initial is sentinel:
+ try:
+ initial = next(iterator)
+ except StopIteration:
+ raise TypeError("reduce() of empty sequence with no initial value")
+ result = initial
+ for item in iterator:
+ result = func(result, item)
+ return result
+
+def filter(func, seq):
+ """filter(function or None, sequence) -> list, tuple, or string
+
+Return those items of sequence for which function(item) is true. If
+function is None, return the items that are true. If sequence is a tuple
+or string, return the same type, else return a list."""
+ if func is None:
+ func = bool
+ if isinstance(seq, str):
+ return _filter_string(func, seq, str)
+ elif isinstance(seq, unicode):
+ return _filter_string(func, seq, unicode)
+ elif isinstance(seq, tuple):
+ return _filter_tuple(func, seq)
+ result = []
+ for item in seq:
+ if func(item):
+ result.append(item)
+ return result
+
+def _filter_string(func, string, str_type):
+ if func is bool and type(string) is str_type:
+ return string
+ result = []
+ for i in range(len(string)):
+ # You must call __getitem__ on the strings, simply iterating doesn't
+ # work :/
+ item = string[i]
+ if func(item):
+ if not isinstance(item, str_type):
+ raise TypeError("__getitem__ returned a non-string type")
+ result.append(item)
+ return str_type().join(result)
+
+def _filter_tuple(func, seq):
+ result = []
+ for i in range(len(seq)):
+ # Again, must call __getitem__, at least there are tests.
+ item = seq[i]
+ if func(item):
+ result.append(item)
+ return tuple(result)
\ No newline at end of file
diff --git a/pypy/module/__builtin__/compiling.py b/pypy/module/__builtin__/compiling.py
--- a/pypy/module/__builtin__/compiling.py
+++ b/pypy/module/__builtin__/compiling.py
@@ -82,8 +82,8 @@
raise OperationError(space.w_TypeError,
w('eval() arg 1 must be a string or code object'))
- caller = space.getexecutioncontext().gettopframe_nohidden()
if space.is_w(w_globals, space.w_None):
+ caller = space.getexecutioncontext().gettopframe_nohidden()
if caller is None:
w_globals = space.newdict()
if space.is_w(w_locals, space.w_None):
@@ -95,14 +95,10 @@
elif space.is_w(w_locals, space.w_None):
w_locals = w_globals
- try:
- space.getitem(w_globals, space.wrap('__builtins__'))
- except OperationError, e:
- if not e.match(space, space.w_KeyError):
- raise
- if caller is not None:
- w_builtin = space.builtin.pick_builtin(caller.w_globals)
- space.setitem(w_globals, space.wrap('__builtins__'), w_builtin)
+ # xxx removed: adding '__builtins__' to the w_globals dict, if there
+ # is none. This logic was removed as costly (it requires to get at
+ # the gettopframe_nohidden()). I bet no test fails, and it's a really
+ # obscure case.
return codeobj.exec_code(space, w_globals, w_locals)
diff --git a/pypy/module/__builtin__/functional.py b/pypy/module/__builtin__/functional.py
--- a/pypy/module/__builtin__/functional.py
+++ b/pypy/module/__builtin__/functional.py
@@ -116,118 +116,6 @@
"""
return min_max(space, __args__, "min")
- at unwrap_spec(collections_w="args_w")
-def map(space, w_func, collections_w):
- """does 3 separate things, hence this enormous docstring.
- 1. if function is None, return a list of tuples, each with one
- item from each collection. If the collections have different
- lengths, shorter ones are padded with None.
-
- 2. if function is not None, and there is only one collection,
- apply function to every item in the collection and return a
- list of the results.
-
- 3. if function is not None, and there are several collections,
- repeatedly call the function with one argument from each
- collection. If the collections have different lengths,
- shorter ones are padded with None
- """
- if not collections_w:
- msg = "map() requires at least two arguments"
- raise OperationError(space.w_TypeError, space.wrap(msg))
- none_func = space.is_w(w_func, space.w_None)
- if len(collections_w) == 1:
- w_collection = collections_w[0]
- if none_func:
- result_w = space.unpackiterable(w_collection)
- else:
- result_w = map_single_collection(space, w_func, w_collection)
- else:
- result_w = map_multiple_collections(space, w_func, collections_w,
- none_func)
- return space.newlist(result_w)
-
-def map_single_collection(space, w_func, w_collection):
- """Special case for 'map(func, coll)', where 'func' is not None and there
- is only one 'coll' argument."""
- w_iter = space.iter(w_collection)
- # xxx special hacks for speed
- from pypy.interpreter import function, pycode
- if isinstance(w_func, function.Function):
- # xxx compatibility issue: what if func_code is modified in the
- # middle of running map()?? That's far too obscure for me to care...
- code = w_func.getcode()
- fast_natural_arity = code.fast_natural_arity
- if fast_natural_arity == (1|pycode.PyCode.FLATPYCALL):
- assert isinstance(code, pycode.PyCode)
- return map_single_user_function(code, w_func, w_iter)
- # /xxx end of special hacks
- return map_single_other_callable(space, w_func, w_iter)
-
-def map_single_other_callable(space, w_func, w_iter):
- result_w = []
- while True:
- try:
- w_item = space.next(w_iter)
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- break
- result_w.append(space.call_function(w_func, w_item))
- return result_w
-map_single_other_callable._dont_inline_ = True
-
-from pypy.rlib.jit import JitDriver
-mapjitdriver = JitDriver(greens = ['code'],
- reds = ['w_func', 'w_iter', 'result_w'])
-def map_single_user_function(code, w_func, w_iter):
- result_w = []
- while True:
- mapjitdriver.can_enter_jit(code=code, w_func=w_func,
- w_iter=w_iter, result_w=result_w)
- mapjitdriver.jit_merge_point(code=code, w_func=w_func,
- w_iter=w_iter, result_w=result_w)
- space = w_func.space
- try:
- w_item = space.next(w_iter)
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- break
- new_frame = space.createframe(code, w_func.w_func_globals,
- w_func)
- new_frame.locals_stack_w[0] = w_item
- w_res = new_frame.run()
- result_w.append(w_res)
- return result_w
-
-def map_multiple_collections(space, w_func, collections_w, none_func):
- result_w = []
- iterators_w = [space.iter(w_seq) for w_seq in collections_w]
- num_iterators = len(iterators_w)
- while True:
- cont = False
- args_w = [space.w_None] * num_iterators
- for i in range(num_iterators):
- if iterators_w[i] is not None:
- try:
- args_w[i] = space.next(iterators_w[i])
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- iterators_w[i] = None
- else:
- cont = True
- if not cont:
- break
- w_args = space.newtuple(args_w)
- if none_func:
- w_res = w_args
- else:
- w_res = space.call(w_func, w_args)
- result_w.append(w_res)
- return result_w
-
@unwrap_spec(sequences_w="args_w")
def zip(space, sequences_w):
"""Return a list of tuples, where the nth tuple contains every nth item of
@@ -249,90 +137,6 @@
return space.newlist(result_w)
result_w.append(space.newtuple(items_w))
-def reduce(space, w_func, w_sequence, w_initial=NoneNotWrapped):
- """ Apply function of two arguments cumulatively to the items of sequence,
- from left to right, so as to reduce the sequence to a single value.
- Optionally begin with an initial value.
- """
- w_iter = space.iter(w_sequence)
- if w_initial is None:
- try:
- w_initial = space.next(w_iter)
- except OperationError, e:
- if e.match(space, space.w_StopIteration):
- msg = "reduce() of empty sequence with no initial value"
- raise OperationError(space.w_TypeError, space.wrap(msg))
- raise
- w_result = w_initial
- while True:
- try:
- w_next = space.next(w_iter)
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- break
- w_result = space.call_function(w_func, w_result, w_next)
- return w_result
-
-def filter(space, w_func, w_seq):
- """construct a list of those elements of collection for which function
- is True. If function is None, then return the items in the sequence
- which are True.
- """
- if space.is_true(space.isinstance(w_seq, space.w_str)):
- return _filter_string(space, w_func, w_seq, space.w_str)
- if space.is_true(space.isinstance(w_seq, space.w_unicode)):
- return _filter_string(space, w_func, w_seq, space.w_unicode)
- if space.is_true(space.isinstance(w_seq, space.w_tuple)):
- return _filter_tuple(space, w_func, w_seq)
- w_iter = space.iter(w_seq)
- result_w = []
- none_func = space.is_w(w_func, space.w_None)
- while True:
- try:
- w_next = space.next(w_iter)
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- break
- if none_func:
- w_keep = w_next
- else:
- w_keep = space.call_function(w_func, w_next)
- if space.is_true(w_keep):
- result_w.append(w_next)
- return space.newlist(result_w)
-
-def _filter_tuple(space, w_func, w_tuple):
- none_func = space.is_w(w_func, space.w_None)
- length = space.len_w(w_tuple)
- result_w = []
- for i in range(length):
- w_item = space.getitem(w_tuple, space.wrap(i))
- if none_func:
- w_keep = w_item
- else:
- w_keep = space.call_function(w_func, w_item)
- if space.is_true(w_keep):
- result_w.append(w_item)
- return space.newtuple(result_w)
-
-def _filter_string(space, w_func, w_string, w_str_type):
- none_func = space.is_w(w_func, space.w_None)
- if none_func and space.is_w(space.type(w_string), w_str_type):
- return w_string
- length = space.len_w(w_string)
- result_w = []
- for i in range(length):
- w_item = space.getitem(w_string, space.wrap(i))
- if none_func or space.is_true(space.call_function(w_func, w_item)):
- if not space.is_true(space.isinstance(w_item, w_str_type)):
- msg = "__getitem__ returned a non-string type"
- raise OperationError(space.w_TypeError, space.wrap(msg))
- result_w.append(w_item)
- w_empty = space.call_function(w_str_type)
- return space.call_method(w_empty, "join", space.newlist(result_w))
-
class W_Enumerate(Wrappable):
def __init__(self, w_iter, w_start):
diff --git a/pypy/module/__builtin__/test/test_functional.py b/pypy/module/__builtin__/test/test_functional.py
--- a/pypy/module/__builtin__/test/test_functional.py
+++ b/pypy/module/__builtin__/test/test_functional.py
@@ -147,7 +147,7 @@
assert list(range(A())) == [0, 1, 2, 3, 4]
assert list(range(0, A())) == [0, 1, 2, 3, 4]
assert list(range(0, 10, A())) == [0, 5]
-
+
def test_range_float(self):
assert list(range(0.1, 2.0, 1.1)) == [0, 1]
diff --git a/pypy/module/_continuation/interp_continuation.py b/pypy/module/_continuation/interp_continuation.py
--- a/pypy/module/_continuation/interp_continuation.py
+++ b/pypy/module/_continuation/interp_continuation.py
@@ -19,6 +19,11 @@
# - normal: self.sthread != None, not is_empty_handle(self.h)
# - finished: self.sthread != None, is_empty_handle(self.h)
+ def __del__(self):
+ sthread = self.sthread
+ if sthread is not None and not sthread.is_empty_handle(self.h):
+ sthread.destroy(self.h)
+
def check_sthread(self):
ec = self.space.getexecutioncontext()
if ec.stacklet_thread is not self.sthread:
@@ -28,6 +33,8 @@
def descr_init(self, w_callable, __args__):
if self.sthread is not None:
raise geterror(self.space, "continulet already __init__ialized")
+ sthread = build_sthread(self.space)
+ workaround_disable_jit(sthread)
#
# hackish: build the frame "by hand", passing it the correct arguments
space = self.space
@@ -41,7 +48,6 @@
self.bottomframe = bottomframe
#
global_state.origin = self
- sthread = build_sthread(self.space)
self.sthread = sthread
h = sthread.new(new_stacklet_callback)
post_switch(sthread, h)
@@ -71,6 +77,7 @@
global_state.clear()
raise geterror(self.space, "continulet already finished")
self.check_sthread()
+ workaround_disable_jit(self.sthread)
#
global_state.origin = self
if to is None:
@@ -259,6 +266,16 @@
sthread = ec.stacklet_thread = SThread(space, ec)
return sthread
+def workaround_disable_jit(sthread):
+ # A bad workaround to kill the JIT anywhere in this thread.
+ # This forces all the frames. It's a bad workaround because
+ # it takes O(depth) time, and it will cause some "abort:
+ # vable escape" in the JIT. The goal is to prevent any frame
+ # from being still virtuals, because the JIT generates code
+ # to un-virtualizable them "on demand" by loading values based
+ # on FORCE_TOKEN, which is an address in the stack.
+ sthread.ec.force_all_frames()
+
# ____________________________________________________________
def permute(space, args_w):
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
@@ -260,8 +260,12 @@
class AppTestNonblocking(object):
def setup_class(cls):
from pypy.module._file.interp_file import W_File
-
+
cls.old_read = os.read
+
+ if option.runappdirect:
+ py.test.skip("works with internals of _file impl on py.py")
+
state = [0]
def read(fd, n=None):
if fd != 42:
@@ -281,7 +285,7 @@
def teardown_class(cls):
os.read = cls.old_read
-
+
def test_nonblocking_file(self):
res = self.stream.read()
assert res == 'xyz'
@@ -403,24 +407,24 @@
with self.file(self.temppath, 'w') as f:
f.write('foo')
assert f.closed
-
+
with self.file(self.temppath, 'r') as f:
s = f.readline()
assert s == "foo"
assert f.closed
-
+
def test_subclass_with(self):
file = self.file
class C(file):
def __init__(self, *args, **kwargs):
self.subclass_closed = False
file.__init__(self, *args, **kwargs)
-
+
def close(self):
self.subclass_closed = True
file.close(self)
-
+
with C(self.temppath, 'w') as f:
pass
assert f.subclass_closed
diff --git a/pypy/module/micronumpy/interp_dtype.py b/pypy/module/micronumpy/interp_dtype.py
--- a/pypy/module/micronumpy/interp_dtype.py
+++ b/pypy/module/micronumpy/interp_dtype.py
@@ -161,9 +161,6 @@
@binop
def mul(self, v1, v2):
return v1 * v2
- @binop
- def div(self, v1, v2):
- return v1 / v2
@unaryop
def pos(self, v):
@@ -217,6 +214,14 @@
return float2string(self.for_computation(self.unbox(item)), 'g', rfloat.DTSF_STR_PRECISION)
@binop
+ def div(self, v1, v2):
+ try:
+ return v1 / v2
+ except ZeroDivisionError:
+ if v1 == v2 == 0.0:
+ return rfloat.NAN
+ return rfloat.copysign(rfloat.INFINITY, v1 * v2)
+ @binop
def mod(self, v1, v2):
return math.fmod(v1, v2)
@binop
@@ -295,6 +300,11 @@
return str(widen(self.unbox(item)))
@binop
+ def div(self, v1, v2):
+ if v2 == 0:
+ return 0
+ return v1 / v2
+ @binop
def mod(self, v1, v2):
return v1 % v2
@@ -426,23 +436,22 @@
pass
if LONG_BIT == 32:
- class W_LongDtype(W_Int32Dtype):
- pass
+ long_dtype = W_Int32Dtype
+ ulong_dtype = W_UInt32Dtype
+elif LONG_BIT == 64:
+ long_dtype = W_Int64Dtype
+ ulong_dtype = W_UInt64Dtype
+else:
+ assert False
- class W_ULongDtype(W_UInt32Dtype):
- pass
-else:
- class W_LongDtype(W_Int64Dtype):
- pass
+class W_LongDtype(long_dtype):
+ num = 7
+ aliases = ["l"]
+ applevel_types = ["int"]
- class W_ULongDtype(W_UInt64Dtype):
- pass
-
-W_LongDtype.num = 7
-W_LongDtype.aliases = ["l"]
-W_LongDtype.applevel_types = ["int"]
-W_ULongDtype.num = 8
-W_ULongDtype.aliases = ["L"]
+class W_ULongDtype(ulong_dtype):
+ num = 8
+ aliases = ["L"]
W_Float32Dtype = create_low_level_dtype(
num = 11, kind = FLOATINGLTR, name = "float32",
diff --git a/pypy/module/micronumpy/test/test_numarray.py b/pypy/module/micronumpy/test/test_numarray.py
--- a/pypy/module/micronumpy/test/test_numarray.py
+++ b/pypy/module/micronumpy/test/test_numarray.py
@@ -285,7 +285,9 @@
assert b[i] == i * 5
def test_div(self):
- from numpy import array, dtype
+ from math import isnan
+ from numpy import array, dtype, inf
+
a = array(range(1, 6))
b = a / a
for i in range(5):
@@ -297,6 +299,24 @@
for i in range(5):
assert b[i] == 1
+ a = array([-1, 0, 1])
+ b = array([0, 0, 0])
+ c = a / b
+ assert (c == [0, 0, 0]).all()
+
+ a = array([-1.0, 0.0, 1.0])
+ b = array([0.0, 0.0, 0.0])
+ c = a / b
+ assert c[0] == -inf
+ assert isnan(c[1])
+ assert c[2] == inf
+
+ b = array([-0.0, -0.0, -0.0])
+ c = a / b
+ assert c[0] == inf
+ assert isnan(c[1])
+ assert c[2] == -inf
+
def test_div_other(self):
from numpy import array
a = array(range(5))
diff --git a/pypy/module/pypyjit/interp_jit.py b/pypy/module/pypyjit/interp_jit.py
--- a/pypy/module/pypyjit/interp_jit.py
+++ b/pypy/module/pypyjit/interp_jit.py
@@ -137,20 +137,15 @@
def jump_absolute(self, jumpto, _, ec=None):
if we_are_jitted():
- # Normally, the tick counter is decremented by 100 for every
- # Python opcode. Here, to better support JIT compilation of
- # small loops, we decrement it by a possibly smaller constant.
- # We get the maximum 100 when the (unoptimized) trace length
- # is at least 3200 (a bit randomly).
- trace_length = r_uint(current_trace_length())
- decr_by = trace_length // 32
- if decr_by < 1:
- decr_by = 1
- elif decr_by > 100: # also if current_trace_length() returned -1
- decr_by = 100
+ #
+ # assume that only threads are using the bytecode counter
+ decr_by = 0
+ if self.space.actionflag.has_bytecode_counter: # constant-folded
+ if self.space.threadlocals.gil_ready: # quasi-immutable field
+ decr_by = _get_adapted_tick_counter()
#
self.last_instr = intmask(jumpto)
- ec.bytecode_trace(self, intmask(decr_by))
+ ec.bytecode_trace(self, decr_by)
jumpto = r_uint(self.last_instr)
#
pypyjitdriver.can_enter_jit(frame=self, ec=ec, next_instr=jumpto,
@@ -158,6 +153,20 @@
is_being_profiled=self.is_being_profiled)
return jumpto
+def _get_adapted_tick_counter():
+ # Normally, the tick counter is decremented by 100 for every
+ # Python opcode. Here, to better support JIT compilation of
+ # small loops, we decrement it by a possibly smaller constant.
+ # We get the maximum 100 when the (unoptimized) trace length
+ # is at least 3200 (a bit randomly).
+ trace_length = r_uint(current_trace_length())
+ decr_by = trace_length // 32
+ if decr_by < 1:
+ decr_by = 1
+ elif decr_by > 100: # also if current_trace_length() returned -1
+ decr_by = 100
+ return intmask(decr_by)
+
PyCode__initialize = PyCode._initialize
diff --git a/pypy/module/pypyjit/test_pypy_c/model.py b/pypy/module/pypyjit/test_pypy_c/model.py
--- a/pypy/module/pypyjit/test_pypy_c/model.py
+++ b/pypy/module/pypyjit/test_pypy_c/model.py
@@ -225,6 +225,8 @@
# strip comment
if '#' in line:
line = line[:line.index('#')]
+ if line.strip() == 'guard_not_invalidated?':
+ return 'guard_not_invalidated', None, [], '...', False
# find the resvar, if any
if ' = ' in line:
resvar, _, line = line.partition(' = ')
@@ -249,7 +251,7 @@
descr = descr[len('descr='):]
else:
descr = None
- return opname, resvar, args, descr
+ return opname, resvar, args, descr, True
@classmethod
def preprocess_expected_src(cls, src):
@@ -258,13 +260,23 @@
# replaced with the corresponding operations, so that tests don't have
# to repeat it every time
ticker_check = """
+ guard_not_invalidated?
+ ticker0 = getfield_raw(ticker_address, descr=<SignedFieldDescr pypysig_long_struct.c_value .*>)
+ ticker_cond0 = int_lt(ticker0, 0)
+ guard_false(ticker_cond0, descr=...)
+ """
+ src = src.replace('--TICK--', ticker_check)
+ #
+ # this is the ticker check generated if we have threads
+ thread_ticker_check = """
+ guard_not_invalidated?
ticker0 = getfield_raw(ticker_address, descr=<SignedFieldDescr pypysig_long_struct.c_value .*>)
ticker1 = int_sub(ticker0, 1)
setfield_raw(ticker_address, ticker1, descr=<SignedFieldDescr pypysig_long_struct.c_value .*>)
ticker_cond0 = int_lt(ticker1, 0)
guard_false(ticker_cond0, descr=...)
"""
- src = src.replace('--TICK--', ticker_check)
+ src = src.replace('--THREAD-TICK--', thread_ticker_check)
#
# this is the ticker check generated in PyFrame.handle_operation_error
exc_ticker_check = """
@@ -298,7 +310,7 @@
if not cond:
raise InvalidMatch(message, frame=sys._getframe(1))
- def match_op(self, op, (exp_opname, exp_res, exp_args, exp_descr)):
+ def match_op(self, op, (exp_opname, exp_res, exp_args, exp_descr, _)):
self._assert(op.name == exp_opname, "operation mismatch")
self.match_var(op.res, exp_res)
if exp_args != ['...']:
@@ -341,7 +353,7 @@
what is after the '...'
"""
iter_exp_ops = iter(expected_ops)
- iter_ops = iter(self.ops)
+ iter_ops = RevertableIterator(self.ops)
for opindex, exp_op in enumerate(iter_exp_ops):
try:
if exp_op == '...':
@@ -360,6 +372,9 @@
break
self.match_op(op, exp_op)
except InvalidMatch, e:
+ if exp_op[4] is False: # optional operation
+ iter_ops.revert_one()
+ continue # try to match with the next exp_op
e.opindex = opindex
raise
#
@@ -372,8 +387,8 @@
return ''
text = str(py.code.Source(src).deindent().indent())
lines = text.splitlines(True)
- if opindex is not None and 0 <= opindex < len(lines):
- lines[opindex] = lines[opindex].rstrip() + '\t<=====\n'
+ if opindex is not None and 0 <= opindex <= len(lines):
+ lines.insert(opindex, '\n\t===== HERE =====\n')
return ''.join(lines)
#
expected_src = self.preprocess_expected_src(expected_src)
@@ -398,3 +413,18 @@
else:
return True
+
+class RevertableIterator(object):
+ def __init__(self, sequence):
+ self.sequence = sequence
+ self.index = 0
+ def __iter__(self):
+ return self
+ def next(self):
+ index = self.index
+ if index == len(self.sequence):
+ raise StopIteration
+ self.index = index + 1
+ return self.sequence[index]
+ def revert_one(self):
+ self.index -= 1
diff --git a/pypy/module/pypyjit/test_pypy_c/test_00_model.py b/pypy/module/pypyjit/test_pypy_c/test_00_model.py
--- a/pypy/module/pypyjit/test_pypy_c/test_00_model.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_00_model.py
@@ -145,15 +145,17 @@
def test_parse_op(self):
res = OpMatcher.parse_op(" a = int_add( b, 3 ) # foo")
- assert res == ("int_add", "a", ["b", "3"], None)
+ assert res == ("int_add", "a", ["b", "3"], None, True)
res = OpMatcher.parse_op("guard_true(a)")
- assert res == ("guard_true", None, ["a"], None)
+ assert res == ("guard_true", None, ["a"], None, True)
res = OpMatcher.parse_op("setfield_gc(p0, i0, descr=<foobar>)")
- assert res == ("setfield_gc", None, ["p0", "i0"], "<foobar>")
+ assert res == ("setfield_gc", None, ["p0", "i0"], "<foobar>", True)
res = OpMatcher.parse_op("i1 = getfield_gc(p0, descr=<foobar>)")
- assert res == ("getfield_gc", "i1", ["p0"], "<foobar>")
+ assert res == ("getfield_gc", "i1", ["p0"], "<foobar>", True)
res = OpMatcher.parse_op("p0 = force_token()")
- assert res == ("force_token", "p0", [], None)
+ assert res == ("force_token", "p0", [], None, True)
+ res = OpMatcher.parse_op("guard_not_invalidated?")
+ assert res == ("guard_not_invalidated", None, [], '...', False)
def test_exact_match(self):
loop = """
@@ -341,7 +343,7 @@
# this is the actual loop
'int_lt', 'guard_true', 'int_add',
# this is the signal checking stuff
- 'getfield_raw', 'int_sub', 'setfield_raw', 'int_lt', 'guard_false',
+ 'guard_not_invalidated', 'getfield_raw', 'int_lt', 'guard_false',
'jump'
]
@@ -407,7 +409,7 @@
# this is the actual loop
'int_lt', 'guard_true', 'force_token', 'int_add',
# this is the signal checking stuff
- 'getfield_raw', 'int_sub', 'setfield_raw', 'int_lt', 'guard_false',
+ 'guard_not_invalidated', 'getfield_raw', 'int_lt', 'guard_false',
'jump'
]
@@ -425,10 +427,9 @@
guard_true(i6, descr=...)
i8 = int_add(i4, 1)
# signal checking stuff
+ guard_not_invalidated(descr=...)
i10 = getfield_raw(37212896, descr=<.* pypysig_long_struct.c_value .*>)
- i12 = int_sub(i10, 1)
- setfield_raw(37212896, i12, descr=<.* pypysig_long_struct.c_value .*>)
- i14 = int_lt(i12, 0)
+ i14 = int_lt(i10, 0)
guard_false(i14, descr=...)
jump(p0, p1, p2, p3, i8, descr=...)
""")
diff --git a/pypy/module/pypyjit/test_pypy_c/test_misc.py b/pypy/module/pypyjit/test_pypy_c/test_misc.py
--- a/pypy/module/pypyjit/test_pypy_c/test_misc.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_misc.py
@@ -329,4 +329,20 @@
guard_false(i28, descr=...)
i30 = int_lshift(i20, 24)
i31 = int_or(i26, i30)
- """ % {"32_bit_only": extra})
\ No newline at end of file
+ """ % {"32_bit_only": extra})
+
+ def test_eval(self):
+ def main():
+ i = 1
+ a = compile('x+x+x+x+x+x', 'eval', 'eval')
+ b = {'x': 7}
+ while i < 1000:
+ y = eval(a,b,b) # ID: eval
+ i += 1
+ return y
+
+ log = self.run(main)
+ assert log.result == 42
+ # the following assertion fails if the loop was cancelled due
+ # to "abort: vable escape"
+ assert len(log.loops_by_id("eval")) == 1
diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py
--- a/pypy/module/pypyjit/test_pypy_c/test_string.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_string.py
@@ -41,7 +41,7 @@
guard_true(i32, descr=...)
i34 = int_add(i6, 1)
--TICK--
- jump(p0, p1, p2, p3, p4, p5, i34, p7, p8, i9, i10, p11, i12, p13, descr=<Loop4>)
+ jump(p0, p1, p2, p3, p4, p5, i34, p7, p8, i9, i10, p11, i12, p13, descr=...)
""")
def test_long(self):
@@ -106,7 +106,7 @@
i58 = int_add_ovf(i6, i57)
guard_no_overflow(descr=...)
--TICK--
- jump(p0, p1, p2, p3, p4, p5, i58, i7, descr=<Loop4>)
+ jump(p0, p1, p2, p3, p4, p5, i58, i7, descr=...)
""")
def test_str_mod(self):
@@ -156,4 +156,41 @@
i40 = int_sub(i4, 1)
--TICK--
jump(p0, p1, p2, p3, i40, i38, descr=<Loop0>)
- """)
\ No newline at end of file
+ """)
+
+ def test_getattr_promote(self):
+ def main(n):
+ class A(object):
+ def meth_a(self):
+ return 1
+ def meth_b(self):
+ return 2
+ a = A()
+
+ l = ['a', 'b']
+ s = 0
+ for i in range(n):
+ name = 'meth_' + l[i & 1]
+ meth = getattr(a, name) # ID: getattr
+ s += meth()
+ return s
+
+ log = self.run(main, [1000])
+ assert log.result == main(1000)
+ loops = log.loops_by_filename(self.filepath)
+ assert len(loops) == 2
+ for loop in loops:
+ loop.match_by_id('getattr','''
+ guard_not_invalidated(descr=...)
+ i32 = strlen(p31)
+ i34 = int_add(5, i32)
+ p35 = newstr(i34)
+ strsetitem(p35, 0, 109)
+ strsetitem(p35, 1, 101)
+ strsetitem(p35, 2, 116)
+ strsetitem(p35, 3, 104)
+ strsetitem(p35, 4, 95)
+ copystrcontent(p31, p35, 0, 5, i32)
+ i49 = call(ConstClass(_ll_2_str_eq_nonnull__rpy_stringPtr_rpy_stringPtr), p35, ConstPtr(ptr48), descr=<SignedCallDescr>)
+ guard_value(i49, 1, descr=<Guard8>)
+ ''')
diff --git a/pypy/module/pypyjit/test_pypy_c/test_thread.py b/pypy/module/pypyjit/test_pypy_c/test_thread.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/pypyjit/test_pypy_c/test_thread.py
@@ -0,0 +1,28 @@
+from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC
+
+
+class TestThread(BaseTestPyPyC):
+ def test_simple(self):
+ def main(n):
+ import thread
+ def f():
+ i = 0
+ while i < n:
+ i += 1
+ done.release()
+
+ done = thread.allocate_lock()
+ done.acquire()
+ thread.start_new_thread(f, ())
+ done.acquire()
+ return 0
+ log = self.run(main, [500])
+ assert round(log.result, 6) == round(main(500), 6)
+ loop, = log.loops_by_filename(self.filepath)
+ assert loop.match("""
+ i2 = int_lt(i0, i1)
+ guard_true(i2, descr=...)
+ i3 = int_add(i0, 1)
+ --THREAD-TICK--
+ jump(..., descr=<Loop0>)
+ """)
diff --git a/pypy/module/signal/interp_signal.py b/pypy/module/signal/interp_signal.py
--- a/pypy/module/signal/interp_signal.py
+++ b/pypy/module/signal/interp_signal.py
@@ -109,8 +109,11 @@
p = pypysig_getaddr_occurred()
value = p.c_value
if self.has_bytecode_counter: # this 'if' is constant-folded
- value -= by
- p.c_value = value
+ if jit.isconstant(by) and by == 0:
+ pass # normally constant-folded too
+ else:
+ value -= by
+ p.c_value = value
return value
diff --git a/pypy/module/sys/__init__.py b/pypy/module/sys/__init__.py
--- a/pypy/module/sys/__init__.py
+++ b/pypy/module/sys/__init__.py
@@ -47,7 +47,7 @@
'pypy_initial_path' : 'state.pypy_initial_path',
'_getframe' : 'vm._getframe',
- '_current_frames' : 'vm._current_frames',
+ '_current_frames' : 'currentframes._current_frames',
'setrecursionlimit' : 'vm.setrecursionlimit',
'getrecursionlimit' : 'vm.getrecursionlimit',
'setcheckinterval' : 'vm.setcheckinterval',
diff --git a/pypy/module/sys/currentframes.py b/pypy/module/sys/currentframes.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/sys/currentframes.py
@@ -0,0 +1,78 @@
+"""
+Implementation of the 'sys._current_frames()' routine.
+"""
+from pypy.interpreter import gateway
+
+app = gateway.applevel('''
+"NOT_RPYTHON"
+import __builtin__
+
+class fake_code(object):
+ co_name = "?"
+ co_filename = "?"
+ co_firstlineno = 0
+
+class fake_frame(object):
+ f_back = None
+ f_builtins = __builtin__.__dict__
+ f_code = fake_code()
+ f_exc_traceback = None
+ f_exc_type = None
+ f_exc_value = None
+ f_globals = {}
+ f_lasti = -1
+ f_lineno = 0
+ f_locals = {}
+ f_restricted = False
+ f_trace = None
+
+ def __init__(self, f):
+ if f is not None:
+ for name in ["f_builtins", "f_code", "f_globals", "f_lasti",
+ "f_lineno"]:
+ setattr(self, name, getattr(f, name))
+''')
+
+def _current_frames(space):
+ """_current_frames() -> dictionary
+
+ Return a dictionary mapping each current thread T's thread id to T's
+ current stack "frame". Functions in the traceback module can build the
+ call stack given such a frame.
+
+ Note that in PyPy this returns fake frame objects, to avoid a runtime
+ penalty everywhere with the JIT. (So far these fake frames can be
+ completely uninformative depending on the JIT state; we could return
+ more with more efforts.)
+
+ This function should be used for specialized purposes only."""
+ w_result = space.newdict()
+ w_fake_frame = app.wget(space, "fake_frame")
+ w_fake_code = app.wget(space, "fake_code")
+ ecs = space.threadlocals.getallvalues()
+ for thread_ident, ec in ecs.items():
+ vref = ec.topframeref
+ frames = []
+ while not vref.virtual:
+ f = vref()
+ if f is None:
+ break
+ frames.append(f)
+ vref = f.f_backref
+ else:
+ frames.append(None)
+ #
+ w_topframe = space.wrap(None)
+ w_prevframe = None
+ for f in frames:
+ w_nextframe = space.call_function(w_fake_frame, space.wrap(f))
+ if w_prevframe is None:
+ w_topframe = w_nextframe
+ else:
+ space.setattr(w_prevframe, space.wrap('f_back'), w_nextframe)
+ w_prevframe = w_nextframe
+ #
+ space.setitem(w_result,
+ space.wrap(thread_ident),
+ w_topframe)
+ return w_result
diff --git a/pypy/module/sys/test/test_sysmodule.py b/pypy/module/sys/test/test_sysmodule.py
--- a/pypy/module/sys/test/test_sysmodule.py
+++ b/pypy/module/sys/test/test_sysmodule.py
@@ -556,7 +556,7 @@
return sys._current_frames()
frames = f()
assert frames.keys() == [0]
- assert frames[0].f_code.co_name == 'f'
+ assert frames[0].f_code.co_name in ('f', '?')
class AppTestCurrentFramesWithThread(AppTestCurrentFrames):
def setup_class(cls):
@@ -568,23 +568,25 @@
import thread
thread_id = thread.get_ident()
- self.ready = False
def other_thread():
- self.ready = True
print "thread started"
- time.sleep(5)
+ lock2.release()
+ lock1.acquire()
+ lock1 = thread.allocate_lock()
+ lock2 = thread.allocate_lock()
+ lock1.acquire()
+ lock2.acquire()
thread.start_new_thread(other_thread, ())
def f():
- for i in range(100):
- if self.ready: break
- time.sleep(0.1)
+ lock2.acquire()
return sys._current_frames()
frames = f()
+ lock1.release()
thisframe = frames.pop(thread_id)
- assert thisframe.f_code.co_name == 'f'
+ assert thisframe.f_code.co_name in ('f', '?')
assert len(frames) == 1
_, other_frame = frames.popitem()
- assert other_frame.f_code.co_name == 'other_thread'
+ assert other_frame.f_code.co_name in ('other_thread', '?')
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
@@ -45,25 +45,6 @@
f.mark_as_escaped()
return space.wrap(f)
-def _current_frames(space):
- """_current_frames() -> dictionary
-
- Return a dictionary mapping each current thread T's thread id to T's
- current stack frame.
-
- This function should be used for specialized purposes only."""
- raise OperationError(space.w_NotImplementedError,
- space.wrap("XXX sys._current_frames() incompatible with the JIT"))
- w_result = space.newdict()
- ecs = space.threadlocals.getallvalues()
- for thread_ident, ec in ecs.items():
- f = ec.gettopframe_nohidden()
- f.mark_as_escaped()
- space.setitem(w_result,
- space.wrap(thread_ident),
- space.wrap(f))
- return w_result
-
def setrecursionlimit(space, w_new_limit):
"""setrecursionlimit() sets the maximum number of nested calls that
can occur before a RuntimeError is raised. On PyPy the limit is
diff --git a/pypy/module/thread/gil.py b/pypy/module/thread/gil.py
--- a/pypy/module/thread/gil.py
+++ b/pypy/module/thread/gil.py
@@ -17,6 +17,7 @@
class GILThreadLocals(OSThreadLocals):
"""A version of OSThreadLocals that enforces a GIL."""
gil_ready = False
+ _immutable_fields_ = ['gil_ready?']
def initialize(self, space):
# add the GIL-releasing callback as an action on the space
diff --git a/pypy/module/thread/threadlocals.py b/pypy/module/thread/threadlocals.py
--- a/pypy/module/thread/threadlocals.py
+++ b/pypy/module/thread/threadlocals.py
@@ -8,9 +8,14 @@
def __init__(self):
self._valuedict = {} # {thread_ident: ExecutionContext()}
+ self._freeze_()
+
+ def _freeze_(self):
+ self._valuedict.clear()
self._mainthreadident = 0
self._mostrecentkey = 0 # fast minicaching for the common case
self._mostrecentvalue = None # fast minicaching for the common case
+ return False
def getvalue(self):
ident = thread.get_ident()
diff --git a/pypy/objspace/std/objecttype.py b/pypy/objspace/std/objecttype.py
--- a/pypy/objspace/std/objecttype.py
+++ b/pypy/objspace/std/objecttype.py
@@ -44,7 +44,6 @@
raise OperationError(space.w_TypeError,
space.wrap("__class__ assignment: only for heap types"))
w_oldcls = space.type(w_obj)
- # XXX taint space should raise a TaintError here if w_oldcls is tainted
assert isinstance(w_oldcls, W_TypeObject)
if w_oldcls.get_full_instance_layout() == w_newcls.get_full_instance_layout():
w_obj.setclass(space, w_newcls)
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
@@ -161,19 +161,19 @@
def str_swapcase__String(space, w_self):
self = w_self._value
- res = [' '] * len(self)
+ builder = StringBuilder(len(self))
for i in range(len(self)):
ch = self[i]
if ch.isupper():
o = ord(ch) + 32
- res[i] = chr(o)
+ builder.append(chr(o))
elif ch.islower():
o = ord(ch) - 32
- res[i] = chr(o)
+ builder.append(chr(o))
else:
- res[i] = ch
+ builder.append(ch)
- return space.wrap("".join(res))
+ return space.wrap(builder.build())
def str_capitalize__String(space, w_self):
@@ -199,19 +199,21 @@
def str_title__String(space, w_self):
input = w_self._value
- buffer = [' '] * len(input)
+ builder = StringBuilder(len(input))
prev_letter=' '
- for pos in range(0, len(input)):
+ for pos in range(len(input)):
ch = input[pos]
if not prev_letter.isalpha():
- buffer[pos] = _upper(ch)
+ ch = _upper(ch)
+ builder.append(ch)
else:
- buffer[pos] = _lower(ch)
+ ch = _lower(ch)
+ builder.append(ch)
- prev_letter = buffer[pos]
+ prev_letter = ch
- return space.wrap("".join(buffer))
+ return space.wrap(builder.build())
def str_split__String_None_ANY(space, w_self, w_none, w_maxsplit=-1):
maxsplit = space.int_w(w_maxsplit)
@@ -754,27 +756,21 @@
input = w_self._value
width = space.int_w(w_width)
- if len(input) >= width:
+ num_zeros = width - len(input)
+ if num_zeros <= 0:
# cannot return w_self, in case it is a subclass of str
return space.wrap(input)
- buf = [' '] * width
+ builder = StringBuilder(width)
if len(input) > 0 and (input[0] == '+' or input[0] == '-'):
- buf[0] = input[0]
+ builder.append(input[0])
start = 1
- middle = width - len(input) + 1
else:
start = 0
- middle = width - len(input)
- for i in range(start, middle):
- buf[i] = '0'
-
- for i in range(middle, width):
- buf[i] = input[start]
- start = start + 1
-
- return space.wrap("".join(buf))
+ builder.append_multiple_char('0', num_zeros)
+ builder.append_slice(input, start, len(input))
+ return space.wrap(builder.build())
def hash__String(space, w_str):
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -10,7 +10,8 @@
from pypy.objspace.std import identitydict
from pypy.rlib.objectmodel import we_are_translated
from pypy.rlib.objectmodel import current_object_addr_as_int, compute_hash
-from pypy.rlib.jit import promote, elidable_promote, we_are_jitted
+from pypy.rlib.jit import promote, elidable_promote, we_are_jitted,\
+ promote_string
from pypy.rlib.jit import elidable, dont_look_inside, unroll_safe
from pypy.rlib.rarithmetic import intmask, r_uint
@@ -101,6 +102,7 @@
'instancetypedef',
'terminator',
'_version_tag?',
+ 'interplevel_cls',
]
# for config.objspace.std.getattributeshortcut
@@ -399,6 +401,7 @@
if version_tag is None:
tup = w_self._lookup_where(name)
return tup
+ name = promote_string(name)
w_class, w_value = w_self._pure_lookup_where_with_method_cache(name, version_tag)
return w_class, unwrap_cell(space, w_value)
diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py
--- a/pypy/objspace/std/unicodeobject.py
+++ b/pypy/objspace/std/unicodeobject.py
@@ -317,54 +317,54 @@
input = w_self._value
if len(input) == 0:
return W_UnicodeObject.EMPTY
- result = [u'\0'] * len(input)
- result[0] = unichr(unicodedb.toupper(ord(input[0])))
+ builder = UnicodeBuilder(len(input))
+ builder.append(unichr(unicodedb.toupper(ord(input[0]))))
for i in range(1, len(input)):
- result[i] = unichr(unicodedb.tolower(ord(input[i])))
- return W_UnicodeObject(u''.join(result))
+ builder.append(unichr(unicodedb.tolower(ord(input[i]))))
+ return W_UnicodeObject(builder.build())
def unicode_title__Unicode(space, w_self):
input = w_self._value
if len(input) == 0:
return w_self
- result = [u'\0'] * len(input)
+ builder = UnicodeBuilder(len(input))
previous_is_cased = False
for i in range(len(input)):
unichar = ord(input[i])
if previous_is_cased:
- result[i] = unichr(unicodedb.tolower(unichar))
+ builder.append(unichr(unicodedb.tolower(unichar)))
else:
- result[i] = unichr(unicodedb.totitle(unichar))
+ builder.append(unichr(unicodedb.totitle(unichar)))
previous_is_cased = unicodedb.iscased(unichar)
- return W_UnicodeObject(u''.join(result))
+ return W_UnicodeObject(builder.build())
def unicode_lower__Unicode(space, w_self):
input = w_self._value
- result = [u'\0'] * len(input)
+ builder = UnicodeBuilder(len(input))
for i in range(len(input)):
- result[i] = unichr(unicodedb.tolower(ord(input[i])))
- return W_UnicodeObject(u''.join(result))
+ builder.append(unichr(unicodedb.tolower(ord(input[i]))))
+ return W_UnicodeObject(builder.build())
def unicode_upper__Unicode(space, w_self):
input = w_self._value
- result = [u'\0'] * len(input)
+ builder = UnicodeBuilder(len(input))
for i in range(len(input)):
- result[i] = unichr(unicodedb.toupper(ord(input[i])))
- return W_UnicodeObject(u''.join(result))
+ builder.append(unichr(unicodedb.toupper(ord(input[i]))))
+ return W_UnicodeObject(builder.build())
def unicode_swapcase__Unicode(space, w_self):
input = w_self._value
- result = [u'\0'] * len(input)
+ builder = UnicodeBuilder(len(input))
for i in range(len(input)):
unichar = ord(input[i])
if unicodedb.islower(unichar):
- result[i] = unichr(unicodedb.toupper(unichar))
+ builder.append(unichr(unicodedb.toupper(unichar)))
elif unicodedb.isupper(unichar):
- result[i] = unichr(unicodedb.tolower(unichar))
+ builder.append(unichr(unicodedb.tolower(unichar)))
else:
- result[i] = input[i]
- return W_UnicodeObject(u''.join(result))
+ builder.append(input[i])
+ return W_UnicodeObject(builder.build())
def _normalize_index(length, index):
if index < 0:
diff --git a/pypy/objspace/taint.py b/pypy/objspace/taint.py
deleted file mode 100644
--- a/pypy/objspace/taint.py
+++ /dev/null
@@ -1,294 +0,0 @@
-"""
-Just an experiment.
-"""
-import os
-from pypy.objspace.std.objspace import StdObjSpace
-from pypy.objspace.proxy import patch_space_in_place
-from pypy.objspace.thunk import nb_forcing_args
-from pypy.interpreter.error import OperationError
-from pypy.interpreter import baseobjspace, gateway, executioncontext
-from pypy.interpreter.function import Method
-from pypy.interpreter.pyframe import PyFrame
-from pypy.tool.sourcetools import func_with_new_name
-from pypy.rlib.unroll import unrolling_iterable
-
-
-class W_Tainted(baseobjspace.W_Root):
- def __init__(self, w_obj):
- self.w_obj = w_obj
-
-## def getdict(self, space):
-## return taint(self.w_obj.getdict(space))
-
-## def getdictvalue(self, space, attr):
-## return taint(self.w_obj.getdictvalue(space, attr))
-
-## def setdictvalue(self, space, attr, w_value):
-## return self.w_obj.setdictvalue(space, attr, w_value)
-
-## ...
-
-class W_TaintBomb(baseobjspace.W_Root):
- filename = '?'
- codename = '?'
- codeline = 0
-
- def __init__(self, space, operr):
- self.space = space
- self.operr = operr
- self.record_debug_info()
-
- def record_debug_info(self):
- ec = self.space.getexecutioncontext()
- frame = ec.gettopframe_nohidden()
- if isinstance(frame, PyFrame): # and, in particular, frame != None
- self.filename = frame.pycode.co_filename
- self.codename = frame.pycode.co_name
- self.codeline = frame.get_last_lineno()
- if get_debug_level(self.space) > 0:
- self.debug_dump()
-
- def debug_dump(self):
- os.write(2, 'Taint Bomb from file "%s", line %d, in %s\n %s\n' % (
- self.filename, self.codeline, self.codename,
- self.operr.errorstr(self.space)))
-
- def explode(self):
- #msg = self.operr.errorstr(space)
- raise OperationError(self.space.w_TaintError, self.space.w_None)
-
-
-def taint(w_obj):
- """Return a tainted version of the argument."""
- if w_obj is None or isinstance(w_obj, W_Tainted):
- return w_obj
- else:
- return W_Tainted(w_obj)
-app_taint = gateway.interp2app(taint)
-
-def is_tainted(space, w_obj):
- """Return whether the argument is tainted."""
- res = isinstance(w_obj, W_Tainted) or isinstance(w_obj, W_TaintBomb)
- return space.wrap(res)
-app_is_tainted = gateway.interp2app(is_tainted)
-
-def untaint(space, w_expectedtype, w_obj):
- """untaint(expectedtype, tainted_obj) -> obj
-Untaint untainted_obj and return it. If the result is not of expectedtype,
-raise a type error."""
- if (isinstance(w_expectedtype, W_Tainted) or
- isinstance(w_expectedtype, W_TaintBomb)):
- raise OperationError(space.w_TypeError,
- space.wrap("untaint() arg 1 must be an untainted type"))
- if not space.is_true(space.isinstance(w_expectedtype, space.w_type)):
- raise OperationError(space.w_TypeError,
- space.wrap("untaint() arg 1 must be a type"))
- if isinstance(w_obj, W_Tainted):
- w_obj = w_obj.w_obj
- elif isinstance(w_obj, W_TaintBomb):
- w_obj.explode()
- #if isinstance(w_expectedtype, W_Tainted):
- # w_expectedtype = w_expectedtype.w_obj
- w_realtype = space.type(w_obj)
- if not space.is_w(w_realtype, w_expectedtype):
- #msg = "expected an object of type '%s'" % (
- # w_expectedtype.getname(space),)
- # #w_realtype.getname(space))
- raise OperationError(space.w_TaintError, space.w_None)
- return w_obj
-app_untaint = gateway.interp2app(untaint)
-
-# ____________________________________________________________
-
- at gateway.unwrap_spec(args_w='args_w')
-def taint_atomic_function(space, w_func, args_w):
- newargs_w = []
- tainted = False
- for w_arg in args_w:
- if isinstance(w_arg, W_Tainted):
- tainted = True
- w_arg = w_arg.w_obj
- elif isinstance(w_arg, W_TaintBomb):
- return w_arg
- newargs_w.append(w_arg)
- w_newargs = space.newtuple(newargs_w)
- try:
- w_res = space.call(w_func, w_newargs)
- except OperationError, operr:
- if not tainted:
- raise
- return W_TaintBomb(space, operr)
- if tainted:
- w_res = taint(w_res)
- return w_res
-
-app_taint_atomic_function = gateway.interp2app(taint_atomic_function)
-
-def taint_atomic(space, w_callable):
- """decorator to make a callable "taint-atomic": if the function is called
-with tainted arguments, those are untainted. The result of the function is
-tainted again. All exceptions that the callable raises are turned into
-taint bombs."""
- meth = Method(space, space.w_fn_taint_atomic_function,
- w_callable, space.type(w_callable))
- return space.wrap(meth)
-app_taint_atomic = gateway.interp2app(taint_atomic)
-
-# ____________________________________________________________
-
-executioncontext.ExecutionContext.taint_debug = 0
-
- at gateway.unwrap_spec(level=int)
-def taint_debug(space, level):
- """Set the debug level. If the debug level is greater than 0, the creation
-of taint bombs will print debug information. For debugging purposes
-only!"""
- space.getexecutioncontext().taint_debug = level
-app_taint_debug = gateway.interp2app(taint_debug)
-
-def taint_look(space, w_obj):
- """Print some info about the taintedness of an object. For debugging
-purposes only!"""
- if isinstance(w_obj, W_Tainted):
- info = space.type(w_obj.w_obj).getname(space)
- msg = space.str_w(w_obj.w_obj.getrepr(space, info))
- msg = 'Taint Box %s\n' % msg
- os.write(2, msg)
- elif isinstance(w_obj, W_TaintBomb):
- w_obj.debug_dump()
- else:
- os.write(2, 'not tainted\n')
-app_taint_look = gateway.interp2app(taint_look)
-
-def get_debug_level(space):
- return space.getexecutioncontext().taint_debug
-
-def debug_bomb(space, operr):
- ec = space.getexecutioncontext()
- filename = '?'
- codename = '?'
- codeline = 0
- frame = ec.gettopframe_nohidden()
- if isinstance(frame, PyFrame): # and, in particular, frame != None
- filename = frame.pycode.co_filename
- codename = frame.pycode.co_name
- codeline = frame.get_last_lineno()
- os.write(2, 'Taint Bomb in file "%s", line %d, in %s\n %s\n' % (
- filename, codeline, codename, operr.errorstr(space)))
-
-# ____________________________________________________________
-
-
-class TaintSpace(StdObjSpace):
-
- def __init__(self, *args, **kwds):
- StdObjSpace.__init__(self, *args, **kwds)
- w_dict = self.newdict()
- self.setitem(w_dict, self.wrap("__doc__"), self.wrap("""\
-Exception that is raised when an operation revealing information on a tainted
-object is performed."""))
- self.w_TaintError = self.call_function(
- self.w_type,
- self.wrap("TaintError"),
- self.newtuple([self.w_Exception]),
- w_dict
- )
- w___pypy__ = self.getbuiltinmodule("__pypy__")
- self.setattr(w___pypy__, self.wrap('taint'),
- self.wrap(app_taint))
- self.setattr(w___pypy__, self.wrap('is_tainted'),
- self.wrap(app_is_tainted))
- self.setattr(w___pypy__, self.wrap('untaint'),
- self.wrap(app_untaint))
- self.w_fn_taint_atomic_function = self.wrap(app_taint_atomic_function)
- self.setattr(w___pypy__, self.wrap('taint_atomic'),
- self.wrap(app_taint_atomic))
- self.setattr(w___pypy__, self.wrap('TaintError'),
- self.w_TaintError)
- self.setattr(w___pypy__, self.wrap('_taint_debug'),
- self.wrap(app_taint_debug))
- self.setattr(w___pypy__, self.wrap('_taint_look'),
- self.wrap(app_taint_look))
- patch_space_in_place(self, 'taint', proxymaker)
-
- # XXX may leak info, perfomance hit, what about taint bombs?
- from pypy.objspace.std.typeobject import W_TypeObject
-
- def taint_lookup(w_obj, name):
- if isinstance(w_obj, W_Tainted):
- w_obj = w_obj.w_obj
- w_type = self.type(w_obj)
- assert isinstance(w_type, W_TypeObject)
- return w_type.lookup(name)
-
- def taint_lookup_in_type_where(w_obj, name):
- if isinstance(w_obj, W_Tainted):
- w_type = w_obj.w_obj
- else:
- w_type = w_obj
- assert isinstance(w_type, W_TypeObject)
- return w_type.lookup_where(name)
-
- self.lookup = taint_lookup
- self.lookup_in_type_where = taint_lookup_in_type_where
-
-
-Space = TaintSpace
-
-
-def tainted_error(space, name):
- #msg = "operation '%s' forbidden on tainted object" % (name,)
- raise OperationError(space.w_TaintError, space.w_None)# space.wrap(msg))
-
-
-RegularMethods = dict.fromkeys(
- [name for name, _, _, _ in baseobjspace.ObjSpace.MethodTable])
-
-TaintResultIrregularMethods = dict.fromkeys(
- ['wrap', 'call_args'] +
- [name for name in baseobjspace.ObjSpace.IrregularOpTable
- if name.startswith('new')])
-
-def proxymaker(space, name, parentfn):
- arity = nb_forcing_args[name]
- indices = unrolling_iterable(range(arity))
- if name in RegularMethods:
-
- def proxy(*args_w):
- newargs_w = ()
- tainted = False
- for i in indices:
- w_arg = args_w[i]
- if isinstance(w_arg, W_Tainted):
- tainted = True
- w_arg = w_arg.w_obj
- elif isinstance(w_arg, W_TaintBomb):
- return w_arg
- newargs_w += (w_arg,)
- newargs_w += args_w[arity:]
- try:
- w_res = parentfn(*newargs_w)
- except OperationError, operr:
- if not tainted:
- raise
- return W_TaintBomb(space, operr)
- if tainted:
- w_res = taint(w_res)
- return w_res
-
- elif arity == 0:
- return None
-
- else:
-
- def proxy(*args_w):
- for i in indices:
- w_arg = args_w[i]
- if isinstance(w_arg, W_Tainted):
- tainted_error(space, name)
- elif isinstance(w_arg, W_TaintBomb):
- w_arg.explode()
- return parentfn(*args_w)
-
- proxy = func_with_new_name(proxy, '%s_proxy' % name)
- return proxy
diff --git a/pypy/objspace/test/test_taintobjspace.py b/pypy/objspace/test/test_taintobjspace.py
deleted file mode 100644
--- a/pypy/objspace/test/test_taintobjspace.py
+++ /dev/null
@@ -1,77 +0,0 @@
-from pypy.conftest import gettestobjspace
-
-class AppTest_Taint:
-
- def setup_class(cls):
- cls.space = gettestobjspace('taint')
-
- def test_simple(self):
- from __pypy__ import taint, untaint, TaintError
- x = taint(6)
- x = x * 7
- raises(TaintError, "if x: y = 1")
- t = type(x)
- raises(TaintError, "if t is int: y = 1")
- assert untaint(int, x) == 42
- raises(TaintError, "untaint(float, x)")
-
- def test_bomb(self):
- from __pypy__ import taint, untaint, TaintError
- x = taint(6)
- x = x / 0
- raises(TaintError, "if x: y = 1")
- t = type(x)
- raises(TaintError, "if t is int: y = 1")
- raises(TaintError, "untaint(int, x)")
- raises(TaintError, "untaint(float, x)")
-
- def test_taint_atomic(self):
- from __pypy__ import taint, untaint, TaintError, taint_atomic
- x = taint(6)
- x *= 7
-
- def dummy(x):
- if x > 40:
- return 5
- else:
- return 3
- dummy = taint_atomic(dummy)
-
- y = dummy(x)
- raises(TaintError, "if y == 3: z = 1")
- assert untaint(int, y) == 5
-
- def test_taint_atomic_exception(self):
- from __pypy__ import taint, untaint, TaintError, taint_atomic
- x = taint(6)
- x *= 7
-
- def dummy(x):
- if x + "world" == "hello world":
- return 5
- else:
- return 3
- dummy = taint_atomic(dummy)
-
- y = dummy(x)
- raises(TaintError, "if y == 3: z = 1")
- raises(TaintError, "untaint(int, y)")
-
- def test_taint_atomic_incoming_bomb(self):
- from __pypy__ import taint, untaint, TaintError, taint_atomic
- x = taint(6)
- x /= 0
- lst = []
-
- def dummy(x):
- lst.append("running!")
- if x > 40:
- return 5
- else:
- return 3
- dummy = taint_atomic(dummy)
-
- y = dummy(x)
- raises(TaintError, "if y == 3: z = 1")
- assert lst == []
- raises(TaintError, "untaint(int, y)")
diff --git a/pypy/rlib/_jit_vref.py b/pypy/rlib/_jit_vref.py
--- a/pypy/rlib/_jit_vref.py
+++ b/pypy/rlib/_jit_vref.py
@@ -25,6 +25,10 @@
def simple_call(self):
return self.s_instance
+ def getattr(self, s_attr):
+ assert s_attr.const == 'virtual'
+ return annmodel.s_Bool
+
def rtyper_makerepr(self, rtyper):
if rtyper.type_system.name == 'lltypesystem':
return vrefrepr
@@ -61,6 +65,13 @@
" prebuilt virtual_ref")
return lltype.nullptr(OBJECTPTR.TO)
+ def rtype_getattr(self, hop):
+ s_attr = hop.args_s[1]
+ assert s_attr.const == 'virtual'
+ v = hop.inputarg(self, arg=0)
+ hop.exception_cannot_occur()
+ return hop.genop('jit_is_virtual', [v], resulttype = lltype.Bool)
+
from pypy.rpython.ootypesystem.rclass import OBJECT
class OOVRefRepr(VRefRepr):
diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py
--- a/pypy/rlib/jit.py
+++ b/pypy/rlib/jit.py
@@ -38,6 +38,7 @@
possible arguments are:
* promote - promote the argument from a variable into a constant
+ * promote_string - same, but promote string by *value*
* access_directly - directly access a virtualizable, as a structure
and don't treat it as a virtualizable
* fresh_virtualizable - means that virtualizable was just allocated.
@@ -51,6 +52,9 @@
def promote(x):
return hint(x, promote=True)
+def promote_string(x):
+ return hint(x, promote_string=True)
+
def dont_look_inside(func):
""" Make sure the JIT does not trace inside decorated function
(it becomes a call instead)
@@ -313,6 +317,12 @@
raise InvalidVirtualRef
return self._x
+ @property
+ def virtual(self):
+ """A property that is True if the vref contains a virtual that would
+ be forced by the '()' operator."""
+ return self._state == 'non-forced'
+
def _finish(self):
if self._state == 'non-forced':
self._state = 'invalid'
diff --git a/pypy/rlib/rerased.py b/pypy/rlib/rerased.py
--- a/pypy/rlib/rerased.py
+++ b/pypy/rlib/rerased.py
@@ -218,15 +218,13 @@
[v_value] = hop.inputargs(lltype.Signed)
c_one = hop.inputconst(lltype.Signed, 1)
hop.exception_is_here()
- v2 = hop.genop('int_lshift_ovf', [v_value, c_one],
+ v2 = hop.genop('int_add_ovf', [v_value, v_value],
resulttype = lltype.Signed)
v2p1 = hop.genop('int_add', [v2, c_one],
resulttype = lltype.Signed)
v_instance = hop.genop('cast_int_to_ptr', [v2p1],
resulttype=self.lowleveltype)
- v = hop.genop('cast_opaque_ptr', [v_instance],
- resulttype=self.lowleveltype)
- return v
+ return v_instance
def convert_const(self, value):
if value._identity is _identity_for_ints:
@@ -266,10 +264,10 @@
return hop.genop('int_rshift', [v2, c_one], resulttype=lltype.Signed)
def rtype_erase_int(self, hop):
- hop.exception_is_here()
[v_value] = hop.inputargs(lltype.Signed)
c_one = hop.inputconst(lltype.Signed, 1)
- v2 = hop.genop('int_lshift_ovf', [v_value, c_one],
+ hop.exception_is_here()
+ v2 = hop.genop('int_add_ovf', [v_value, v_value],
resulttype = lltype.Signed)
v2p1 = hop.genop('int_add', [v2, c_one],
resulttype = lltype.Signed)
diff --git a/pypy/rlib/test/test__jit_vref.py b/pypy/rlib/test/test__jit_vref.py
--- a/pypy/rlib/test/test__jit_vref.py
+++ b/pypy/rlib/test/test__jit_vref.py
@@ -27,10 +27,13 @@
x1 = X()
vref = virtual_ref(x1)
assert vref._state == 'non-forced'
+ assert vref.virtual is True
assert vref() is x1
assert vref._state == 'forced'
+ assert vref.virtual is False
virtual_ref_finish(vref, x1)
assert vref._state == 'forced'
+ assert vref.virtual is False
assert vref() is x1
def test_direct_invalid():
@@ -135,6 +138,13 @@
x = self.interpret(f, [])
assert x == 42
+ def test_rtype_virtualattr(self):
+ def f():
+ vref = virtual_ref(X())
+ return vref.virtual
+ x = self.interpret(f, [])
+ assert x is False
+
class TestLLtype(BaseTestVRef, LLRtypeMixin):
OBJECTTYPE = OBJECTPTR
diff --git a/pypy/rlib/test/test_jit.py b/pypy/rlib/test/test_jit.py
--- a/pypy/rlib/test/test_jit.py
+++ b/pypy/rlib/test/test_jit.py
@@ -139,12 +139,11 @@
def test_isconstant(self):
def f(n):
- assert n >= 0
assert isconstant(n) is False
l = []
l.append(n)
return len(l)
- res = self.interpret(f, [234])
+ res = self.interpret(f, [-234])
assert res == 1
diff --git a/pypy/rpython/lltypesystem/ll2ctypes.py b/pypy/rpython/lltypesystem/ll2ctypes.py
--- a/pypy/rpython/lltypesystem/ll2ctypes.py
+++ b/pypy/rpython/lltypesystem/ll2ctypes.py
@@ -658,6 +658,8 @@
if T == llmemory.GCREF:
if isinstance(llobj._obj, _llgcopaque):
return ctypes.c_void_p(llobj._obj.intval)
+ if isinstance(llobj._obj, int): # tagged pointer
+ return ctypes.c_void_p(llobj._obj)
container = llobj._obj.container
T = lltype.Ptr(lltype.typeOf(container))
# otherwise it came from integer and we want a c_void_p with
@@ -1268,6 +1270,7 @@
class _llgcopaque(lltype._container):
_TYPE = llmemory.GCREF.TO
_name = "_llgcopaque"
+ _read_directly_intval = True # for _ptr._cast_to_int()
def __init__(self, void_p):
if isinstance(void_p, (int, long)):
@@ -1299,11 +1302,6 @@
return _opaque_objs[self.intval // 2]
return force_cast(PTRTYPE, self.intval)
-## def _cast_to_int(self):
-## return self.intval
-
-## def _cast_to_adr(self):
-## return _lladdress(self.intval)
def cast_adr_to_int(addr):
if isinstance(addr, llmemory.fakeaddress):
diff --git a/pypy/rpython/lltypesystem/llmemory.py b/pypy/rpython/lltypesystem/llmemory.py
--- a/pypy/rpython/lltypesystem/llmemory.py
+++ b/pypy/rpython/lltypesystem/llmemory.py
@@ -498,6 +498,8 @@
def _cast_to_int(self, symbolic=False):
if self:
+ if isinstance(self.ptr._obj0, int): # tagged integer
+ return self.ptr._obj0
if symbolic:
return AddressAsInt(self)
else:
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
@@ -428,6 +428,7 @@
'jit_marker': LLOp(),
'jit_force_virtualizable':LLOp(canrun=True),
'jit_force_virtual': LLOp(canrun=True),
+ 'jit_is_virtual': LLOp(canrun=True),
'jit_force_quasi_immutable': LLOp(canrun=True),
'get_exception_addr': LLOp(),
'get_exc_value_addr': LLOp(),
diff --git a/pypy/rpython/lltypesystem/lltype.py b/pypy/rpython/lltypesystem/lltype.py
--- a/pypy/rpython/lltypesystem/lltype.py
+++ b/pypy/rpython/lltypesystem/lltype.py
@@ -1360,6 +1360,8 @@
obj = normalizeptr(self, check)._getobj(check)
if isinstance(obj, int):
return obj # special case for cast_int_to_ptr() results put into opaques
+ if getattr(obj, '_read_directly_intval', False):
+ return obj.intval # special case for _llgcopaque
result = intmask(obj._getid())
# assume that id() returns an addressish value which is
# not zero and aligned to at least a multiple of 4
diff --git a/pypy/rpython/lltypesystem/opimpl.py b/pypy/rpython/lltypesystem/opimpl.py
--- a/pypy/rpython/lltypesystem/opimpl.py
+++ b/pypy/rpython/lltypesystem/opimpl.py
@@ -538,6 +538,9 @@
def op_jit_force_virtual(x):
return x
+def op_jit_is_virtual(x):
+ return False
+
def op_jit_force_quasi_immutable(*args):
pass
diff --git a/pypy/rpython/lltypesystem/rtagged.py b/pypy/rpython/lltypesystem/rtagged.py
--- a/pypy/rpython/lltypesystem/rtagged.py
+++ b/pypy/rpython/lltypesystem/rtagged.py
@@ -43,7 +43,7 @@
v_value = hop.inputarg(lltype.Signed, arg=1)
c_one = hop.inputconst(lltype.Signed, 1)
hop.exception_is_here()
- v2 = hop.genop('int_lshift_ovf', [v_value, c_one],
+ v2 = hop.genop('int_add_ovf', [v_value, v_value],
resulttype = lltype.Signed)
v2p1 = hop.genop('int_add', [v2, c_one],
resulttype = lltype.Signed)
diff --git a/pypy/rpython/lltypesystem/test/test_ll2ctypes.py b/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
--- a/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
+++ b/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
@@ -1123,6 +1123,9 @@
#assert lltype.cast_ptr_to_int(ref1) == intval
+ x = rffi.cast(llmemory.GCREF, -17)
+ assert lltype.cast_ptr_to_int(x) == -17
+
def test_ptr_truth(self):
abc = rffi.cast(lltype.Ptr(lltype.FuncType([], lltype.Void)), 0)
assert not abc
diff --git a/pypy/rpython/rclass.py b/pypy/rpython/rclass.py
--- a/pypy/rpython/rclass.py
+++ b/pypy/rpython/rclass.py
@@ -191,6 +191,10 @@
"class %r inherits from its parent _immutable_=True, "
"so it should also declare _immutable_=True" % (
self.classdef,))
+ if loc.classdict.get('_immutable_').value is not True:
+ raise TyperError(
+ "class %r: _immutable_ = something else than True" % (
+ self.classdef,))
hints = hints.copy()
hints['immutable'] = True
self.immutable_field_set = set() # unless overwritten below
diff --git a/pypy/tool/logparser.py b/pypy/tool/logparser.py
--- a/pypy/tool/logparser.py
+++ b/pypy/tool/logparser.py
@@ -298,6 +298,8 @@
image.paste(textpercent, (t1x, 5), textpercent)
image.paste(textlabel, (t2x, 5), textlabel)
images.append(image)
+ if not images:
+ return None
return combine(images, spacing=0, border=1, horizontal=False)
def get_timesummary_single_image(totaltimes, totaltime0, componentdict,
@@ -333,6 +335,8 @@
del totaltimes[None]
img2 = render_histogram(totaltimes, totaltime0, {},
width, summarybarheight)
+ if img2 is None:
+ return img1
return combine([img1, img2], spacing=spacing, horizontal=True)
# ----------
diff --git a/pypy/translator/c/funcgen.py b/pypy/translator/c/funcgen.py
--- a/pypy/translator/c/funcgen.py
+++ b/pypy/translator/c/funcgen.py
@@ -833,7 +833,7 @@
return 'INSTRUMENT_COUNT(%s);' % counter_label
def OP_IS_EARLY_CONSTANT(self, op):
- return self.expr(op.result) + ' = 0;' # Allways false
+ return '%s = 0; /* IS_EARLY_CONSTANT */' % (self.expr(op.result),)
def OP_JIT_MARKER(self, op):
return '/* JIT_MARKER %s */' % op
@@ -845,6 +845,9 @@
return '%s = %s; /* JIT_FORCE_VIRTUAL */' % (self.expr(op.result),
self.expr(op.args[0]))
+ def OP_JIT_IS_VIRTUAL(self, op):
+ return '%s = 0; /* JIT_IS_VIRTUAL */' % (self.expr(op.result),)
+
def OP_JIT_FORCE_QUASI_IMMUTABLE(self, op):
return '/* JIT_FORCE_QUASI_IMMUTABLE %s */' % op
diff --git a/pypy/translator/c/src/thread_nt.h b/pypy/translator/c/src/thread_nt.h
--- a/pypy/translator/c/src/thread_nt.h
+++ b/pypy/translator/c/src/thread_nt.h
@@ -245,7 +245,7 @@
if (pending_acquires <= 0)
return 0;
InterlockedIncrement(&pending_acquires);
- PulseEvent(&cond_gil);
+ PulseEvent(cond_gil);
/* hack: the three following lines do a pthread_cond_wait(), and
normally specifying a timeout of INFINITE would be fine. But the
@@ -253,7 +253,7 @@
(small) risk that PulseEvent misses the WaitForSingleObject().
In this case the process will just sleep a few milliseconds. */
LeaveCriticalSection(&mutex_gil);
- WaitForSingleObject(&cond_gil, 15);
+ WaitForSingleObject(cond_gil, 15);
EnterCriticalSection(&mutex_gil);
InterlockedDecrement(&pending_acquires);
@@ -263,7 +263,7 @@
void RPyGilRelease(void)
{
LeaveCriticalSection(&mutex_gil);
- PulseEvent(&cond_gil);
+ PulseEvent(cond_gil);
}
void RPyGilAcquire(void)
diff --git a/pypy/translator/platform/linux.py b/pypy/translator/platform/linux.py
--- a/pypy/translator/platform/linux.py
+++ b/pypy/translator/platform/linux.py
@@ -1,5 +1,6 @@
"""Support for Linux."""
+import sys
from pypy.translator.platform.posix import BasePosix
class BaseLinux(BasePosix):
@@ -24,15 +25,18 @@
return self._pkg_config("libffi", "--libs-only-L",
['/usr/lib/libffi'])
+ def library_dirs_for_libffi_a(self):
+ # places where we need to look for libffi.a
+ # XXX obscuuure! only look for libffi.a if run with translate.py
+ if 'translate' in sys.modules:
+ return self.library_dirs_for_libffi() + ['/usr/lib']
+ else:
+ return []
+
class Linux(BaseLinux):
shared_only = () # it seems that on 32-bit linux, compiling with -fPIC
# gives assembler that asmgcc is not happy about.
- def library_dirs_for_libffi_a(self):
- # places where we need to look for libffi.a
- return self.library_dirs_for_libffi() + ['/usr/lib']
-
-
class Linux64(BaseLinux):
pass
More information about the pypy-commit
mailing list