Author: Richard Plangger <planri...@gmail.com> Branch: py3.5-ssl Changeset: r88788:c75ec5f968ae Date: 2016-12-01 12:51 +0100 http://bitbucket.org/pypy/pypy/changeset/c75ec5f968ae/
Log: merge py3.5 diff --git a/lib-python/3/distutils/sysconfig_pypy.py b/lib-python/3/distutils/sysconfig_pypy.py --- a/lib-python/3/distutils/sysconfig_pypy.py +++ b/lib-python/3/distutils/sysconfig_pypy.py @@ -60,6 +60,8 @@ def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" + so_list = [s[0] for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION] + so_ext = (so_list or ['.so'])[0] g = {} g['CC'] = "gcc -pthread" g['CXX'] = "g++ -pthread" @@ -67,7 +69,7 @@ g['CFLAGS'] = "-DNDEBUG -O2" g['CCSHARED'] = "-fPIC" g['LDSHARED'] = "gcc -pthread -shared" - g['SO'] = [s[0] for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION][0] + g['SO'] = so_ext g['SHLIB_SUFFIX'] = g['SO'] g['AR'] = "ar" g['ARFLAGS'] = "rc" diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -192,6 +192,14 @@ assert self._finalize_.im_func is not W_Root._finalize_.im_func space.finalizer_queue.register_finalizer(self) + def may_unregister_rpython_finalizer(self, space): + """Optimization hint only: if there is no user-defined __del__() + method, pass the hint ``don't call any finalizer'' to rgc. + """ + if not self.getclass(space).hasuserdel: + from rpython.rlib import rgc + rgc.may_ignore_finalizer(self) + # hooks that the mapdict implementations needs: def _get_mapdict_map(self): return None diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py --- a/pypy/interpreter/generator.py +++ b/pypy/interpreter/generator.py @@ -3,7 +3,7 @@ from pypy.interpreter.pyopcode import LoopBlock, SApplicationException, Yield from pypy.interpreter.pycode import CO_YIELD_INSIDE_TRY from pypy.interpreter.astcompiler import consts -from rpython.rlib import jit +from rpython.rlib import jit, rgc from rpython.rlib.objectmodel import specialize from rpython.rlib.rarithmetic import r_uint @@ -20,7 +20,7 @@ self.running = False self._name = name # may be null, use get_name() self._qualname = qualname # may be null, use get_qualname() - if (isinstance(self, Coroutine) # XXX would be cool not to need this + if (isinstance(self, Coroutine) or self.pycode.co_flags & CO_YIELD_INSIDE_TRY): self.register_finalizer(self.space) self.saved_operr = None @@ -89,7 +89,7 @@ # if the frame is now marked as finished, it was RETURNed from if frame.frame_finished_execution: - self.frame = None + self.frame_is_finished() if space.is_w(w_result, space.w_None): raise OperationError(space.w_StopIteration, space.w_None) else: @@ -107,6 +107,14 @@ if self.saved_operr is not None: ec.set_sys_exc_info(self.saved_operr) self.saved_operr = None + # + # Optimization only: after we've started a Coroutine without + # CO_YIELD_INSIDE_TRY, then Coroutine._finalize_() will be a no-op + if (isinstance(self, Coroutine) + and frame.last_instr == -1 + and not (self.pycode.co_flags & CO_YIELD_INSIDE_TRY)): + rgc.may_ignore_finalizer(self) + # self.running = True try: w_result = frame.execute_frame(self, w_arg_or_err) @@ -116,7 +124,7 @@ if e.match(space, space.w_StopIteration): self._leak_stopiteration(e) finally: - self.frame = None + self.frame_is_finished() raise finally: frame.f_backref = jit.vref_None @@ -323,6 +331,10 @@ break block = block.previous + def frame_is_finished(self): + self.frame = None + rgc.may_ignore_finalizer(self) + class GeneratorIterator(GeneratorOrCoroutine): "An iterator created by a generator." @@ -364,7 +376,7 @@ break # if the frame is now marked as finished, it was RETURNed from if frame.frame_finished_execution: - self.frame = None + self.frame_is_finished() break results.append(w_result) # YIELDed return unpack_into diff --git a/pypy/interpreter/module.py b/pypy/interpreter/module.py --- a/pypy/interpreter/module.py +++ b/pypy/interpreter/module.py @@ -121,31 +121,8 @@ return space.newtuple(tup_return) def descr_module__repr__(self, space): - w_loader = space.finditem(self.w_dict, space.wrap('__loader__')) - if w_loader is not None: - try: - return space.call_method(w_loader, "module_repr", self) - except OperationError: - pass - try: - w_name = space.getattr(self, space.wrap('__name__')) - name = space.unicode_w(space.repr(w_name)) - except OperationError: - name = u"'?'" - - try: - w___file__ = space.getattr(self, space.wrap('__file__')) - except OperationError: - w___file__ = space.w_None - if not space.isinstance_w(w___file__, space.w_unicode): - if w_loader is not None: - w_loader_repr = space.unicode_w(space.repr(w_loader)) - return space.wrap(u"<module %s (%s)>" % (name, w_loader_repr)) - else: - return space.wrap(u"<module %s>" % (name,)) - else: - __file__ = space.unicode_w(space.repr(w___file__)) - return space.wrap(u"<module %s from %s>" % (name, __file__)) + w_importlib = space.getbuiltinmodule('_frozen_importlib') + return space.call_method(w_importlib, "_module_repr", self) def descr_getattribute(self, space, w_attr): from pypy.objspace.descroperation import object_getattribute diff --git a/pypy/interpreter/test/test_module.py b/pypy/interpreter/test/test_module.py --- a/pypy/interpreter/test/test_module.py +++ b/pypy/interpreter/test/test_module.py @@ -129,6 +129,20 @@ expected_repr = "<module 'test_module' ({})>".format(loader_repr) assert mod_repr == expected_repr + def test_repr_with_loader_with_raising_module_repr2(self): + import sys + test_module = type(sys)("test_module", "doc") + # If an exception occurs in module_repr(), the exception is caught + # and discarded, and the calculation of the module’s repr continues + # as if module_repr() did not exist. + class CustomLoaderWithRaisingRepr: + @classmethod + def module_repr(cls, module): + raise KeyboardInterrupt + + test_module.__loader__ = CustomLoaderWithRaisingRepr + raises(KeyboardInterrupt, 'repr(test_module)') + def test_repr_with_raising_loader_and___file__(self): import sys test_module = type(sys)("test_module", "doc") diff --git a/pypy/module/__builtin__/descriptor.py b/pypy/module/__builtin__/descriptor.py --- a/pypy/module/__builtin__/descriptor.py +++ b/pypy/module/__builtin__/descriptor.py @@ -1,3 +1,4 @@ +from rpython.rlib import jit from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.gateway import WrappedDefault, interp2app, unwrap_spec @@ -67,29 +68,43 @@ # fallback to object.__getattribute__() return space.call_function(object_getattribute(space), self, w_name) -def _super_from_frame(space, frame): - """super() without args -- fill in from __class__ and first local - variable on the stack. - """ - code = frame.pycode - if not code: - raise oefmt(space.w_RuntimeError, "super(): no code object") +@jit.elidable +def _get_self_location(space, code): if code.co_argcount == 0: raise oefmt(space.w_RuntimeError, "super(): no arguments") - w_obj = frame.locals_cells_stack_w[0] - if not w_obj: - raise oefmt(space.w_RuntimeError, "super(): arg[0] deleted") + args_to_copy = code._args_as_cellvars + for i in range(len(args_to_copy)): + if args_to_copy[i] == 0: + self_cell = i + break + else: + self_cell = -1 for index, name in enumerate(code.co_freevars): if name == '__class__': break else: raise oefmt(space.w_RuntimeError, "super(): __class__ cell not found") + class_cell = len(code.co_cellvars) + index + return self_cell, class_cell + +def _super_from_frame(space, frame): + """super() without args -- fill in from __class__ and first local + variable on the stack. + """ + if frame is None: + raise oefmt(space.w_RuntimeError, "super(): no frame object") + self_cell, class_cell = _get_self_location(space, frame.getcode()) + if self_cell < 0: + w_obj = frame.locals_cells_stack_w[0] + else: + w_obj = frame._getcell(self_cell).w_value + if not w_obj: + raise oefmt(space.w_RuntimeError, "super(): arg[0] deleted") + # a kind of LOAD_DEREF - cell = frame._getcell(len(code.co_cellvars) + index) - try: - w_starttype = cell.get() - except ValueError: + w_starttype = frame._getcell(class_cell).w_value + if w_starttype is None: raise oefmt(space.w_RuntimeError, "super(): empty __class__ cell") return w_starttype, w_obj diff --git a/pypy/module/__builtin__/test/test_descriptor.py b/pypy/module/__builtin__/test/test_descriptor.py --- a/pypy/module/__builtin__/test/test_descriptor.py +++ b/pypy/module/__builtin__/test/test_descriptor.py @@ -508,4 +508,15 @@ del __class__ super() raises(RuntimeError, X().f) + class X: + def f(self): + def g(): + print(self) # make 'self' a closure inside 'f' + del self + super() + raises(RuntimeError, X().f) + class X: + def f(*args): + super() + raises(RuntimeError, X().f) """ diff --git a/pypy/module/_cffi_backend/cdataobj.py b/pypy/module/_cffi_backend/cdataobj.py --- a/pypy/module/_cffi_backend/cdataobj.py +++ b/pypy/module/_cffi_backend/cdataobj.py @@ -397,7 +397,7 @@ space = self.space if space.is_none(w_destructor): if isinstance(self, W_CDataGCP): - self.w_destructor = None + self.detach_destructor() return space.w_None raise oefmt(space.w_TypeError, "Can remove destructor only on a object " @@ -604,6 +604,10 @@ self.w_destructor = None self.space.call_function(w_destructor, self.w_original_cdata) + def detach_destructor(self): + self.w_destructor = None + self.may_unregister_rpython_finalizer(self.space) + W_CData.typedef = TypeDef( '_cffi_backend.CData', diff --git a/pypy/module/_cffi_backend/cdlopen.py b/pypy/module/_cffi_backend/cdlopen.py --- a/pypy/module/_cffi_backend/cdlopen.py +++ b/pypy/module/_cffi_backend/cdlopen.py @@ -55,6 +55,7 @@ if not libhandle: raise oefmt(self.ffi.w_FFIError, "library '%s' is already closed", self.libname) + self.may_unregister_rpython_finalizer(self.ffi.space) # Clear the dict to force further accesses to do cdlopen_fetch() # again, and fail because the library was closed. Note that the diff --git a/pypy/module/_io/interp_bufferedio.py b/pypy/module/_io/interp_bufferedio.py --- a/pypy/module/_io/interp_bufferedio.py +++ b/pypy/module/_io/interp_bufferedio.py @@ -63,15 +63,47 @@ raise NotImplementedError def read_w(self, space, w_size=None): + """Read and return up to n bytes. + +If the argument is omitted, None, or negative, reads and +returns all data until EOF. + +If the argument is positive, and the underlying raw stream is +not 'interactive', multiple raw reads may be issued to satisfy +the byte count (unless EOF is reached first). But for +interactive raw streams (as well as sockets and pipes), at most +one raw read will be issued, and a short result does not imply +that EOF is imminent. + +Returns an empty bytes object on EOF. + +Returns None if the underlying raw stream was open in non-blocking +mode and no data is available at the moment.""" self._unsupportedoperation(space, "read") def read1_w(self, space, w_size): + """Read and return up to n bytes, with at most one read() call +to the underlying raw stream. A short result does not imply +that EOF is imminent. + +Returns an empty bytes object on EOF.""" self._unsupportedoperation(space, "read1") def write_w(self, space, w_data): + """Write the given buffer to the IO stream. + +Returns the number of bytes written, which is always the length of b +in bytes. + +Raises BlockingIOError if the buffer is full and the +underlying raw stream cannot accept more data at the moment.""" self._unsupportedoperation(space, "write") def detach_w(self, space): + """Disconnect this buffer from its underlying raw stream and return it. + +After the raw stream has been detached, the buffer is in an unusable +state.""" self._unsupportedoperation(space, "detach") def readinto_w(self, space, w_buffer): @@ -92,6 +124,20 @@ W_BufferedIOBase.typedef = TypeDef( '_io._BufferedIOBase', W_IOBase.typedef, + __doc__="""Base class for buffered IO objects. + +The main difference with RawIOBase is that the read() method +supports omitting the size argument, and does not have a default +implementation that defers to readinto(). + +In addition, read(), readinto() and write() may raise +BlockingIOError if the underlying raw stream is in non-blocking +mode and not ready; unlike their raw counterparts, they will never +return None. + +A typical implementation should not inherit from a RawIOBase +implementation, but wrap one. +""", __new__ = generic_new_descr(W_BufferedIOBase), read = interp2app(W_BufferedIOBase.read_w), read1 = interp2app(W_BufferedIOBase.read1_w), @@ -112,6 +158,9 @@ def getlength(self): return self.length + def getitem(self, index): + return self.buf[index] + def setitem(self, index, char): self.buf[self.start + index] = char diff --git a/pypy/module/_io/test/test_bufferedio.py b/pypy/module/_io/test/test_bufferedio.py --- a/pypy/module/_io/test/test_bufferedio.py +++ b/pypy/module/_io/test/test_bufferedio.py @@ -65,6 +65,23 @@ bufio = _io.BufferedReader(MockIO()) assert bufio.read(9000) == b"abcdefg" + def test_valid_buffer(self): + import _io + + class MockIO(_io._IOBase): + def readable(self): + return True + + def readinto(self, buf): + # Check that `buf` is a valid memoryview object + assert buf.itemsize == 1 + assert buf.strides == (1,) + assert buf.shape == (len(buf),) + return len(bytes(buf)) + + bufio = _io.BufferedReader(MockIO()) + assert len(bufio.read(5)) == 5 # Note: PyPy zeros the buffer, CPython does not + def test_buffering(self): import _io data = b"abcdefghi" @@ -695,7 +712,7 @@ expected[j] = 2 expected[i] = 1 assert raw.getvalue() == expected - + def test_interleaved_read_write(self): import _io as io # Test for issue #12213 diff --git a/pypy/module/_socket/interp_socket.py b/pypy/module/_socket/interp_socket.py --- a/pypy/module/_socket/interp_socket.py +++ b/pypy/module/_socket/interp_socket.py @@ -216,7 +216,7 @@ def descr_repr(self, space): fd = intmask(self.sock.fd) # Force to signed type even on Windows. return space.wrap("<socket object, fd=%d, family=%d," - " type=%d, protocol=%d>" % + " type=%d, proto=%d>" % (fd, self.sock.family, self.sock.type, self.sock.proto)) @@ -266,6 +266,7 @@ except SocketError: # cpython doesn't return any errors on close pass + self.may_unregister_rpython_finalizer(space) def connect_w(self, space, w_addr): """connect(address) diff --git a/pypy/module/_socket/test/test_sock_app.py b/pypy/module/_socket/test/test_sock_app.py --- a/pypy/module/_socket/test/test_sock_app.py +++ b/pypy/module/_socket/test/test_sock_app.py @@ -373,12 +373,12 @@ import _socket s = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM) try: - expected = ('<socket object, fd=%s, family=%s, type=%s, protocol=%s>' + expected = ('<socket object, fd=%s, family=%s, type=%s, proto=%s>' % (s.fileno(), s.family, s.type, s.proto)) assert repr(s) == expected finally: s.close() - expected = ('<socket object, fd=-1, family=%s, type=%s, protocol=%s>' + expected = ('<socket object, fd=-1, family=%s, type=%s, proto=%s>' % (s.family, s.type, s.proto)) assert repr(s) == expected diff --git a/pypy/module/_weakref/interp__weakref.py b/pypy/module/_weakref/interp__weakref.py --- a/pypy/module/_weakref/interp__weakref.py +++ b/pypy/module/_weakref/interp__weakref.py @@ -218,7 +218,7 @@ return self.space.w_None return w_obj - def descr__eq__(self, space, w_ref2): + def compare(self, space, w_ref2, invert): if not isinstance(w_ref2, W_Weakref): return space.w_NotImplemented ref1 = self @@ -226,11 +226,18 @@ w_obj1 = ref1.dereference() w_obj2 = ref2.dereference() if w_obj1 is None or w_obj2 is None: - return space.is_(ref1, ref2) - return space.eq(w_obj1, w_obj2) + w_res = space.is_(ref1, ref2) + else: + w_res = space.eq(w_obj1, w_obj2) + if invert: + w_res = space.not_(w_res) + return w_res + + def descr__eq__(self, space, w_ref2): + return self.compare(space, w_ref2, invert=False) def descr__ne__(self, space, w_ref2): - return space.not_(space.eq(self, w_ref2)) + return self.compare(space, w_ref2, invert=True) def descr_callback(self, space): return self.w_callable diff --git a/pypy/module/_weakref/test/test_weakref.py b/pypy/module/_weakref/test/test_weakref.py --- a/pypy/module/_weakref/test/test_weakref.py +++ b/pypy/module/_weakref/test/test_weakref.py @@ -153,6 +153,14 @@ assert not (ref1 == []) assert ref1 != [] + def test_ne(self): + import _weakref + class X(object): + pass + ref1 = _weakref.ref(X()) + assert ref1.__eq__(X()) is NotImplemented + assert ref1.__ne__(X()) is NotImplemented + def test_getweakrefs(self): import _weakref, gc class A(object): diff --git a/pypy/module/pypyjit/test_pypy_c/test_ffi.py b/pypy/module/pypyjit/test_pypy_c/test_ffi.py --- a/pypy/module/pypyjit/test_pypy_c/test_ffi.py +++ b/pypy/module/pypyjit/test_pypy_c/test_ffi.py @@ -272,11 +272,11 @@ _write = libc.load_function(BWrite, 'write') i = 0 fd0, fd1 = os.pipe() - buffer = _cffi_backend.newp(BCharP, 'A') + buffer = _cffi_backend.newp(BCharP, b'A') while i < 300: tmp = _write(fd1, buffer, 1) # ID: cfficall assert tmp == 1 - assert os.read(fd0, 2) == 'A' + assert os.read(fd0, 2) == b'A' i += 1 os.close(fd0) os.close(fd1) @@ -410,7 +410,7 @@ i161 = int_lt(i160, i43) guard_true(i161, descr=...) i162 = int_add(i160, 1) - setfield_gc(p22, i162, descr=<FieldS pypy.module.__builtin__.functional.W_XRangeIterator.inst_current .>) + setfield_gc(p22, i162, descr=<FieldS pypy.module.__builtin__.functional.W_IntRangeIterator.inst_current .>) guard_not_invalidated(descr=...) p163 = force_token() p164 = force_token() diff --git a/pypy/module/pypyjit/test_pypy_c/test_globals.py b/pypy/module/pypyjit/test_pypy_c/test_globals.py --- a/pypy/module/pypyjit/test_pypy_c/test_globals.py +++ b/pypy/module/pypyjit/test_pypy_c/test_globals.py @@ -16,5 +16,6 @@ assert log.result == 500 loop, = log.loops_by_filename(self.filepath) assert loop.match_by_id("loadglobal", """ + p1 = getfield_gc_r(..., descr=...) # dead guard_not_invalidated(descr=...) """) diff --git a/pypy/module/pypyjit/test_pypy_c/test_instance.py b/pypy/module/pypyjit/test_pypy_c/test_instance.py --- a/pypy/module/pypyjit/test_pypy_c/test_instance.py +++ b/pypy/module/pypyjit/test_pypy_c/test_instance.py @@ -320,3 +320,36 @@ --TICK-- jump(..., descr=...) """) + + def test_super_no_args(self): + def main(): + class A(object): + def m(self, x): + return x + 1 + class B(A): + def m(self, x): + return super().m(x) + i = 0 + while i < 300: + i = B().m(i) + return i + + log = self.run(main, []) + loop, = log.loops_by_filename(self.filepath) + assert loop.match(""" + i78 = int_lt(i72, 300) + guard_true(i78, descr=...) + guard_not_invalidated(descr=...) + p1 = force_token() + p65 = force_token() + p3 = force_token() + i81 = int_add(i72, 1) + + # can't use TICK here, because of the extra setfield_gc + ticker0 = getfield_raw_i(#, descr=<FieldS pypysig_long_struct.c_value .*>) + setfield_gc(p0, p65, descr=<FieldP pypy.interpreter.pyframe.PyFrame.vable_token .>) + ticker_cond0 = int_lt(ticker0, 0) + guard_false(ticker_cond0, descr=...) + + jump(..., descr=...) + """) diff --git a/pypy/module/pypyjit/test_pypy_c/test_min_max.py b/pypy/module/pypyjit/test_pypy_c/test_min_max.py --- a/pypy/module/pypyjit/test_pypy_c/test_min_max.py +++ b/pypy/module/pypyjit/test_pypy_c/test_min_max.py @@ -38,7 +38,7 @@ loop, = log.loops_by_filename(self.filepath) assert loop.match(""" ... - p76 = call_assembler_r(_, _, _, _, descr=...) + p76 = call_assembler_r(..., descr=...) ... """) loop2 = log.loops[0] @@ -79,6 +79,6 @@ assert len(guards) < 20 assert loop.match(""" ... - p76 = call_assembler_r(_, _, _, _, descr=...) + p76 = call_assembler_r(..., 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 @@ -247,6 +247,13 @@ """) def test_dont_trace_every_iteration(self): + def reference(a, b): + i = sa = 0 + while i < 300: + sa += a % b + i += 1 + return sa + # def main(a, b): i = sa = 0 while i < 300: @@ -258,9 +265,12 @@ i += 1 return sa # + log_ref = self.run(reference, [10, 20]) + assert log_ref.result == 300 * (10 % 20) + # log = self.run(main, [10, 20]) assert log.result == 300 * (10 % 20) - assert log.jit_summary.tracing_no == 1 + assert log.jit_summary.tracing_no == log_ref.jit_summary.tracing_no loop, = log.loops_by_filename(self.filepath) assert loop.match(""" i11 = int_lt(i7, 300) @@ -274,7 +284,7 @@ # log = self.run(main, [-10, -20]) assert log.result == 300 * (-10 % -20) - assert log.jit_summary.tracing_no == 1 + assert log.jit_summary.tracing_no == log_ref.jit_summary.tracing_no def test_overflow_checking(self): """ @@ -297,6 +307,7 @@ self.run_and_check(main, []) def test_global(self): + # check that the global read is removed even from the entry bridge log = self.run(""" i = 0 globalinc = 1 @@ -308,7 +319,10 @@ """, [1000]) loop, = log.loops_by_id("globalread", is_entry_bridge=True) - assert len(loop.ops_by_id("globalread")) == 0 + assert loop.match_by_id("globalread", """ + # only a dead read + p26 = getfield_gc_r(ConstPtr(ptr25), descr=<FieldP pypy.objspace.std.unicodeobject.W_UnicodeObject.inst__utf8 .>) + """) def test_eval(self): def main(): @@ -349,7 +363,8 @@ def test_long_comparison(self): def main(n): while n: - 12345L > 123L # ID: long_op + x = 12345678901234567890123456 + x > 1231231231231231231231231 # ID: long_op n -= 1 log = self.run(main, [300]) diff --git a/pypy/module/pypyjit/test_pypy_c/test_shift.py b/pypy/module/pypyjit/test_pypy_c/test_shift.py --- a/pypy/module/pypyjit/test_pypy_c/test_shift.py +++ b/pypy/module/pypyjit/test_pypy_c/test_shift.py @@ -196,8 +196,7 @@ """ from sys import maxint - def main(a, b, c): - from sys import maxint + def main(a, b, c, maxint): i = sa = 0 while i < 300: if 0 < a < 10: pass @@ -210,9 +209,9 @@ sa += (b<<100)>>100 sa += (c<<100)>>100 i += 1 - return long(sa) + return sa for a in (1, 4, 8, 100): for b in (-10, 10, -201, 201, -maxint/3, maxint/3): for c in (-10, 10, -maxint/3, maxint/3): - yield self.run_and_check, main, [a, b, c] + yield self.run_and_check, main, [a, b, c, maxint] 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 @@ -5,50 +5,102 @@ class TestString(BaseTestPyPyC): + + def test_python3_missing_bchr(self): + # Check that 'bytes([i])' is special-cased into something + # efficient, as Python 3.5 doesn't have a bchr() function or + # anything more direct. + def main(n): + i = 0 + result = b'' + while i < n: + c = bytes([i]) + result += c + i += 1 + return i + log = self.run(main, [255]) + assert log.result == 255 + loop, = log.loops_by_filename(self.filepath) + assert loop.match(""" + # nothing left like allocating a list object or doing any + # residual call + i49 = int_lt(i38, i26) + guard_true(i49, descr=...) + guard_not_invalidated(descr=...) + i51 = int_lt(i38, 256) + guard_true(i51, descr=...) + i53 = int_add(i38, 1) + --TICK-- + i58 = strlen(p46) + i60 = int_add(i58, 1) + p61 = newstr(i60) + copystrcontent(p46, p61, 0, 0, i58) + strsetitem(p61, i58, i38) + p62 = newstr(1) + strsetitem(p62, 0, i38) + jump(..., descr=...) + """) + def test_lookup_default_encoding(self): def main(n): - import string i = 0 - letters = string.letters - uletters = unicode(string.letters) + letters = b'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + uletters = u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' while i < n: - i += letters[i % len(letters)] == uletters[i % len(letters)] + c = bytes([letters[i % len(uletters)]]) + i += (c.decode() == uletters[i % len(uletters)]) return i log = self.run(main, [300], import_site=True) assert log.result == 300 loop, = log.loops_by_filename(self.filepath) assert loop.match(""" - i14 = int_lt(i6, i9) - guard_true(i14, descr=...) + i88 = int_lt(i83, i36) + guard_true(i88, descr=...) + p90 = getfield_gc_r(ConstPtr(ptr89), descr=<FieldP pypy.objspace.std.unicodeobject.W_UnicodeObject.inst__utf8 .>) guard_not_invalidated(descr=...) - i16 = int_eq(i6, %d) - i19 = call_i(ConstClass(ll_int_py_mod__Signed_Signed), i6, i10, descr=<Calli . ii EF=0 OS=14>) - i21 = int_lt(i19, 0) - guard_false(i21, descr=...) - i22 = int_ge(i19, i10) - guard_false(i22, descr=...) - i23 = strgetitem(p11, i19) - i24 = int_ge(i19, i12) - guard_false(i24, descr=...) - i25 = unicodegetitem(p13, i19) - p27 = newstr(1) - strsetitem(p27, 0, i23) - p30 = call_r(ConstClass(ll_str2unicode__rpy_stringPtr), p27, descr=...) + i92 = int_eq(i83, %d) + i94 = call_i(ConstClass(ll_int_py_mod__Signed_Signed), i83, i46, descr=<Calli . ii EF=0 OS=14>) + i96 = int_lt(i94, 0) + guard_false(i96, descr=...) + i97 = int_ge(i94, i53) + guard_false(i97, descr=...) + i98 = strgetitem(p52, i94) + p100 = getfield_gc_r(ConstPtr(ptr99), descr=<FieldP pypy.objspace.std.unicodeobject.W_UnicodeObject.inst__utf8 .>) + p101 = force_token() + p103 = newstr(1) + strsetitem(p103, 0, i98) + p104 = new(descr=<SizeDescr ..?>) + p106 = newunicode(1) + setfield_gc(p0, p101, descr=<FieldP pypy.interpreter.pyframe.PyFrame.vable_token .>) + setfield_gc(p104, p106, descr=<FieldP unicodebuilder.current_buf ..?>) + setfield_gc(p104, 0, descr=<FieldS unicodebuilder.current_pos ..?>) + setfield_gc(p104, 1, descr=<FieldS unicodebuilder.current_end ..?>) + setfield_gc(p104, 1, descr=<FieldS unicodebuilder.total_size 32>) + i113 = call_may_force_i(ConstClass(str_decode_utf_8_impl), p103, 1, ConstPtr(null), 1, 0, 0, p104, descr=<Calli . ririiir EF=7>) + guard_not_forced(descr=...) guard_no_exception(descr=...) - i32 = call_i(ConstClass(_ll_2_str_eq_checknull_char__rpy_unicodePtr_UniChar), p30, i25, descr=...) - guard_true(i32, descr=...) - i34 = int_add(i6, 1) + p116 = call_r(ConstClass(ll_build_trampoline__v1351___simple_call__function_), p104, descr=<Callr . r EF=5>) + guard_no_exception(descr=...) + guard_nonnull(p116, descr=...) + p118 = getfield_gc_r(ConstPtr(ptr117), descr=<FieldP pypy.objspace.std.unicodeobject.W_UnicodeObject.inst__utf8 .>) + guard_not_invalidated(descr=...) + i119 = int_ge(i94, i46) + guard_false(i119, descr=...) + i120 = unicodegetitem(p45, i94) + i122 = call_i(ConstClass(_ll_2_str_eq_nonnull_char__rpy_unicodePtr_UniChar), p116, i120, descr=<Calli . ri EF=0 OS=49>) + guard_true(i122, descr=...) + i124 = int_add(i83, 1) --TICK-- jump(..., descr=...) """ % (-sys.maxint-1,)) - def test_long(self): + def test_int_base_16(self): def main(n): - import string i = 1 while i < n: - i += int(long(string.digits[i % len(string.digits)], 16)) + digits = '0123456789' + i += int(digits[i % len(digits)], 16) return i log = self.run(main, [1100], import_site=True) @@ -61,7 +113,9 @@ assert loop.match(""" i11 = int_lt(i6, i7) guard_true(i11, descr=...) + p70 = getfield_gc_r(ConstPtr(ptr69), descr=<FieldP pypy.objspace.std.unicodeobject.W_UnicodeObject.inst__utf8 .>) guard_not_invalidated(descr=...) + p72 = getfield_gc_r(ConstPtr(ptr71), descr=<FieldP pypy.objspace.std.unicodeobject.W_UnicodeObject.inst__utf8 .>) i13 = int_eq(i6, %d) # value provided below # "mod 10" block: @@ -73,17 +127,20 @@ i87 = int_mul(i85, 10) i19 = int_sub(i6, i87) - i23 = strgetitem(p10, i19) - p25 = newstr(1) - strsetitem(p25, 0, i23) - p93 = call_r(ConstClass(fromstr), p25, 16, descr=<Callr . ri EF=4>) + i23 = unicodegetitem(ConstPtr(ptr92), i19) + p25 = newunicode(1) + unicodesetitem(p25, 0, i23) + p97 = call_r(ConstClass(_rpy_unicode_to_decimal_w), p25, descr=<Callr . r EF=5>) guard_no_exception(descr=...) - i95 = getfield_gc_i(p93, descr=<FieldS rpython.rlib.rbigint.rbigint.inst_size .*>) - i96 = int_gt(i95, #) - guard_false(i96, descr=...) - i94 = call_i(ConstClass(rbigint._toint_helper), p93, descr=<Calli . r EF=4>) + i98 = unicodelen(p97) + p99 = force_token() + setfield_gc(p0, p99, descr=<FieldP pypy.interpreter.pyframe.PyFrame.vable_token .>) + p104 = call_may_force_r(ConstClass(unicode_encode_utf_8_impl), p97, i98, ConstPtr(ptr101), 1, 1, descr=<Callr . ririi EF=7>) + guard_not_forced(descr=...) guard_no_exception(descr=...) - i95 = int_add_ovf(i6, i94) + i107 = call_i(ConstClass(string_to_int), p104, 16, descr=<Calli . ri EF=4>) + guard_no_exception(descr=...) + i95 = int_add_ovf(i6, i107) guard_no_overflow(descr=...) --TICK-- jump(..., descr=...) diff --git a/pypy/module/pypyjit/test_pypy_c/test_struct.py b/pypy/module/pypyjit/test_pypy_c/test_struct.py --- a/pypy/module/pypyjit/test_pypy_c/test_struct.py +++ b/pypy/module/pypyjit/test_pypy_c/test_struct.py @@ -21,7 +21,7 @@ while i < n: buf = struct.pack("<i", i) # ID: pack x = struct.unpack("<i", buf)[0] # ID: unpack - i += x / i + i += x // i return i log = self.run(main, [1000]) @@ -82,7 +82,7 @@ while i < n: buf = s.pack(-1, i) # ID: pack x = s.unpack(buf)[1] # ID: unpack - i += x / i + i += x // i return i log = self.run(main, [1000]) diff --git a/pypy/module/select/interp_epoll.py b/pypy/module/select/interp_epoll.py --- a/pypy/module/select/interp_epoll.py +++ b/pypy/module/select/interp_epoll.py @@ -81,6 +81,7 @@ class W_Epoll(W_Root): def __init__(self, space, epfd): + self.space = space self.epfd = epfd self.register_finalizer(space) @@ -113,6 +114,7 @@ if not self.get_closed(): socketclose(self.epfd) self.epfd = -1 + self.may_unregister_rpython_finalizer(self.space) def epoll_ctl(self, space, ctl, w_fd, eventmask, ignore_ebadf=False): fd = space.c_filedescriptor_w(w_fd) diff --git a/pypy/module/select/interp_kqueue.py b/pypy/module/select/interp_kqueue.py --- a/pypy/module/select/interp_kqueue.py +++ b/pypy/module/select/interp_kqueue.py @@ -109,6 +109,7 @@ class W_Kqueue(W_Root): def __init__(self, space, kqfd): + self.space = space self.kqfd = kqfd self.register_finalizer(space) @@ -137,6 +138,7 @@ kqfd = self.kqfd self.kqfd = -1 socketclose_no_errno(kqfd) + self.may_unregister_rpython_finalizer(self.space) def check_closed(self, space): if self.get_closed(): diff --git a/pypy/module/signal/test/test_signal.py b/pypy/module/signal/test/test_signal.py --- a/pypy/module/signal/test/test_signal.py +++ b/pypy/module/signal/test/test_signal.py @@ -40,23 +40,22 @@ } def setup_class(cls): - cls.w_signal = cls.space.getbuiltinmodule('_signal') cls.w_temppath = cls.space.wrap( str(py.test.ensuretemp("signal").join("foo.txt"))) cls.w_appdirect = cls.space.wrap(cls.runappdirect) def test_exported_names(self): - import os - self.signal.__dict__ # crashes if the interpleveldefs are invalid + import os, _signal + _signal.__dict__ # crashes if the interpleveldefs are invalid if os.name == 'nt': - assert self.signal.CTRL_BREAK_EVENT == 1 - assert self.signal.CTRL_C_EVENT == 0 + assert _signal.CTRL_BREAK_EVENT == 1 + assert _signal.CTRL_C_EVENT == 0 def test_basics(self): - import types, os + import types, os, _signal if not hasattr(os, 'kill') or not hasattr(os, 'getpid'): skip("requires os.kill() and os.getpid()") - signal = self.signal # the signal module to test + signal = _signal # the signal module to test if not hasattr(signal, 'SIGUSR1'): skip("requires SIGUSR1 in signal") signum = signal.SIGUSR1 diff --git a/pypy/module/sys/app.py b/pypy/module/sys/app.py --- a/pypy/module/sys/app.py +++ b/pypy/module/sys/app.py @@ -112,23 +112,35 @@ class SimpleNamespace: + """A simple attribute-based namespace. + +SimpleNamespace(**kwargs)""" + def __init__(self, **kwargs): self.__dict__.update(kwargs) - def __repr__(self, recurse=set()): + def __repr__(self): ident = id(self) - if ident in recurse: + if ident in sns_recurse: return "namespace(...)" - recurse.add(ident) + sns_recurse.add(ident) try: pairs = ('%s=%r' % item for item in sorted(self.__dict__.items())) return "namespace(%s)" % ', '.join(pairs) finally: - recurse.remove(ident) + sns_recurse.discard(ident) def __eq__(self, other): - return (isinstance(other, SimpleNamespace) and - self.__dict__ == other.__dict__) + if issubclass(type(other), SimpleNamespace): + return self.__dict__ == other.__dict__ + return NotImplemented + + def __ne__(self, other): + if issubclass(type(other), SimpleNamespace): + return self.__dict__ != other.__dict__ + return NotImplemented + +sns_recurse = set() # This class is not exposed in sys, but by the types module. SimpleNamespace.__module__ = 'types' 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 @@ -589,6 +589,35 @@ # raises(AttributeError, "del ns.spam") del ns.y + # + assert ns == SimpleNamespace(z=4, x=1, w=3) + assert (ns != SimpleNamespace(z=4, x=1, w=3)) is False + assert (ns == SimpleNamespace(z=4, x=2, w=3)) is False + assert ns != SimpleNamespace(z=4, x=2, w=3) + # + class Foo(SimpleNamespace): + pass + assert ns == Foo(z=4, x=1, w=3) + assert (ns != Foo(z=4, x=1, w=3)) is False + assert (ns == Foo(z=4, x=2, w=3)) is False + assert ns != Foo(z=4, x=2, w=3) + # + class Other: + def __init__(self, x, z, w): + self.x = x + self.z = z + self.w = w + assert (ns == Other(z=4, x=1, w=3)) is False + assert ns != Other(z=4, x=1, w=3) + assert (Foo(z=4, x=1, w=3) == Other(z=4, x=1, w=3)) is False + assert Foo(z=4, x=1, w=3) != Other(z=4, x=1, w=3) + # + class Fake: + __class__ = SimpleNamespace + assert isinstance(Fake(), SimpleNamespace) + assert not issubclass(Fake, SimpleNamespace) + assert (Fake() == SimpleNamespace()) is False + assert SimpleNamespace() != Fake() def test_pickle_simplenamespace(self): import pickle, sys diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -183,6 +183,7 @@ if mode == rzlib.Z_FINISH: # release the data structures now rzlib.deflateEnd(self.stream) self.stream = rzlib.null_stream + self.may_unregister_rpython_finalizer(space) finally: self.unlock() except rzlib.RZlibError as e: diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py --- a/pypy/objspace/descroperation.py +++ b/pypy/objspace/descroperation.py @@ -705,7 +705,7 @@ return w_result raise oefmt(space.w_TypeError, - "%(specialname)s returned non-%(targetname)s (type " + "%(specialname)s returned non-string (type " "'%%T')", w_result) assert not hasattr(DescrOperation, %(targetname)r) DescrOperation.%(targetname)s = %(targetname)s diff --git a/pypy/objspace/fake/objspace.py b/pypy/objspace/fake/objspace.py --- a/pypy/objspace/fake/objspace.py +++ b/pypy/objspace/fake/objspace.py @@ -66,15 +66,16 @@ class W_MyType(W_MyObject): name = "foobar" flag_map_or_seq = '?' + hasuserdel = False def __init__(self): self.mro_w = [w_some_obj(), w_some_obj()] self.dict_w = {'__str__': w_some_obj()} + self.hasuserdel = True def get_module(self): return w_some_obj() - def getname(self, space): return self.name.decode('utf-8') diff --git a/pypy/objspace/std/bytesobject.py b/pypy/objspace/std/bytesobject.py --- a/pypy/objspace/std/bytesobject.py +++ b/pypy/objspace/std/bytesobject.py @@ -1,6 +1,6 @@ """The builtin bytes implementation""" -from rpython.rlib.jit import we_are_jitted +from rpython.rlib import jit from rpython.rlib.objectmodel import ( compute_hash, compute_unique_id, import_from_mixin, newlist_hint, resizelist_hint, HASH_ALGORITHM) @@ -529,10 +529,22 @@ @unwrap_spec(encoding='str_or_None', errors='str_or_None') def descr_new(space, w_stringtype, w_source=None, encoding=None, errors=None): - if (w_source and space.is_w(space.type(w_source), space.w_bytes) and - space.is_w(w_stringtype, space.w_bytes) and encoding is None - and errors is None): - return w_source + if (w_source and space.is_w(w_stringtype, space.w_bytes) + and encoding is None and errors is None): + # special-case 'bytes(byte_object)' + w_srctype = space.type(w_source) + if w_srctype is space.w_bytes: + return w_source + # special-case 'bytes([single_integer])' or 'bytes((single_int,))' + # for JITted performance only, when we clearly see the + # length of the list/tuple being constant and equal to 1 + if w_srctype is space.w_list or w_srctype is space.w_tuple: + length = space.len_w(w_source) + if jit.isconstant(length) and length == 1: + w_item = space.getitem(w_source, space.wrap(0)) + value = getbytevalue(space, w_item) + return W_BytesObject(value) + # value = newbytesdata_w(space, w_source, encoding, errors) w_obj = space.allocate_instance(W_BytesObject, w_stringtype) W_BytesObject.__init__(w_obj, value) diff --git a/pypy/objspace/std/test/test_bytesobject.py b/pypy/objspace/std/test/test_bytesobject.py --- a/pypy/objspace/std/test/test_bytesobject.py +++ b/pypy/objspace/std/test/test_bytesobject.py @@ -89,6 +89,13 @@ assert self.space.listview_bytes(w_bytes) == None assert self.space.listview_int(w_bytes) == [97, 98, 99, 100] + def test_constructor_single_char(self, monkeypatch): + from rpython.rlib import jit + monkeypatch.setattr(jit, 'isconstant', lambda x: True) + space = self.space + w_res = space.call_function(space.w_bytes, space.wrap([42])) + assert space.str_w(w_res) == '*' + class AppTestBytesObject: def test_constructor(self): @@ -97,6 +104,23 @@ assert bytes(b'abc') == b'abc' assert bytes('abc', 'ascii') == b'abc' assert bytes(set(b'foo')) in (b'fo', b'of') + assert bytes([]) == b'' + assert bytes([42]) == b'*' + assert bytes([0xFC]) == b'\xFC' + assert bytes([42, 0xCC]) == b'*\xCC' + + def test_constructor_list_of_objs(self): + class X: + def __index__(self): + return 42 + class Y: + def __int__(self): + return 42 + for obj in [42, X()]: + assert bytes([obj]) == b'*' + assert bytes([obj, obj, obj]) == b'***' + raises(TypeError, bytes, [Y()]) + raises(TypeError, bytes, [Y(), Y()]) def test_fromhex(self): assert bytes.fromhex("abcd") == b'\xab\xcd' 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 @@ -1257,7 +1257,11 @@ def unicode_to_decimal_w(space, w_unistr, allow_surrogates=False): if not isinstance(w_unistr, W_UnicodeObject): raise oefmt(space.w_TypeError, "expected unicode, got '%T'", w_unistr) - unistr = w_unistr._value + value = _rpy_unicode_to_decimal_w(space, w_unistr._value) + return unicodehelper.encode_utf8(space, value, + allow_surrogates=allow_surrogates) + +def _rpy_unicode_to_decimal_w(space, unistr): result = [u'\0'] * len(unistr) for i in xrange(len(unistr)): uchr = ord(unistr[i]) @@ -1270,8 +1274,7 @@ except KeyError: pass result[i] = unichr(uchr) - return unicodehelper.encode_utf8(space, u''.join(result), - allow_surrogates=allow_surrogates) + return u''.join(result) @jit.elidable def g_encode_utf8(value): diff --git a/pypy/objspace/test/test_descroperation.py b/pypy/objspace/test/test_descroperation.py --- a/pypy/objspace/test/test_descroperation.py +++ b/pypy/objspace/test/test_descroperation.py @@ -280,7 +280,8 @@ assert operate(A()) == "hello" * n assert type(operate(A())) is str answer = 42 - raises(TypeError, operate, A()) + excinfo = raises(TypeError, operate, A()) + assert "returned non-string (type 'int')" in str(excinfo.value) def test_string_results_unicode(self): class A(object): diff --git a/pypy/tool/pytest/apptest.py b/pypy/tool/pytest/apptest.py --- a/pypy/tool/pytest/apptest.py +++ b/pypy/tool/pytest/apptest.py @@ -21,13 +21,6 @@ pypyroot = os.path.dirname(pypydir) -RENAMED_USEMODULES = dict( - _winreg='winreg', - exceptions='builtins', - struct='_struct', - thread='_thread', - operator='_operator', - ) class AppError(Exception): def __init__(self, excinfo): @@ -63,6 +56,11 @@ return repr(value) +def _rename_module(name): + mod = __import__("pypy.module." + name, globals(), locals(), ['Module']) + return mod.Module.applevel_name or name + + def run_with_python(python_, target_, usemodules, **definitions): if python_ is None: py.test.skip("Cannot find the default python3 interpreter to run with -A") @@ -133,8 +131,7 @@ check_usemodules = '' if usemodules: - usemodules = [str(RENAMED_USEMODULES.get(name, name)) - for name in usemodules] + usemodules = [_rename_module(name) for name in usemodules] check_usemodules = """\ missing = set(%r).difference(sys.builtin_module_names) if missing: diff --git a/pypy/tool/pytest/test/test_appsupport.py b/pypy/tool/pytest/test/test_appsupport.py --- a/pypy/tool/pytest/test/test_appsupport.py +++ b/pypy/tool/pytest/test/test_appsupport.py @@ -135,3 +135,12 @@ info = raises(ZeroDivisionError, "x/0") assert info.type is ZeroDivisionError assert isinstance(info.value, ZeroDivisionError) + +def test_rename_module(): + from pypy.tool.pytest.apptest import _rename_module + assert _rename_module("sys") == "sys" + if sys.platform == "win32": + assert _rename_module("_winreg") == "winreg" + assert _rename_module("struct") == "_struct" + assert _rename_module("operator") == "_operator" + assert _rename_module("signal") == "_signal" diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py --- a/rpython/memory/gc/incminimark.py +++ b/rpython/memory/gc/incminimark.py @@ -155,7 +155,10 @@ # 'old_objects_pointing_to_pinned' and doesn't have to be added again. GCFLAG_PINNED_OBJECT_PARENT_KNOWN = GCFLAG_PINNED -_GCFLAG_FIRST_UNUSED = first_gcflag << 10 # the first unused bit +# record that ignore_finalizer() has been called +GCFLAG_IGNORE_FINALIZER = first_gcflag << 10 + +_GCFLAG_FIRST_UNUSED = first_gcflag << 11 # the first unused bit # States for the incremental GC @@ -1161,6 +1164,11 @@ obj = self.get_forwarding_address(obj) return self.get_type_id(obj) + def get_possibly_forwarded_tid(self, obj): + if self.is_in_nursery(obj) and self.is_forwarded(obj): + obj = self.get_forwarding_address(obj) + return self.header(obj).tid + def get_total_memory_used(self): """Return the total memory used, not counting any object in the nursery: only objects in the ArenaCollection or raw-malloced. @@ -1672,7 +1680,7 @@ self.rrc_minor_collection_trace() # # visit the "probably young" objects with finalizers. They - # always all survive. + # all survive, except if IGNORE_FINALIZER is set. if self.probably_young_objects_with_finalizers.non_empty(): self.deal_with_young_objects_with_finalizers() # @@ -2675,6 +2683,8 @@ while self.probably_young_objects_with_finalizers.non_empty(): obj = self.probably_young_objects_with_finalizers.popleft() fq_nr = self.probably_young_objects_with_finalizers.popleft() + if self.get_possibly_forwarded_tid(obj) & GCFLAG_IGNORE_FINALIZER: + continue self.singleaddr.address[0] = obj self._trace_drag_out1(self.singleaddr) obj = self.singleaddr.address[0] @@ -2697,6 +2707,8 @@ fq_nr = self.old_objects_with_finalizers.popleft() ll_assert(self._finalization_state(x) != 1, "bad finalization state 1") + if self.header(x).tid & GCFLAG_IGNORE_FINALIZER: + continue if self.header(x).tid & GCFLAG_VISITED: new_with_finalizer.append(x) new_with_finalizer.append(fq_nr) @@ -2787,6 +2799,9 @@ self.objects_to_trace.append(obj) self.visit_all_objects() + def ignore_finalizer(self, obj): + self.header(obj).tid |= GCFLAG_IGNORE_FINALIZER + # ---------- # Weakrefs diff --git a/rpython/memory/gctransform/framework.py b/rpython/memory/gctransform/framework.py --- a/rpython/memory/gctransform/framework.py +++ b/rpython/memory/gctransform/framework.py @@ -545,6 +545,12 @@ s_gcref], annmodel.s_None) + self.ignore_finalizer_ptr = None + if hasattr(GCClass, 'ignore_finalizer'): + self.ignore_finalizer_ptr = getfn(GCClass.ignore_finalizer, + [s_gc, SomeAddress()], + annmodel.s_None) + def create_custom_trace_funcs(self, gc, rtyper): custom_trace_funcs = tuple(rtyper.custom_trace_funcs) rtyper.custom_trace_funcs = custom_trace_funcs @@ -1572,6 +1578,13 @@ hop.genop("cast_adr_to_ptr", [v_adr], resultvar = hop.spaceop.result) + def gct_gc_ignore_finalizer(self, hop): + if self.ignore_finalizer_ptr is not None: + v_adr = hop.genop("cast_ptr_to_adr", [hop.spaceop.args[0]], + resulttype=llmemory.Address) + hop.genop("direct_call", [self.ignore_finalizer_ptr, + self.c_const_gc, v_adr]) + class TransformerLayoutBuilder(gctypelayout.TypeLayoutBuilder): diff --git a/rpython/rlib/rfile.py b/rpython/rlib/rfile.py --- a/rpython/rlib/rfile.py +++ b/rpython/rlib/rfile.py @@ -4,7 +4,7 @@ """ import os, stat, errno, sys -from rpython.rlib import rposix +from rpython.rlib import rposix, rgc from rpython.rlib.objectmodel import enforceargs from rpython.rlib.rarithmetic import intmask from rpython.rlib.rstring import StringBuilder @@ -294,6 +294,7 @@ if ll_file: # double close is allowed self._ll_file = lltype.nullptr(FILEP.TO) + rgc.may_ignore_finalizer(self) do_close = self._close2[0] try: if do_close: diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py --- a/rpython/rlib/rgc.py +++ b/rpython/rlib/rgc.py @@ -527,6 +527,14 @@ hop.exception_cannot_occur() return hop.inputconst(lltype.Signed, hop.s_result.const) +@jit.dont_look_inside +@specialize.argtype(0) +def may_ignore_finalizer(obj): + """Optimization hint: says that it is valid for any finalizer + for 'obj' to be ignored, depending on the GC.""" + from rpython.rtyper.lltypesystem.lloperation import llop + llop.gc_ignore_finalizer(lltype.Void, obj) + # ____________________________________________________________ diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py --- a/rpython/rtyper/lltypesystem/lloperation.py +++ b/rpython/rtyper/lltypesystem/lloperation.py @@ -486,6 +486,7 @@ 'gc_add_memory_pressure': LLOp(), 'gc_fq_next_dead' : LLOp(), 'gc_fq_register' : LLOp(), + 'gc_ignore_finalizer' : LLOp(canrun=True), 'gc_rawrefcount_init': LLOp(), 'gc_rawrefcount_create_link_pypy': LLOp(), diff --git a/rpython/rtyper/lltypesystem/opimpl.py b/rpython/rtyper/lltypesystem/opimpl.py --- a/rpython/rtyper/lltypesystem/opimpl.py +++ b/rpython/rtyper/lltypesystem/opimpl.py @@ -736,6 +736,9 @@ assert isinstance(x, bool) return x +def op_gc_ignore_finalizer(obj): + pass + # ____________________________________________________________ def get_op_impl(opname): diff --git a/rpython/translator/c/src/mem.h b/rpython/translator/c/src/mem.h --- a/rpython/translator/c/src/mem.h +++ b/rpython/translator/c/src/mem.h @@ -152,6 +152,7 @@ #define OP_GC_IS_RPY_INSTANCE(x, r) r = 0 #define OP_GC_DUMP_RPY_HEAP(fd, r) r = 0 #define OP_GC_SET_EXTRA_THRESHOLD(x, r) /* nothing */ +#define OP_GC_IGNORE_FINALIZER(x, r) /* nothing */ /****************************/ /* The "asmgcc" root finder */ diff --git a/rpython/translator/c/test/test_boehm.py b/rpython/translator/c/test/test_boehm.py --- a/rpython/translator/c/test/test_boehm.py +++ b/rpython/translator/c/test/test_boehm.py @@ -409,7 +409,9 @@ # def fn(): for i in range(1000): - fq.register_finalizer(A(i)) + x = A(i) + fq.register_finalizer(x) + rgc.may_ignore_finalizer(x) # this is ignored with Boehm rgc.collect() rgc.collect() if glob.triggered == 0: diff --git a/rpython/translator/c/test/test_newgc.py b/rpython/translator/c/test/test_newgc.py --- a/rpython/translator/c/test/test_newgc.py +++ b/rpython/translator/c/test/test_newgc.py @@ -1705,6 +1705,38 @@ res = self.run("limited_memory_linux", -1, runner=myrunner) assert res == 42 + def define_ignore_finalizer(cls): + class X(object): + pass + class FQ(rgc.FinalizerQueue): + Class = X + def finalizer_trigger(self): + pass + queue = FQ() + def g(): + x1 = X() + x2 = X() + queue.register_finalizer(x1) + queue.register_finalizer(x2) + rgc.may_ignore_finalizer(x1) + g._dont_inline_ = True + def f(): + g() + rgc.collect() + seen = 0 + while True: + obj = queue.next_dead() + if obj is None: + break + seen += 1 + return seen + assert f() == 2 # untranslated: may_ignore_finalizer() is ignored + return f + + def test_ignore_finalizer(self): + res = self.run("ignore_finalizer") + assert res == 1 # translated: x1 is removed from the list + # ____________________________________________________________________ _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit