Author: Ronan Lamy <[email protected]>
Branch: py3.5
Changeset: r92100:45907003511b
Date: 2017-08-06 15:37 +0100
http://bitbucket.org/pypy/pypy/changeset/45907003511b/
Log: hg merge default
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -60,3 +60,16 @@
Small improvement to optimize list accesses with constant indexes better by
throwing away information about them less eagerly.
+
+
+.. branch: getarrayitem-into-bridges:
+
+More information is retained into a bridge: knowledge about the content of
+arrays (at fixed indices) is stored in guards (and thus available at the
+beginning of bridges). Also, some better feeding of information about known
+fields of constant objects into bridges.
+
+.. branch: cpyext-leakchecking
+
+Add support for leakfinder in cpyext tests (disabled for now, due to too many
+failures).
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -1,4 +1,5 @@
import sys
+import py
from rpython.rlib.cache import Cache
from rpython.tool.uid import HUGEVAL_BYTES
@@ -1271,8 +1272,22 @@
self.setitem(w_globals, w_key, self.builtin)
return statement.exec_code(self, w_globals, w_locals)
+ @not_rpython
+ def appdef(self, source):
+ '''Create interp-level function object from app-level source.
+
+ The source should be in the same format as for space.appexec():
+ """(foo, bar): return 'baz'"""
+ '''
+ source = source.lstrip()
+ assert source.startswith('('), "incorrect header in:\n%s" % (source,)
+ source = py.code.Source("def anonymous%s\n" % source)
+ w_glob = self.newdict(module=True)
+ self.exec_(str(source), w_glob, w_glob)
+ return self.getitem(w_glob, self.newtext('anonymous'))
+
@specialize.arg(2)
- def appexec(self, posargs_w, source):
+ def appexec(self, posargs_w, source, cache=True):
""" return value from executing given source at applevel.
The source must look like
'''(x, y):
@@ -1280,7 +1295,11 @@
return result
'''
"""
- w_func = self.fromcache(AppExecCache).getorbuild(source)
+ if cache:
+ w_func = self.fromcache(AppExecCache).getorbuild(source)
+ else:
+ # NB: since appdef() is not-RPython, using cache=False also is.
+ w_func = self.appdef(source)
args = Arguments(self, list(posargs_w))
return self.call_args(w_func, args)
@@ -1817,15 +1836,7 @@
class AppExecCache(SpaceCache):
@not_rpython
def build(cache, source):
- space = cache.space
- # XXX will change once we have our own compiler
- import py
- source = source.lstrip()
- assert source.startswith('('), "incorrect header in:\n%s" % (source,)
- source = py.code.Source("def anonymous%s\n" % source)
- w_glob = space.newdict(module=True)
- space.exec_(str(source), w_glob, w_glob)
- return space.getitem(w_glob, space.newtext('anonymous'))
+ return cache.space.appdef(source)
# Table describing the regular part of the interface of object spaces,
diff --git a/pypy/module/cpyext/buffer.py b/pypy/module/cpyext/buffer.py
--- a/pypy/module/cpyext/buffer.py
+++ b/pypy/module/cpyext/buffer.py
@@ -84,22 +84,27 @@
if self.needs_decref:
if self.releasebufferproc:
func_target = rffi.cast(releasebufferproc,
self.releasebufferproc)
- with lltype.scoped_alloc(Py_buffer) as pybuf:
- pybuf.c_buf = self.ptr
- pybuf.c_len = self.size
- pybuf.c_ndim = cts.cast('int', self.ndim)
- pybuf.c_shape = cts.cast('Py_ssize_t*', pybuf.c__shape)
- pybuf.c_strides = cts.cast('Py_ssize_t*',
pybuf.c__strides)
- for i in range(self.ndim):
- pybuf.c_shape[i] = self.shape[i]
- pybuf.c_strides[i] = self.strides[i]
- if self.format:
- pybuf.c_format = rffi.str2charp(self.format)
- else:
- pybuf.c_format = rffi.str2charp("B")
+ size = rffi.sizeof(cts.gettype('Py_buffer'))
+ pybuf = lltype.malloc(rffi.VOIDP.TO, size, flavor='raw',
zero=True)
+ pybuf = cts.cast('Py_buffer*', pybuf)
+ pybuf.c_buf = self.ptr
+ pybuf.c_len = self.size
+ pybuf.c_ndim = cts.cast('int', self.ndim)
+ pybuf.c_shape = cts.cast('Py_ssize_t*', pybuf.c__shape)
+ pybuf.c_strides = cts.cast('Py_ssize_t*', pybuf.c__strides)
+ for i in range(self.ndim):
+ pybuf.c_shape[i] = self.shape[i]
+ pybuf.c_strides[i] = self.strides[i]
+ fmt = rffi.str2charp(self.format if self.format else "B")
+ try:
+ pybuf.c_format = fmt
generic_cpy_call(self.space, func_target, self.pyobj,
pybuf)
+ finally:
+ lltype.free(fmt, flavor='raw')
+ lltype.free(pybuf, flavor='raw')
decref(self.space, self.pyobj)
self.pyobj = lltype.nullptr(PyObject.TO)
+ self.w_obj = None
else:
#do not call twice
return
@@ -211,6 +216,7 @@
view.c_suboffsets = lltype.nullptr(Py_ssize_tP.TO)
view.c_internal = lltype.nullptr(rffi.VOIDP.TO)
+DEFAULT_FMT = rffi.str2charp("B")
@cpython_api([lltype.Ptr(Py_buffer), PyObject, rffi.VOIDP, Py_ssize_t,
lltype.Signed, lltype.Signed], rffi.INT, error=-1)
@@ -233,7 +239,8 @@
rffi.setintfield(view, 'c_ndim', 1)
view.c_format = lltype.nullptr(rffi.CCHARP.TO)
if (flags & PyBUF_FORMAT) == PyBUF_FORMAT:
- view.c_format = rffi.str2charp("B")
+ # NB: this needs to be a static string, because nothing frees it
+ view.c_format = DEFAULT_FMT
view.c_shape = lltype.nullptr(Py_ssize_tP.TO)
if (flags & PyBUF_ND) == PyBUF_ND:
view.c_shape = rffi.cast(Py_ssize_tP, view.c__shape)
diff --git a/pypy/module/cpyext/memoryobject.py
b/pypy/module/cpyext/memoryobject.py
--- a/pypy/module/cpyext/memoryobject.py
+++ b/pypy/module/cpyext/memoryobject.py
@@ -73,7 +73,7 @@
readonly=widen(view.c_readonly))
# Ensure view.c_buf is released upon object finalization
fq.register_finalizer(buf)
- # Allow subclassing W_MemeoryView
+ # Allow subclassing W_MemoryView
w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type))
w_obj = space.allocate_instance(W_MemoryView, w_type)
w_obj.__init__(buf)
@@ -178,11 +178,9 @@
return (_IsCContiguous(view) or _IsFortranContiguous(view))
return 0
-@cpython_api([PyObject], PyObject, result_is_ll=True)
+@cpython_api([PyObject], PyObject)
def PyMemoryView_FromObject(space, w_obj):
- w_memview = space.call_method(space.builtin, "memoryview", w_obj)
- py_memview = make_ref(space, w_memview, w_obj)
- return py_memview
+ return space.call_method(space.builtin, "memoryview", w_obj)
@cts.decl("""PyObject *
PyMemoryView_FromMemory(char *mem, Py_ssize_t size, int flags)""")
@@ -207,6 +205,7 @@
# copy view into obj.c_view, without creating a new view.c_obj
typedescr = get_typedescr(W_MemoryView.typedef)
py_obj = typedescr.allocate(space, space.w_memoryview)
+
py_mem = rffi.cast(PyMemoryViewObject, py_obj)
mview = py_mem.c_view
mview.c_buf = view.c_buf
diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py
--- a/pypy/module/cpyext/pyobject.py
+++ b/pypy/module/cpyext/pyobject.py
@@ -29,7 +29,7 @@
from pypy.module.cpyext.typeobject import subtype_dealloc
return subtype_dealloc.api_func
- def allocate(self, space, w_type, itemcount=0):
+ def allocate(self, space, w_type, itemcount=0, immortal=False):
# typically called from PyType_GenericAlloc via typedescr.allocate
# this returns a PyObject with ob_refcnt == 1.
@@ -50,7 +50,7 @@
assert size >= rffi.sizeof(PyObject.TO)
buf = lltype.malloc(rffi.VOIDP.TO, size,
flavor='raw', zero=True,
- add_memory_pressure=True)
+ add_memory_pressure=True, immortal=immortal)
pyobj = rffi.cast(PyObject, buf)
if pytype.c_tp_itemsize:
pyvarobj = rffi.cast(PyVarObject, pyobj)
@@ -102,7 +102,7 @@
basestruct = tp_basestruct
if tp_alloc:
- def allocate(self, space, w_type, itemcount=0):
+ def allocate(self, space, w_type, itemcount=0, immortal=False):
return tp_alloc(space, w_type, itemcount)
if tp_dealloc:
@@ -151,7 +151,7 @@
class InvalidPointerException(Exception):
pass
-def create_ref(space, w_obj, w_userdata=None):
+def create_ref(space, w_obj, w_userdata=None, immortal=False):
"""
Allocates a PyObject, and fills its fields with info from the given
interpreter object.
@@ -163,7 +163,7 @@
itemcount = space.len_w(w_obj) # PyBytesObject and subclasses
else:
itemcount = 0
- py_obj = typedescr.allocate(space, w_type, itemcount=itemcount)
+ py_obj = typedescr.allocate(space, w_type, itemcount=itemcount,
immortal=immortal)
track_reference(space, py_obj, w_obj)
#
# py_obj.c_ob_refcnt should be exactly REFCNT_FROM_PYPY + 1 here,
@@ -227,7 +227,7 @@
assert isinstance(w_type, W_TypeObject)
return get_typedescr(w_type.layout.typedef).realize(space, ref)
-def as_pyobj(space, w_obj, w_userdata=None):
+def as_pyobj(space, w_obj, w_userdata=None, immortal=False):
"""
Returns a 'PyObject *' representing the given intepreter object.
This doesn't give a new reference, but the returned 'PyObject *'
@@ -239,7 +239,7 @@
assert not is_pyobj(w_obj)
py_obj = rawrefcount.from_obj(PyObject, w_obj)
if not py_obj:
- py_obj = create_ref(space, w_obj, w_userdata)
+ py_obj = create_ref(space, w_obj, w_userdata, immortal=immortal)
return py_obj
else:
return lltype.nullptr(PyObject.TO)
@@ -270,7 +270,7 @@
return hop.inputconst(lltype.Bool, hop.s_result.const)
@specialize.ll()
-def make_ref(space, obj, w_userdata=None):
+def make_ref(space, obj, w_userdata=None, immortal=False):
"""Increment the reference counter of the PyObject and return it.
Can be called with either a PyObject or a W_Root.
"""
@@ -278,7 +278,7 @@
pyobj = rffi.cast(PyObject, obj)
at_least = 1
else:
- pyobj = as_pyobj(space, obj, w_userdata)
+ pyobj = as_pyobj(space, obj, w_userdata, immortal=immortal)
at_least = rawrefcount.REFCNT_FROM_PYPY
if pyobj:
assert pyobj.c_ob_refcnt >= at_least
diff --git a/pypy/module/cpyext/sequence.py b/pypy/module/cpyext/sequence.py
--- a/pypy/module/cpyext/sequence.py
+++ b/pypy/module/cpyext/sequence.py
@@ -48,7 +48,7 @@
m as the message text. If the conversion otherwise, fails, reraise the
original exception"""
if isinstance(w_obj, W_ListObject):
- # make sure we can return a borrowed obj from PySequence_Fast_GET_ITEM
+ # make sure we can return a borrowed obj from PySequence_Fast_GET_ITEM
w_obj.convert_to_cpy_strategy(space)
return w_obj
try:
@@ -313,7 +313,7 @@
self)
w_clone.switch_to_object_strategy()
return w_clone
-
+
def copy_into(self, w_list, w_other):
w_list.switch_to_object_strategy()
w_list.strategy.copy_into(w_list, w_other)
@@ -378,7 +378,7 @@
def is_empty_strategy(self):
return False
-
+
PyObjectList = lltype.Ptr(lltype.Array(PyObject, hints={'nolength': True}))
diff --git a/pypy/module/cpyext/test/array.c b/pypy/module/cpyext/test/array.c
--- a/pypy/module/cpyext/test/array.c
+++ b/pypy/module/cpyext/test/array.c
@@ -2322,10 +2322,12 @@
if (PyList_Check(obj1) && ((arrayobject*)obj2)->ob_descr->typecode == 'i'
&& Py_SIZE(obj2) == 1)
{
int ii, nn;
+ PyObject *ret;
int n = PyList_Size(obj1);
PyObject *v = getarrayitem(obj2, 0);
long i = PyLong_AsLong(v); // XXX: error checking?
- PyObject * ret = PyList_New(n*i);
+ Py_DECREF(v);
+ ret = PyList_New(n*i);
for (ii = 0; ii < i; ii++)
for (nn = 0; nn < n; nn++)
{
@@ -2338,10 +2340,12 @@
else if (PyList_Check(obj2) && ((arrayobject*)obj1)->ob_descr->typecode ==
'i' && Py_SIZE(obj1) == 1)
{
int ii, nn;
+ PyObject *ret;
int n = PyList_Size(obj2);
PyObject *v = getarrayitem(obj1, 0);
long i = PyLong_AsLong(v);
- PyObject * ret = PyList_New(n*i);
+ Py_DECREF(v);
+ ret = PyList_New(n*i);
for (ii = 0; ii < i; ii++)
for (nn = 0; nn < n; nn++)
{
@@ -2374,31 +2378,35 @@
if (PyList_Check(obj1) && ((arrayobject*)obj2)->ob_descr->typecode == 'i'
&& Py_SIZE(obj2) == 1)
{
int nn;
+ PyObject *ret;
int n = PyList_Size(obj1);
PyObject * lhs, * out;
PyObject * rhs = getarrayitem(obj2, 0);
- PyObject * ret = PyList_New(n);
+ ret = PyList_New(n);
for (nn = 0; nn < n; nn++)
{
lhs = PyList_GetItem(obj1, nn);
out = lhs->ob_type->tp_as_number->nb_multiply(lhs, rhs);
PyList_SetItem(ret, nn, out);
}
+ Py_DECREF(rhs);
return ret;
}
else if (PyList_Check(obj2) && ((arrayobject*)obj1)->ob_descr->typecode ==
'i' && Py_SIZE(obj1) == 1)
{
int nn;
+ PyObject *ret;
int n = PyList_Size(obj2);
PyObject * rhs, * out;
PyObject * lhs = getarrayitem(obj1, 0);
- PyObject * ret = PyList_New(n);
+ ret = PyList_New(n);
for (nn = 0; nn < n; nn++)
{
rhs = PyList_GetItem(obj2, nn);
out = lhs->ob_type->tp_as_number->nb_multiply(lhs, rhs);
PyList_SetItem(ret, nn, out);
}
+ Py_DECREF(lhs);
return ret;
}
else if(obj1->ob_type == &Arraytype)
@@ -2904,6 +2912,16 @@
return PyLong_FromLong(buf_len);
}
+static PyObject *
+same_dealloc(PyObject *self, PyObject *args)
+{
+ PyObject *obj1, *obj2;
+ if (!PyArg_ParseTuple(args, "OO", &obj1, &obj2)) {
+ return NULL;
+ }
+ return PyLong_FromLong(obj1->ob_type->tp_dealloc ==
obj2->ob_type->tp_dealloc);
+}
+
/*********************** Install Module **************************/
static PyMethodDef a_methods[] = {
@@ -2914,6 +2932,7 @@
{"get_releasebuffer_cnt", (PyCFunction)get_releasebuffer_cnt,
METH_NOARGS, NULL},
{"create_and_release_buffer", (PyCFunction)create_and_release_buffer,
METH_O, NULL},
{"write_buffer_len", write_buffer_len, METH_O, NULL},
+ {"same_dealloc", (PyCFunction)same_dealloc, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL} /* Sentinel */
};
diff --git a/pypy/module/cpyext/test/test_api.py
b/pypy/module/cpyext/test/test_api.py
--- a/pypy/module/cpyext/test/test_api.py
+++ b/pypy/module/cpyext/test/test_api.py
@@ -6,7 +6,8 @@
from pypy.module.cpyext.api import (
slot_function, cpython_api, copy_header_files, INTERPLEVEL_API,
Py_ssize_t, Py_ssize_tP, PyObject, cts)
-from pypy.module.cpyext.test.test_cpyext import freeze_refcnts,
LeakCheckingTest
+from pypy.module.cpyext.test.test_cpyext import (
+ freeze_refcnts, LeakCheckingTest)
from pypy.interpreter.error import OperationError
from rpython.rlib import rawrefcount
import os
@@ -21,17 +22,7 @@
class BaseApiTest(LeakCheckingTest):
def setup_class(cls):
space = cls.space
- # warm up reference counts:
- # - the posix module allocates a HCRYPTPROV on Windows
- # - writing to stdout and stderr allocates a file lock
- space.getbuiltinmodule("cpyext")
- space.getbuiltinmodule(os.name)
- space.call_function(space.getattr(space.sys.get("stderr"),
- space.wrap("write")),
- space.wrap(""))
- space.call_function(space.getattr(space.sys.get("stdout"),
- space.wrap("write")),
- space.wrap(""))
+ cls.preload_builtins(space)
class CAPI:
def __getattr__(self, name):
@@ -39,9 +30,6 @@
cls.api = CAPI()
CAPI.__dict__.update(INTERPLEVEL_API)
- print 'DONT_FREE_ANY_MORE'
- rawrefcount._dont_free_any_more()
-
def raises(self, space, api, expected_exc, f, *args):
if not callable(f):
raise Exception("%s is not callable" % (f,))
diff --git a/pypy/module/cpyext/test/test_arraymodule.py
b/pypy/module/cpyext/test/test_arraymodule.py
--- a/pypy/module/cpyext/test/test_arraymodule.py
+++ b/pypy/module/cpyext/test/test_arraymodule.py
@@ -1,3 +1,4 @@
+import pytest
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
from pypy.conftest import option
@@ -99,6 +100,19 @@
res = [1, 2, 3] * arr
assert res == [2, 4, 6]
+ @pytest.mark.xfail
+ def test_subclass_dealloc(self):
+ module = self.import_module(name='array')
+ class Sub(module.array):
+ pass
+
+ arr = Sub('i', [2])
+ module.readbuffer_as_string(arr)
+ class A(object):
+ pass
+ assert not module.same_dealloc(arr, module.array('i', [2]))
+ assert module.same_dealloc(arr, A())
+
def test_string_buf(self):
module = self.import_module(name='array')
arr = module.array('u', '123')
diff --git a/pypy/module/cpyext/test/test_cpyext.py
b/pypy/module/cpyext/test/test_cpyext.py
--- a/pypy/module/cpyext/test/test_cpyext.py
+++ b/pypy/module/cpyext/test/test_cpyext.py
@@ -4,8 +4,11 @@
from pypy.tool.cpyext.extbuild import SystemCompilationInfo, HERE
from pypy.interpreter.gateway import unwrap_spec, interp2app
+from pypy.interpreter.error import OperationError
from rpython.rtyper.lltypesystem import lltype
from pypy.module.cpyext import api
+from pypy.module.cpyext.api import cts
+from pypy.module.cpyext.pyobject import from_ref
from pypy.module.cpyext.state import State
from rpython.tool import leakfinder
from rpython.rlib import rawrefcount
@@ -75,18 +78,94 @@
def freeze_refcnts(self):
rawrefcount._dont_free_any_more()
+def preload(space, name):
+ from pypy.module.cpyext.pyobject import make_ref
+ if '.' not in name:
+ w_obj = space.builtin.getdictvalue(space, name)
+ else:
+ module, localname = name.rsplit('.', 1)
+ code = "(): import {module}; return {module}.{localname}"
+ code = code.format(**locals())
+ w_obj = space.appexec([], code)
+ make_ref(space, w_obj)
+
+def preload_expr(space, expr):
+ from pypy.module.cpyext.pyobject import make_ref
+ code = "(): return {}".format(expr)
+ w_obj = space.appexec([], code)
+ make_ref(space, w_obj)
+
+def is_interned_string(space, w_obj):
+ try:
+ s = space.str_w(w_obj)
+ except OperationError:
+ return False
+ return space.is_interned_str(s)
+
+def is_allowed_to_leak(space, obj):
+ from pypy.module.cpyext.methodobject import W_PyCFunctionObject
+ try:
+ w_obj = from_ref(space, cts.cast('PyObject*', obj._as_ptr()))
+ except:
+ return False
+ if isinstance(w_obj, W_PyCFunctionObject):
+ return True
+ # It's OK to "leak" some interned strings: if the pyobj is created by
+ # the test, but the w_obj is referred to from elsewhere.
+ return is_interned_string(space, w_obj)
+
+def _get_w_obj(space, c_obj):
+ return from_ref(space, cts.cast('PyObject*', c_obj._as_ptr()))
+
+class CpyextLeak(leakfinder.MallocMismatch):
+ def __str__(self):
+ lines = [leakfinder.MallocMismatch.__str__(self), '']
+ lines.append(
+ "These objects are attached to the following W_Root objects:")
+ for c_obj in self.args[0]:
+ try:
+ lines.append(" %s" % (_get_w_obj(self.args[1], c_obj),))
+ except:
+ pass
+ return '\n'.join(lines)
+
+
class LeakCheckingTest(object):
"""Base class for all cpyext tests."""
spaceconfig = dict(usemodules=['cpyext', 'thread', 'struct', 'array',
'itertools', 'time', 'binascii', 'mmap',
])
+ @classmethod
+ def preload_builtins(cls, space):
+ """
+ Eagerly create pyobjs for various builtins so they don't look like
+ leaks.
+ """
+ for name in [
+ 'buffer', 'mmap.mmap',
+ 'types.FunctionType', 'types.CodeType',
+ 'types.TracebackType', 'types.FrameType']:
+ preload(space, name)
+ for expr in ['type(str.join)']:
+ preload_expr(space, expr)
+
def cleanup(self):
self.space.getexecutioncontext().cleanup_cpyext_state()
for _ in range(5):
rawrefcount._collect()
self.space.user_del_action._run_finalizers()
- leakfinder.stop_tracking_allocations(check=False)
+ try:
+ # set check=True to actually enable leakfinder
+ leakfinder.stop_tracking_allocations(check=False)
+ except leakfinder.MallocMismatch as e:
+ result = e.args[0]
+ filtered_result = {}
+ for obj, value in result.iteritems():
+ if not is_allowed_to_leak(self.space, obj):
+ filtered_result[obj] = value
+ if filtered_result:
+ raise CpyextLeak(filtered_result, self.space)
assert not self.space.finalizer_queue.next_dead()
@@ -127,6 +206,7 @@
def debug_collect(space):
rawrefcount._collect()
+
class AppTestCpythonExtensionBase(LeakCheckingTest):
def setup_class(cls):
@@ -136,13 +216,8 @@
cls.w_runappdirect = space.wrap(cls.runappdirect)
if not cls.runappdirect:
cls.sys_info = get_cpyext_info(space)
- space.getbuiltinmodule("cpyext")
- # 'import os' to warm up reference counts
- w_import = space.builtin.getdictvalue(space, '__import__')
- space.call_function(w_import, space.wrap("os"))
- #state = cls.space.fromcache(RefcountState) ZZZ
- #state.non_heaptypes_w[:] = []
cls.w_debug_collect = space.wrap(interp2app(debug_collect))
+ cls.preload_builtins(space)
else:
def w_import_module(self, name, init=None, body='', filename=None,
include_dirs=None, PY_SSIZE_T_CLEAN=False):
diff --git a/pypy/module/cpyext/test/test_dictobject.py
b/pypy/module/cpyext/test/test_dictobject.py
--- a/pypy/module/cpyext/test/test_dictobject.py
+++ b/pypy/module/cpyext/test/test_dictobject.py
@@ -112,74 +112,14 @@
PyDict_Update(space, w_d, w_d2)
assert space.unwrap(w_d) == dict(a='b') # unchanged
- def test_iter(self, space):
- w_dict = space.sys.getdict(space)
- py_dict = make_ref(space, w_dict)
-
- ppos = lltype.malloc(Py_ssize_tP.TO, 1, flavor='raw')
- ppos[0] = 0
- pkey = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
- pvalue = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
-
- try:
- w_copy = space.newdict()
- while PyDict_Next(space, w_dict, ppos, pkey, pvalue):
- w_key = from_ref(space, pkey[0])
- w_value = from_ref(space, pvalue[0])
- space.setitem(w_copy, w_key, w_value)
- finally:
- lltype.free(ppos, flavor='raw')
- lltype.free(pkey, flavor='raw')
- lltype.free(pvalue, flavor='raw')
-
- decref(space, py_dict) # release borrowed references
-
- assert space.eq_w(space.len(w_copy), space.len(w_dict))
- assert space.eq_w(w_copy, w_dict)
-
- def test_iterkeys(self, space):
- w_dict = space.sys.getdict(space)
- py_dict = make_ref(space, w_dict)
-
- ppos = lltype.malloc(Py_ssize_tP.TO, 1, flavor='raw')
- pkey = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
- pvalue = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
-
- keys_w = []
- values_w = []
- try:
- ppos[0] = 0
- while PyDict_Next(space, w_dict, ppos, pkey, None):
- w_key = from_ref(space, pkey[0])
- keys_w.append(w_key)
- ppos[0] = 0
- while PyDict_Next(space, w_dict, ppos, None, pvalue):
- w_value = from_ref(space, pvalue[0])
- values_w.append(w_value)
- finally:
- lltype.free(ppos, flavor='raw')
- lltype.free(pkey, flavor='raw')
- lltype.free(pvalue, flavor='raw')
-
- decref(space, py_dict) # release borrowed references
-
- assert space.eq_w(space.newlist(keys_w),
- space.call_function(
- space.w_list,
- space.call_method(w_dict, "keys")))
- assert space.eq_w(space.newlist(values_w),
- space.call_function(
- space.w_list,
- space.call_method(w_dict, "values")))
-
def test_dictproxy(self, space):
- w_dict = space.sys.get('modules')
+ w_dict = space.appexec([], """(): return {1: 2, 3: 4}""")
w_proxy = PyDictProxy_New(space, w_dict)
- assert space.contains_w(w_proxy, space.wrap('sys'))
+ assert space.contains_w(w_proxy, space.newint(1))
raises(OperationError, space.setitem,
- w_proxy, space.wrap('sys'), space.w_None)
+ w_proxy, space.newint(1), space.w_None)
raises(OperationError, space.delitem,
- w_proxy, space.wrap('sys'))
+ w_proxy, space.newint(1))
raises(OperationError, space.call_method, w_proxy, 'clear')
assert PyDictProxy_Check(space, w_proxy)
@@ -248,6 +188,59 @@
d = {"a": 1}
raises(AttributeError, module.update, d, [("c", 2)])
+ def test_iter(self):
+ module = self.import_extension('foo', [
+ ("copy", "METH_O",
+ '''
+ Py_ssize_t pos = 0;
+ PyObject *key, *value;
+ PyObject* copy = PyDict_New();
+ while (PyDict_Next(args, &pos, &key, &value))
+ {
+ if (PyDict_SetItem(copy, key, value) < 0)
+ {
+ Py_DecRef(copy);
+ return NULL;
+ }
+ }
+ return copy;
+ ''')])
+ d = {1: 'xyz', 3: 'abcd'}
+ copy = module.copy(d)
+ assert len(copy) == len(d)
+ assert copy == d
+
+ def test_iterkeys(self):
+ module = self.import_extension('foo', [
+ ("keys_and_values", "METH_O",
+ '''
+ Py_ssize_t pos = 0;
+ PyObject *key, *value, *values;
+ PyObject* keys = PyList_New(0);
+ while (PyDict_Next(args, &pos, &key, NULL))
+ {
+ if (PyList_Append(keys, key) < 0)
+ {
+ Py_DecRef(keys);
+ return NULL;
+ }
+ }
+ pos = 0;
+ values = PyList_New(0);
+ while (PyDict_Next(args, &pos, NULL, &value))
+ {
+ if (PyList_Append(values, value) < 0)
+ {
+ Py_DecRef(keys);
+ Py_DecRef(values);
+ return NULL;
+ }
+ }
+ return Py_BuildValue("(NN)", keys, values);
+ ''')])
+ d = {1: 'xyz', 3: 'abcd'}
+ assert module.keys_and_values(d) == (list(d.keys()), list(d.values()))
+
def test_typedict2(self):
module = self.import_extension('foo', [
("get_type_dict", "METH_O",
@@ -260,6 +253,7 @@
])
d = module.get_type_dict(1)
assert d['real'].__get__(1, 1) == 1
+
def test_advanced(self):
module = self.import_extension('foo', [
("dict_len", "METH_O",
@@ -271,7 +265,7 @@
'''
int ret;
PyObject * dict = PyTuple_GetItem(args, 0);
- if (PyTuple_Size(args) < 3 || !dict ||
+ if (PyTuple_Size(args) < 3 || !dict ||
!dict->ob_type->tp_as_mapping ||
!dict->ob_type->tp_as_mapping->mp_ass_subscript)
return PyLong_FromLong(-1);
@@ -284,7 +278,7 @@
'''
int ret;
PyObject * dict = PyTuple_GetItem(args, 0);
- if (PyTuple_Size(args) < 2 || !dict ||
+ if (PyTuple_Size(args) < 2 || !dict ||
!dict->ob_type->tp_as_mapping ||
!dict->ob_type->tp_as_mapping->mp_ass_subscript)
return PyLong_FromLong(-1);
diff --git a/pypy/module/cpyext/test/test_eval.py
b/pypy/module/cpyext/test/test_eval.py
--- a/pypy/module/cpyext/test/test_eval.py
+++ b/pypy/module/cpyext/test/test_eval.py
@@ -279,6 +279,7 @@
assert module.call_method("text") == 2
def test_CompileString_and_Exec(self):
+ import sys
module = self.import_extension('foo', [
("compile_string", "METH_NOARGS",
"""
@@ -313,6 +314,9 @@
print(mod.__dict__)
assert mod.f(42) == 47
+ # Clean-up
+ del sys.modules['cpyext_test_modname']
+
def test_merge_compiler_flags(self):
import sys
module = self.import_extension('foo', [
diff --git a/pypy/module/cpyext/test/test_floatobject.py
b/pypy/module/cpyext/test/test_floatobject.py
--- a/pypy/module/cpyext/test/test_floatobject.py
+++ b/pypy/module/cpyext/test/test_floatobject.py
@@ -104,6 +104,7 @@
PyFloatObject* pfo = (PyFloatObject*)pyobj;
int res = PyFloat_Check(pyobj) && PyFloat_CheckExact(pyobj) &&
PyFloat_Check(pfo) && PyFloat_CheckExact(pfo);
+ Py_DecRef(pyobj);
return PyLong_FromLong(res);"""),
])
assert module.test() == 1
diff --git a/pypy/module/cpyext/test/test_funcobject.py
b/pypy/module/cpyext/test/test_funcobject.py
--- a/pypy/module/cpyext/test/test_funcobject.py
+++ b/pypy/module/cpyext/test/test_funcobject.py
@@ -44,7 +44,7 @@
w_function = space.appexec([], """():
def func(x, y, z): return x
return func
- """)
+ """, cache=False)
w_code = PyFunction_GetCode(space, w_function)
assert w_code.co_name == "func"
@@ -61,7 +61,7 @@
w_code = space.appexec([], """():
def func(%s): %s
return func.__code__
- """ % (signature, body))
+ """ % (signature, body), cache=False)
ref = make_ref(space, w_code)
co_flags = rffi.cast(PyCodeObject, ref).c_co_flags
decref(space, ref)
diff --git a/pypy/module/cpyext/test/test_longobject.py
b/pypy/module/cpyext/test/test_longobject.py
--- a/pypy/module/cpyext/test/test_longobject.py
+++ b/pypy/module/cpyext/test/test_longobject.py
@@ -329,6 +329,7 @@
ret = obj->ob_type->tp_as_number->nb_power(obj, one, one);
else
ret = PyLong_FromLong(-1);
+ Py_DECREF(one);
Py_DECREF(obj);
return ret;
""")])
diff --git a/pypy/module/cpyext/test/test_memoryobject.py
b/pypy/module/cpyext/test/test_memoryobject.py
--- a/pypy/module/cpyext/test/test_memoryobject.py
+++ b/pypy/module/cpyext/test/test_memoryobject.py
@@ -6,7 +6,7 @@
from pypy.module.cpyext.test.test_api import BaseApiTest
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
from pypy.interpreter.buffer import SimpleView
-from pypy.module.cpyext.pyobject import from_ref
+from pypy.module.cpyext.pyobject import make_ref, from_ref
from pypy.module.cpyext.memoryobject import PyMemoryViewObject
only_pypy ="config.option.runappdirect and '__pypy__' not in
sys.builtin_module_names"
@@ -14,9 +14,9 @@
class TestMemoryViewObject(BaseApiTest):
def test_frombuffer(self, space, api):
w_view = SimpleView(StringBuffer("hello")).wrap(space)
+ w_memoryview = api.PyMemoryView_FromObject(w_view)
c_memoryview = rffi.cast(
- PyMemoryViewObject, api.PyMemoryView_FromObject(w_view))
- w_memoryview = from_ref(space, c_memoryview)
+ PyMemoryViewObject, make_ref(space, w_memoryview))
view = c_memoryview.c_view
assert view.c_ndim == 1
f = rffi.charp2str(view.c_format)
@@ -34,6 +34,7 @@
assert space.eq_w(space.getattr(w_mv, w_f),
space.getattr(w_memoryview, w_f))
api.Py_DecRef(ref)
+ api.Py_DecRef(w_memoryview)
class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase):
def test_fillWithObject(self):
@@ -64,7 +65,6 @@
""")])
result = module.fillinfo()
assert b"hello, world." == result
- del result
def test_0d(self):
module = self.import_extension('foo', [
@@ -195,8 +195,6 @@
# in <bound method ConcreteArray.__del__ ...> ignored
def test_releasebuffer(self):
- if not self.runappdirect:
- skip("Fails due to ll2ctypes nonsense")
module = self.import_extension('foo', [
("create_test", "METH_NOARGS",
"""
diff --git a/pypy/module/cpyext/test/test_traceback.py
b/pypy/module/cpyext/test/test_traceback.py
--- a/pypy/module/cpyext/test/test_traceback.py
+++ b/pypy/module/cpyext/test/test_traceback.py
@@ -3,17 +3,19 @@
from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref
from pypy.module.cpyext.pytraceback import PyTracebackObject
from pypy.interpreter.pytraceback import PyTraceback
-from pypy.interpreter.pyframe import PyFrame
+from pypy.interpreter.baseobjspace import AppExecCache
class TestPyTracebackObject(BaseApiTest):
def test_traceback(self, space, api):
- w_traceback = space.appexec([], """():
+ src = """():
import sys
try:
1/0
except:
return sys.exc_info()[2]
- """)
+ """
+ w_traceback = space.appexec([], src)
+
py_obj = make_ref(space, w_traceback)
py_traceback = rffi.cast(PyTracebackObject, py_obj)
assert (from_ref(space, rffi.cast(PyObject, py_traceback.c_ob_type)) is
@@ -38,3 +40,5 @@
assert lltype.normalizeptr(py_traceback) is None
api.Py_DecRef(py_obj)
+ # hack to allow the code object to be freed
+ del space.fromcache(AppExecCache).content[src]
diff --git a/pypy/module/cpyext/test/test_tupleobject.py
b/pypy/module/cpyext/test/test_tupleobject.py
--- a/pypy/module/cpyext/test/test_tupleobject.py
+++ b/pypy/module/cpyext/test/test_tupleobject.py
@@ -24,6 +24,7 @@
def test_tuple_realize_refuses_nulls(self, space, api):
py_tuple = api.PyTuple_New(1)
py.test.raises(FatalError, from_ref, space, py_tuple)
+ api.Py_DecRef(py_tuple)
def test_tuple_resize(self, space, api):
w_42 = space.wrap(42)
@@ -70,6 +71,7 @@
w_tuple = from_ref(space, py_tuple)
assert space.eq_w(w_tuple, space.newtuple([space.wrap(42),
space.wrap(43)]))
+ api.Py_DecRef(py_tuple)
def test_getslice(self, space, api):
w_tuple = space.newtuple([space.wrap(i) for i in range(10)])
@@ -174,6 +176,7 @@
res = PyTuple_SetItem(tuple, 0, one);
if (res != 0)
{
+ Py_DECREF(one);
Py_DECREF(tuple);
return NULL;
}
@@ -187,14 +190,13 @@
/* Do something that uses the tuple, but does not incref */
t2 = PyTuple_GetSlice(tuple, 0, 1);
Py_DECREF(t2);
- Py_INCREF(one);
res = PyTuple_SetItem(tuple, 0, one);
- Py_DECREF(tuple);
if (res != 0)
{
- Py_DECREF(one);
+ Py_DECREF(tuple);
return NULL;
}
+ Py_DECREF(tuple);
Py_INCREF(Py_None);
return Py_None;
"""),
@@ -205,4 +207,3 @@
raises(SystemError, module.set_after_use, s)
else:
module.set_after_use(s)
-
diff --git a/pypy/module/cpyext/tupleobject.py
b/pypy/module/cpyext/tupleobject.py
--- a/pypy/module/cpyext/tupleobject.py
+++ b/pypy/module/cpyext/tupleobject.py
@@ -143,6 +143,7 @@
old_ref = tupleobj.c_ob_item[index]
if pyobj_has_w_obj(ref):
# similar but not quite equal to ref.c_ob_refcnt != 1 on CPython
+ decref(space, py_obj)
raise oefmt(space.w_SystemError, "PyTuple_SetItem called on tuple
after"
" use of tuple")
tupleobj.c_ob_item[index] = py_obj # consumes a reference
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -194,6 +194,8 @@
py_getsetdef = make_GetSet(space, w_obj)
assert space.isinstance_w(w_userdata, space.w_type)
w_obj = W_GetSetPropertyEx(py_getsetdef, w_userdata)
+ # now w_obj.getset is py_getsetdef, which was freshly allocated
+ # XXX how is this ever released?
# XXX assign to d_dname, d_type?
assert isinstance(w_obj, W_GetSetPropertyEx)
py_getsetdescr.c_d_getset = w_obj.getset
@@ -823,7 +825,9 @@
bases_w = []
else:
bases_w = [from_ref(space, base_pyo)]
- pto.c_tp_bases = make_ref(space, space.newtuple(bases_w))
+ is_heaptype = bool(pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE)
+ pto.c_tp_bases = make_ref(space, space.newtuple(bases_w),
+ immortal=not is_heaptype)
def finish_type_2(space, pto, w_obj):
"""
diff --git a/pypy/objspace/std/specialisedtupleobject.py
b/pypy/objspace/std/specialisedtupleobject.py
--- a/pypy/objspace/std/specialisedtupleobject.py
+++ b/pypy/objspace/std/specialisedtupleobject.py
@@ -74,8 +74,7 @@
elif typetuple[i] == int:
# mimic cpythons behavior of a hash value of -2 for -1
y = value
- if y == -1:
- y = -2
+ y -= (y == -1) # No explicit condition, to avoid JIT
bridges
elif typetuple[i] == float:
# get the correct hash for float which is an
# integer & other less frequent cases
diff --git a/pypy/objspace/std/test/test_specialisedtupleobject.py
b/pypy/objspace/std/test/test_specialisedtupleobject.py
--- a/pypy/objspace/std/test/test_specialisedtupleobject.py
+++ b/pypy/objspace/std/test/test_specialisedtupleobject.py
@@ -37,6 +37,8 @@
self.space.eq(self.space.hash(N_w_tuple),
self.space.hash(S_w_tuple)))
+ hash_test([-1, -1])
+ hash_test([-1.0, -1.0])
hash_test([1, 2])
hash_test([1.5, 2.8])
hash_test([1.0, 2.0])
diff --git a/rpython/jit/metainterp/logger.py b/rpython/jit/metainterp/logger.py
--- a/rpython/jit/metainterp/logger.py
+++ b/rpython/jit/metainterp/logger.py
@@ -13,10 +13,11 @@
self.guard_number = guard_number
def log_loop_from_trace(self, trace, memo):
+ debug_start("jit-log-noopt")
if not have_debug_prints():
+ debug_stop("jit-log-noopt")
return
inputargs, ops = self._unpack_trace(trace)
- debug_start("jit-log-noopt")
debug_print("# Traced loop or bridge with", len(ops), "ops")
logops = self._log_operations(inputargs, ops, None, memo)
debug_stop("jit-log-noopt")
diff --git a/rpython/jit/metainterp/optimizeopt/bridgeopt.py
b/rpython/jit/metainterp/optimizeopt/bridgeopt.py
--- a/rpython/jit/metainterp/optimizeopt/bridgeopt.py
+++ b/rpython/jit/metainterp/optimizeopt/bridgeopt.py
@@ -18,6 +18,10 @@
# (<box1> <descr> <box2>) length times, if getfield(box1, descr) == box2
# both boxes should be in the liveboxes
#
+# <length>
+# (<box1> <index> <descr> <box2>) length times, if getarrayitem_gc(box1,
index, descr) == box2
+# both boxes should be in the liveboxes
+#
# ----
@@ -82,18 +86,26 @@
# structs
# XXX could be extended to arrays
if optimizer.optheap:
- triples = optimizer.optheap.serialize_optheap(available_boxes)
+ triples_struct, triples_array =
optimizer.optheap.serialize_optheap(available_boxes)
# can only encode descrs that have a known index into
# metainterp_sd.all_descrs
- triples = [triple for triple in triples if triple[1].descr_index != -1]
- numb_state.append_int(len(triples))
- for box1, descr, box2 in triples:
- index = descr.descr_index
+ triples_struct = [triple for triple in triples_struct if
triple[1].descr_index != -1]
+ numb_state.append_int(len(triples_struct))
+ for box1, descr, box2 in triples_struct:
+ descr_index = descr.descr_index
+ numb_state.append_short(tag_box(box1, liveboxes_from_env, memo))
+ numb_state.append_int(descr_index)
+ numb_state.append_short(tag_box(box2, liveboxes_from_env, memo))
+ numb_state.append_int(len(triples_array))
+ for box1, index, descr, box2 in triples_array:
+ descr_index = descr.descr_index
numb_state.append_short(tag_box(box1, liveboxes_from_env, memo))
numb_state.append_int(index)
+ numb_state.append_int(descr_index)
numb_state.append_short(tag_box(box2, liveboxes_from_env, memo))
else:
numb_state.append_int(0)
+ numb_state.append_int(0)
def deserialize_optimizer_knowledge(optimizer, resumestorage, frontend_boxes,
liveboxes):
reader = resumecode.Reader(resumestorage.rd_numb)
@@ -123,13 +135,24 @@
if not optimizer.optheap:
return
length = reader.next_item()
- result = []
+ result_struct = []
+ for i in range(length):
+ tagged = reader.next_item()
+ box1 = decode_box(resumestorage, tagged, liveboxes, metainterp_sd.cpu)
+ descr_index = reader.next_item()
+ descr = metainterp_sd.all_descrs[descr_index]
+ tagged = reader.next_item()
+ box2 = decode_box(resumestorage, tagged, liveboxes, metainterp_sd.cpu)
+ result_struct.append((box1, descr, box2))
+ length = reader.next_item()
+ result_array = []
for i in range(length):
tagged = reader.next_item()
box1 = decode_box(resumestorage, tagged, liveboxes, metainterp_sd.cpu)
index = reader.next_item()
- descr = metainterp_sd.all_descrs[index]
+ descr_index = reader.next_item()
+ descr = metainterp_sd.all_descrs[descr_index]
tagged = reader.next_item()
box2 = decode_box(resumestorage, tagged, liveboxes, metainterp_sd.cpu)
- result.append((box1, descr, box2))
- optimizer.optheap.deserialize_optheap(result)
+ result_array.append((box1, index, descr, box2))
+ optimizer.optheap.deserialize_optheap(result_struct, result_array)
diff --git a/rpython/jit/metainterp/optimizeopt/heap.py
b/rpython/jit/metainterp/optimizeopt/heap.py
--- a/rpython/jit/metainterp/optimizeopt/heap.py
+++ b/rpython/jit/metainterp/optimizeopt/heap.py
@@ -698,7 +698,7 @@
return self.emit(op)
def serialize_optheap(self, available_boxes):
- result = []
+ result_getfield = []
for descr, cf in self.cached_fields.iteritems():
if cf._lazy_set:
continue # XXX safe default for now
@@ -706,27 +706,62 @@
if not parent_descr.is_object():
continue # XXX could be extended to non-instance objects
for i, box1 in enumerate(cf.cached_structs):
- if box1 not in available_boxes:
+ if not box1.is_constant() and box1 not in available_boxes:
continue
structinfo = cf.cached_infos[i]
- box2 = structinfo.getfield(descr).get_box_replacement()
- if isinstance(box2, Const) or box2 in available_boxes:
- result.append((box1, descr, box2))
- return result
+ box2 = structinfo.getfield(descr)
+ if box2 is None:
+ # XXX this should not happen, as it is an invariant
+ # violation! yet it does if box1 is a constant
+ continue
+ box2 = box2.get_box_replacement()
+ if box2.is_constant() or box2 in available_boxes:
+ result_getfield.append((box1, descr, box2))
+ result_array = []
+ for descr, indexdict in self.cached_arrayitems.iteritems():
+ for index, cf in indexdict.iteritems():
+ if cf._lazy_set:
+ continue # XXX safe default for now
+ for i, box1 in enumerate(cf.cached_structs):
+ if not box1.is_constant() and box1 not in available_boxes:
+ continue
+ arrayinfo = cf.cached_infos[i]
+ box2 = arrayinfo.getitem(descr, index)
+ if box2 is None:
+ # XXX this should not happen, as it is an invariant
+ # violation! yet it does if box1 is a constant
+ continue
+ box2 = box2.get_box_replacement()
+ if box2.is_constant() or box2 in available_boxes:
+ result_array.append((box1, index, descr, box2))
+ return result_getfield, result_array
- def deserialize_optheap(self, triples):
- for box1, descr, box2 in triples:
+ def deserialize_optheap(self, triples_struct, triples_array):
+ for box1, descr, box2 in triples_struct:
parent_descr = descr.get_parent_descr()
assert parent_descr.is_object()
- structinfo = box1.get_forwarded()
- if not isinstance(structinfo, info.AbstractVirtualPtrInfo):
- structinfo = info.InstancePtrInfo(parent_descr)
- structinfo.init_fields(parent_descr, descr.get_index())
- box1.set_forwarded(structinfo)
-
+ if box1.is_constant():
+ structinfo = info.ConstPtrInfo(box1)
+ else:
+ structinfo = box1.get_forwarded()
+ if not isinstance(structinfo, info.AbstractVirtualPtrInfo):
+ structinfo = info.InstancePtrInfo(parent_descr)
+ structinfo.init_fields(parent_descr, descr.get_index())
+ box1.set_forwarded(structinfo)
cf = self.field_cache(descr)
structinfo.setfield(descr, box1, box2, optheap=self, cf=cf)
+ for box1, index, descr, box2 in triples_array:
+ if box1.is_constant():
+ arrayinfo = info.ConstPtrInfo(box1)
+ else:
+ arrayinfo = box1.get_forwarded()
+ if not isinstance(arrayinfo, info.AbstractVirtualPtrInfo):
+ arrayinfo = info.ArrayPtrInfo(descr)
+ box1.set_forwarded(arrayinfo)
+ cf = self.arrayitem_cache(descr, index)
+ arrayinfo.setitem(descr, index, box1, box2, optheap=self, cf=cf)
+
dispatch_opt = make_dispatcher_method(OptHeap, 'optimize_',
default=OptHeap.emit)
diff --git a/rpython/jit/metainterp/test/test_bridgeopt.py
b/rpython/jit/metainterp/test/test_bridgeopt.py
--- a/rpython/jit/metainterp/test/test_bridgeopt.py
+++ b/rpython/jit/metainterp/test/test_bridgeopt.py
@@ -61,7 +61,7 @@
serialize_optimizer_knowledge(optimizer, numb_state, liveboxes, {}, None)
- assert unpack_numbering(numb_state.create_numbering()) == [1, 0b010000, 0]
+ assert unpack_numbering(numb_state.create_numbering()) == [1, 0b010000, 0,
0]
rbox1 = InputArgRef()
rbox2 = InputArgRef()
@@ -97,7 +97,7 @@
serialize_optimizer_knowledge(optimizer, numb_state, liveboxes, {}, None)
- assert len(numb_state.create_numbering().code) == 2 +
math.ceil(len(refboxes) / 6.0)
+ assert len(numb_state.create_numbering().code) == 3 +
math.ceil(len(refboxes) / 6.0)
dct = {box: cls
for box, known_class in boxes_known_classes
@@ -143,11 +143,7 @@
def test_bridge_field_read(self):
myjitdriver = jit.JitDriver(greens=[], reds=['y', 'res', 'n', 'a'])
class A(object):
- def f(self):
- return 1
- class B(A):
- def f(self):
- return 2
+ pass
class M(object):
_immutable_fields_ = ['x']
def __init__(self, x):
@@ -156,14 +152,12 @@
m1 = M(1)
m2 = M(2)
def f(x, y, n):
+ a = A()
+ a.n = n
if x:
- a = A()
a.m = m1
- a.n = n
else:
- a = B()
a.m = m2
- a.n = n
a.x = 0
res = 0
while y > 0:
@@ -186,3 +180,105 @@
self.check_resops(getfield_gc_i=4) # 3x a.x, 1x a.n
self.check_resops(getfield_gc_r=1) # in main loop
+ def test_bridge_field_read_constants(self):
+ myjitdriver = jit.JitDriver(greens=[], reds=['y', 'res', 'n'])
+ class A(object):
+ pass
+ class M(object):
+ _immutable_fields_ = ['x']
+ def __init__(self, x):
+ self.x = x
+
+ m1 = M(1)
+ m2 = M(2)
+ a = A()
+ a.m = m1
+ a.n = 0
+ def f(x, y, n):
+ if x:
+ a.m = m1
+ a.n = n
+ else:
+ a.m = m2
+ a.n = n
+ a.x = 0
+ res = 0
+ while y > 0:
+ myjitdriver.jit_merge_point(y=y, n=n, res=res)
+ n1 = a.n
+ m = jit.promote(a.m)
+ res += m.x
+ a.x += 1
+ if y > n:
+ res += 1
+ m = jit.promote(a.m)
+ res += m.x
+ res += n1 + a.n
+ y -= 1
+ return res
+ res = self.meta_interp(f, [6, 32, 16])
+ assert res == f(6, 32, 16)
+ self.check_trace_count(3)
+ self.check_resops(guard_value=1)
+ self.check_resops(getfield_gc_i=4) # 3x a.x, 1x a.n
+ self.check_resops(getfield_gc_r=1) # in main loop
+
+ def test_bridge_array_read(self):
+ myjitdriver = jit.JitDriver(greens=[], reds=['y', 'res', 'n', 'a'])
+ def f(x, y, n):
+ if x:
+ a = [1, n, 0]
+ else:
+ a = [2, n, 0]
+ res = 0
+ while y > 0:
+ myjitdriver.jit_merge_point(y=y, n=n, res=res, a=a)
+ n1 = a[1]
+ m = jit.promote(a[0])
+ res += m
+ a[2] += 1
+ if y > n:
+ res += 1
+ m = jit.promote(a[0])
+ res += m
+ res += n1 + a[1]
+ y -= 1
+ return res
+ res = self.meta_interp(f, [6, 32, 16])
+ assert res == f(6, 32, 16)
+ self.check_trace_count(3)
+ self.check_resops(guard_value=1)
+ self.check_resops(getarrayitem_gc_i=4)
+
+ def test_bridge_array_read_constant(self):
+ myjitdriver = jit.JitDriver(greens=[], reds=['y', 'res', 'n'])
+ class A(object):
+ pass
+ a = A()
+ a.l = [1, -65, 0]
+ def f(x, y, n):
+ if x:
+ a.l[0] = 1
+ else:
+ a.l[0] = 2
+ a.l[1] = n
+ a.l[2] = 0
+ res = 0
+ while y > 0:
+ myjitdriver.jit_merge_point(y=y, n=n, res=res)
+ n1 = a.l[1]
+ m = jit.promote(a.l[0])
+ res += m
+ a.l[2] += 1
+ if y > n:
+ res += 1
+ m = jit.promote(a.l[0])
+ res += m
+ res += n1 + a.l[1]
+ y -= 1
+ return res
+ res = self.meta_interp(f, [6, 32, 16])
+ assert res == f(6, 32, 16)
+ self.check_trace_count(3)
+ self.check_resops(guard_value=1)
+ self.check_resops(getarrayitem_gc_i=5)
diff --git a/rpython/rlib/rpath.py b/rpython/rlib/rpath.py
--- a/rpython/rlib/rpath.py
+++ b/rpython/rlib/rpath.py
@@ -5,6 +5,7 @@
import os, stat
from rpython.rlib import rposix
from rpython.rlib.signature import signature
+from rpython.rlib.rstring import assert_str0
from rpython.annotator.model import s_Str0
@@ -31,9 +32,11 @@
"""Test whether a path is absolute"""
return s.startswith('/')
+@signature(s_Str0, returns=s_Str0)
def _posix_rnormpath(path):
"""Normalize path, eliminating double slashes, etc."""
slash, dot = '/', '.'
+ assert_str0(dot)
if path == '':
return dot
initial_slashes = path.startswith('/')
@@ -56,6 +59,7 @@
path = slash.join(comps)
if initial_slashes:
path = slash*initial_slashes + path
+ assert_str0(path)
return path or dot
@signature(s_Str0, returns=s_Str0)
@@ -66,6 +70,7 @@
if not _posix_risabs(path):
cwd = os.getcwd()
path = _posix_rjoin(cwd, path)
+ assert path is not None
return _posix_rnormpath(path)
except OSError:
return path
diff --git a/rpython/rlib/rvmprof/cintf.py b/rpython/rlib/rvmprof/cintf.py
--- a/rpython/rlib/rvmprof/cintf.py
+++ b/rpython/rlib/rvmprof/cintf.py
@@ -18,7 +18,7 @@
SHARED = SRC.join('shared')
BACKTRACE = SHARED.join('libbacktrace')
-compile_extra = ['-DRPYTHON_VMPROF', '-O3']
+compile_extra = ['-DRPYTHON_VMPROF']
separate_module_files = [
SHARED.join('symboltable.c'),
SHARED.join('vmprof_unix.c')
diff --git a/rpython/rtyper/lltypesystem/lltype.py
b/rpython/rtyper/lltypesystem/lltype.py
--- a/rpython/rtyper/lltypesystem/lltype.py
+++ b/rpython/rtyper/lltypesystem/lltype.py
@@ -2208,7 +2208,7 @@
return _ptr(Ptr(T), o, solid)
@analyzer_for(malloc)
-def ann_malloc(s_T, s_n=None, s_flavor=None, s_zero=None,
+def ann_malloc(s_T, s_n=None, s_flavor=None, s_immortal=None, s_zero=None,
s_track_allocation=None, s_add_memory_pressure=None,
s_nonmovable=None):
assert (s_n is None or s_n.knowntype == int
diff --git a/rpython/rtyper/rbuiltin.py b/rpython/rtyper/rbuiltin.py
--- a/rpython/rtyper/rbuiltin.py
+++ b/rpython/rtyper/rbuiltin.py
@@ -347,19 +347,20 @@
# annotation of low-level types
@typer_for(lltype.malloc)
-def rtype_malloc(hop, i_flavor=None, i_zero=None, i_track_allocation=None,
- i_add_memory_pressure=None, i_nonmovable=None):
+def rtype_malloc(hop, i_flavor=None, i_immortal=None, i_zero=None,
+ i_track_allocation=None, i_add_memory_pressure=None,
i_nonmovable=None):
assert hop.args_s[0].is_constant()
vlist = [hop.inputarg(lltype.Void, arg=0)]
opname = 'malloc'
kwds_v = parse_kwds(
hop,
(i_flavor, lltype.Void),
+ (i_immortal, None),
(i_zero, None),
(i_track_allocation, None),
(i_add_memory_pressure, None),
(i_nonmovable, None))
- (v_flavor, v_zero, v_track_allocation,
+ (v_flavor, v_immortal, v_zero, v_track_allocation,
v_add_memory_pressure, v_nonmovable) = kwds_v
flags = {'flavor': 'gc'}
if v_flavor is not None:
diff --git a/rpython/rtyper/tool/rffi_platform.py
b/rpython/rtyper/tool/rffi_platform.py
--- a/rpython/rtyper/tool/rffi_platform.py
+++ b/rpython/rtyper/tool/rffi_platform.py
@@ -545,7 +545,7 @@
def question(self, ask_gcc):
try:
- ask_gcc(self.name + ';')
+ ask_gcc('(void)' + self.name + ';')
return True
except CompilationError:
return False
diff --git a/rpython/translator/c/src/signals.c
b/rpython/translator/c/src/signals.c
--- a/rpython/translator/c/src/signals.c
+++ b/rpython/translator/c/src/signals.c
@@ -31,11 +31,11 @@
# endif
#endif
+#define N_LONGBITS (8 * sizeof(long))
+#define N_LONGSIG ((NSIG - 1) / N_LONGBITS + 1)
+
struct pypysig_long_struct pypysig_counter = {0};
-static char volatile pypysig_flags[NSIG] = {0};
-static int volatile pypysig_occurred = 0;
-/* pypysig_occurred is only an optimization: it tells if any
- pypysig_flags could be set. */
+static long volatile pypysig_flags_bits[N_LONGSIG];
static int wakeup_fd = -1;
static int wakeup_with_nul_byte = 1;
@@ -73,12 +73,28 @@
#endif
}
+#ifdef _WIN32
+#define atomic_cas(ptr, oldv, newv) (InterlockedCompareExchange(ptr, \
+ newv, oldv) == (oldv))
+#else
+#define atomic_cas(ptr, oldv, newv) __sync_bool_compare_and_swap(ptr, \
+ oldv, newv)
+#endif
+
void pypysig_pushback(int signum)
{
if (0 <= signum && signum < NSIG)
{
- pypysig_flags[signum] = 1;
- pypysig_occurred = 1;
+ int ok, index = signum / N_LONGBITS;
+ unsigned long bitmask = 1UL << (signum % N_LONGBITS);
+ do
+ {
+ long value = pypysig_flags_bits[index];
+ if (value & bitmask)
+ break; /* already set */
+ ok = atomic_cas(&pypysig_flags_bits[index], value, value |
bitmask);
+ } while (!ok);
+
pypysig_counter.value = -1;
}
}
@@ -161,19 +177,22 @@
int pypysig_poll(void)
{
- if (pypysig_occurred)
- {
- int i;
- pypysig_occurred = 0;
- for (i=0; i<NSIG; i++)
- if (pypysig_flags[i])
- {
- pypysig_flags[i] = 0;
- pypysig_occurred = 1; /* maybe another signal is pending */
- return i;
- }
+ int index;
+ for (index = 0; index < N_LONGSIG; index++) {
+ long value;
+ retry:
+ value = pypysig_flags_bits[index];
+ if (value != 0L) {
+ int j = 0;
+ while ((value & (1UL << j)) == 0)
+ j++;
+ if (!atomic_cas(&pypysig_flags_bits[index], value,
+ value & ~(1UL << j)))
+ goto retry;
+ return index * N_LONGBITS + j;
+ }
}
- return -1; /* no pending signal */
+ return -1; /* no pending signal */
}
int pypysig_set_wakeup_fd(int fd, int with_nul_byte)
diff --git a/rpython/translator/platform/__init__.py
b/rpython/translator/platform/__init__.py
--- a/rpython/translator/platform/__init__.py
+++ b/rpython/translator/platform/__init__.py
@@ -129,7 +129,7 @@
# some helpers which seem to be cross-platform enough
def _execute_c_compiler(self, cc, args, outname, cwd=None):
- log.execute(cc + ' ' + ' '.join(args))
+ #log.execute(cc + ' ' + ' '.join(args))
# 'cc' can also contain some options for the C compiler;
# e.g. it can be "gcc -m32". We handle it by splitting on ' '.
cclist = cc.split()
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit