From pypy.commits at gmail.com Tue Oct 1 05:14:57 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 01 Oct 2019 02:14:57 -0700 (PDT) Subject: [pypy-commit] pypy record-known-result: speed up checking the result of unicode.find Message-ID: <5d931911.1c69fb81.ab189.d1b4@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: record-known-result Changeset: r97691:681aab475a24 Date: 2019-09-30 21:28 +0200 http://bitbucket.org/pypy/pypy/changeset/681aab475a24/ Log: speed up checking the result of unicode.find 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 @@ -1007,18 +1007,14 @@ if forward: res_index = self._utf8.find(w_sub._utf8, start_index, end_index) - if res_index < 0: - return None - res = self._byte_to_index(res_index) - assert res >= 0 - return space.newint(res) else: res_index = self._utf8.rfind(w_sub._utf8, start_index, end_index) - if res_index < 0: - return None - res = self._byte_to_index(res_index) - assert res >= 0 - return space.newint(res) + if res_index < 0: + return None + res = self._byte_to_index(res_index) + assert res >= 0 + jit.promote(res >= 0) # always true! + return space.newint(res) def _unwrap_and_compute_idx_params(self, space, w_start, w_end): # unwrap start and stop indices, optimized for the case where From pypy.commits at gmail.com Tue Oct 1 05:14:59 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 01 Oct 2019 02:14:59 -0700 (PDT) Subject: [pypy-commit] pypy record-known-result: another record_known_result: the content of W_UnicodeObject is known to be a valid utf8 string Message-ID: <5d931913.1c69fb81.4a5d6.3cea@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: record-known-result Changeset: r97692:298551521a23 Date: 2019-10-01 10:52 +0200 http://bitbucket.org/pypy/pypy/changeset/298551521a23/ Log: another record_known_result: the content of W_UnicodeObject is known to be a valid utf8 string 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 @@ -102,6 +102,7 @@ return space.text_w(encode_object(space, self, 'ascii', 'strict')) def utf8_w(self, space): + jit.record_known_result(self._length, rutf8._check_utf8, self._utf8, True, 0, -1) return self._utf8 def readbuf_w(self, space): @@ -1012,8 +1013,8 @@ if res_index < 0: return None res = self._byte_to_index(res_index) + jit.promote(res >= 0) # always true! assert res >= 0 - jit.promote(res >= 0) # always true! return space.newint(res) def _unwrap_and_compute_idx_params(self, space, w_start, w_end): From pypy.commits at gmail.com Tue Oct 1 15:12:21 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 01 Oct 2019 12:12:21 -0700 (PDT) Subject: [pypy-commit] pypy default: don't turn ord(u) and u.islower() etc into bridges Message-ID: <5d93a515.1c69fb81.b79f3.a300@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r97694:f0d037751b82 Date: 2019-10-01 21:02 +0200 http://bitbucket.org/pypy/pypy/changeset/f0d037751b82/ Log: don't turn ord(u) and u.islower() etc into bridges 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 @@ -41,6 +41,10 @@ def prev_codepoint_pos_dont_look_inside(utf8, p): return rutf8.prev_codepoint_pos(utf8, p) + at jit.elidable +def codepoint_at_pos_dont_look_inside(utf8, p): + return rutf8.codepoint_at_pos(utf8, p) + class W_UnicodeObject(W_Root): import_from_mixin(StringMethods) @@ -134,7 +138,7 @@ raise oefmt(space.w_TypeError, "ord() expected a character, but string of length %d " "found", self._len()) - return space.newint(rutf8.codepoint_at_pos(self._utf8, 0)) + return space.newint(self.codepoint_at_pos_dont_look_inside(0)) def _empty(self): return W_UnicodeObject.EMPTY @@ -432,7 +436,7 @@ if self._length == 0: return space.w_False if self._length == 1: - return space.newbool(func(rutf8.codepoint_at_pos(self._utf8, 0))) + return space.newbool(func(self.codepoint_at_pos_dont_look_inside(0))) else: return self._is_generic_loop(space, self._utf8, func_name) @@ -977,6 +981,11 @@ return pos - 1 return prev_codepoint_pos_dont_look_inside(self._utf8, pos) + def codepoint_at_pos_dont_look_inside(self, pos): + if self.is_ascii(): + return ord(self._utf8[pos]) + return codepoint_at_pos_dont_look_inside(self._utf8, pos) + @always_inline def _unwrap_and_search(self, space, w_sub, w_start, w_end, forward=True): w_sub = self.convert_arg_to_w_unicode(space, w_sub) From pypy.commits at gmail.com Tue Oct 1 15:12:59 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 01 Oct 2019 12:12:59 -0700 (PDT) Subject: [pypy-commit] pypy record-known-result: (cfbolz porting, yodada did the actual work) implement record_exact_value by Message-ID: <5d93a53b.1c69fb81.b83e8.95c0@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: record-known-result Changeset: r97695:0f0cf3af2deb Date: 2019-10-01 13:47 +0200 http://bitbucket.org/pypy/pypy/changeset/0f0cf3af2deb/ Log: (cfbolz porting, yodada did the actual work) implement record_exact_value by stealing the code from the record-exact-value branch. it's another rlib.jit.record_* hint, this one can be used to tell the JIT about a known constant value diff --git a/rpython/jit/codewriter/jtransform.py b/rpython/jit/codewriter/jtransform.py --- a/rpython/jit/codewriter/jtransform.py +++ b/rpython/jit/codewriter/jtransform.py @@ -309,6 +309,9 @@ op1 = [op1, SpaceOperation('-live-', [], None)] return op1 + def rewrite_op_jit_record_exact_value(self, op): + return SpaceOperation("record_exact_value", [op.args[0], op.args[1]], None) + def rewrite_op_debug_assert_not_none(self, op): if isinstance(op.args[0], Variable): return SpaceOperation('assert_not_none', [op.args[0]], None) diff --git a/rpython/jit/metainterp/blackhole.py b/rpython/jit/metainterp/blackhole.py --- a/rpython/jit/metainterp/blackhole.py +++ b/rpython/jit/metainterp/blackhole.py @@ -602,6 +602,10 @@ calldescr): pass + @arguments("r", "r") + def bhimpl_record_exact_value(a, b): + pass + @arguments("i", returns="i") def bhimpl_int_copy(a): return a diff --git a/rpython/jit/metainterp/optimizeopt/rewrite.py b/rpython/jit/metainterp/optimizeopt/rewrite.py --- a/rpython/jit/metainterp/optimizeopt/rewrite.py +++ b/rpython/jit/metainterp/optimizeopt/rewrite.py @@ -541,6 +541,12 @@ self.make_constant_class(op.getarg(0), expectedclassbox, update_last_guard=False) + def optimize_RECORD_EXACT_VALUE(self, op): + box = op.getarg(0) + expectedconstbox = op.getarg(1) + assert isinstance(expectedconstbox, Const) + self.make_constant(box, expectedconstbox) + def optimize_GUARD_CLASS(self, op): expectedclassbox = op.getarg(1) info = self.ensure_ptr_info_arg0(op) diff --git a/rpython/jit/metainterp/optimizeopt/simplify.py b/rpython/jit/metainterp/optimizeopt/simplify.py --- a/rpython/jit/metainterp/optimizeopt/simplify.py +++ b/rpython/jit/metainterp/optimizeopt/simplify.py @@ -43,6 +43,9 @@ def optimize_RECORD_EXACT_CLASS(self, op): pass + def optimize_RECORD_EXACT_VALUE(self, op): + pass + def optimize_GUARD_FUTURE_CONDITION(self, op): self.optimizer.notice_guard_future_condition(op) diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py --- a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py +++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py @@ -7212,6 +7212,21 @@ self.optimize_loop(ops, expected) + def test_record_exact_value(self): + ops = """ + [p0] + record_exact_value(p0, ConstPtr(myptr3)) + i1 = getfield_gc_i(p0, descr=valuedescr3) + escape_i(i1) + jump(p0) + """ + expected = """ + [] + escape_i(7) + jump() + """ + self.optimize_loop(ops, expected) + def test_quasi_immut(self): ops = """ [p0, p1, i0] diff --git a/rpython/jit/metainterp/pyjitpl.py b/rpython/jit/metainterp/pyjitpl.py --- a/rpython/jit/metainterp/pyjitpl.py +++ b/rpython/jit/metainterp/pyjitpl.py @@ -313,6 +313,21 @@ # an operation self.metainterp._record_helper_nonpure_varargs(rop.RECORD_KNOWN_RESULT, None, calldescr, allboxes) + @arguments("box", "box") + def opimpl_record_exact_value(self, box, const_box): + if isinstance(const_box, Const): + self.execute(rop.RECORD_EXACT_VALUE, box, const_box) + elif have_debug_prints(): + if len(self.metainterp.framestack) >= 2: + # caller of ll_record_exact_value + name = self.metainterp.framestack[-2].jitcode.name + else: + name = self.jitcode.name + loc = self.metainterp.jitdriver_sd.warmstate.get_location_str(self.greenkey) + debug_print("record_exact_value with non-constant second argument, ignored", + name, loc) + + @arguments("box") def _opimpl_any_return(self, box): self.metainterp.finishframe(box) diff --git a/rpython/jit/metainterp/resoperation.py b/rpython/jit/metainterp/resoperation.py --- a/rpython/jit/metainterp/resoperation.py +++ b/rpython/jit/metainterp/resoperation.py @@ -137,7 +137,7 @@ return 'v' + str(num) if hasattr(self, '_vec_debug_info'): vecinfo = self._vec_debug_info - count = vecinfo.count + count = vecinfo.count datatype = vecinfo.datatype bytesize = vecinfo.bytesize elif self.vector == -2: @@ -322,7 +322,7 @@ "shallow copy: the returned operation is meant to be used in place of self" # XXX specialize from rpython.jit.metainterp.history import DONT_CHANGE - + if args is None: args = self.getarglist_copy() if descr is None: @@ -1143,6 +1143,7 @@ 'QUASIIMMUT_FIELD/1d/n', # [objptr], descr=SlowMutateDescr 'ASSERT_NOT_NONE/1/n', # [objptr] 'RECORD_EXACT_CLASS/2/n', # [objptr, clsptr] + 'RECORD_EXACT_VALUE/2/n', # [objptr, objptr] 'KEEPALIVE/1/n', 'SAVE_EXCEPTION/0/r', 'SAVE_EXC_CLASS/0/i', # XXX kill me diff --git a/rpython/jit/metainterp/test/test_ajit.py b/rpython/jit/metainterp/test/test_ajit.py --- a/rpython/jit/metainterp/test/test_ajit.py +++ b/rpython/jit/metainterp/test/test_ajit.py @@ -2,7 +2,6 @@ import sys import py -import weakref from rpython.rlib import rgc from rpython.jit.codewriter.policy import StopAtXPolicy @@ -14,7 +13,8 @@ from rpython.rlib.jit import (JitDriver, we_are_jitted, hint, dont_look_inside, loop_invariant, elidable, promote, jit_debug, assert_green, AssertGreenFailed, unroll_safe, current_trace_length, look_inside_iff, - isconstant, isvirtual, set_param, record_exact_class, record_known_result) + isconstant, isvirtual, set_param, record_exact_class, record_known_result, + record_exact_value) from rpython.rlib.longlong2float import float2longlong, longlong2float from rpython.rlib.rarithmetic import ovfcheck, is_valid_int, int_force_ge_zero from rpython.rtyper.lltypesystem import lltype, rffi @@ -4016,6 +4016,33 @@ assert res == main(10) self.check_resops(call_i=2) # two calls to f, both get removed by the backend + + def test_record_exact_value(self): + class A(object): + _immutable_fields_ = ['x'] + + a = A() + b = A() + a.x = 42 + b.x = 233 + + @dont_look_inside + def make(x): + if x > 0: + return a + else: + return b + def f(x): + inst = make(x) + if x > 0: + record_exact_value(inst, a) + else: + record_exact_value(inst, b) + return inst.x + res = self.interp_operations(f, [1]) + assert res == 42 + self.check_operations_history(record_exact_value=1) + def test_generator(self): def g(n): yield n+1 diff --git a/rpython/rlib/jit.py b/rpython/rlib/jit.py --- a/rpython/rlib/jit.py +++ b/rpython/rlib/jit.py @@ -1232,6 +1232,36 @@ return hop.genop("jit_record_known_result", args_v, resulttype=lltype.Void) +def record_exact_value(value, const_value): + """ + Assure the JIT that value is the same as const_value + """ + assert value is const_value + +def ll_record_exact_value(ll_value, ll_const_value): + from rpython.rlib.debug import ll_assert + from rpython.rtyper.lltypesystem.lloperation import llop + from rpython.rtyper.lltypesystem import lltype + ll_assert(ll_value is ll_const_value, "record_exact_value called with two different arguments") + llop.jit_record_exact_value(lltype.Void, ll_value, ll_const_value) + +class Entry(ExtRegistryEntry): + _about_ = record_exact_value + + def compute_result_annotation(self, s_inst, s_const_inst): + from rpython.annotator import model as annmodel + assert isinstance(s_inst, annmodel.SomeInstance) + assert isinstance(s_const_inst, annmodel.SomeInstance) + + def specialize_call(self, hop): + from rpython.rtyper.lltypesystem import lltype + from rpython.rtyper import rclass + + v_inst = hop.inputarg(hop.args_r[0], arg=0) + v_const_inst = hop.inputarg(hop.args_r[1], arg=1) + hop.exception_is_here() + return hop.gendirectcall(ll_record_exact_value, v_inst, v_const_inst) + def _jit_conditional_call(condition, function, *args): pass # special-cased below diff --git a/rpython/rlib/test/test_jit.py b/rpython/rlib/test/test_jit.py --- a/rpython/rlib/test/test_jit.py +++ b/rpython/rlib/test/test_jit.py @@ -5,7 +5,8 @@ from rpython.rlib.jit import (hint, we_are_jitted, JitDriver, elidable_promote, JitHintError, oopspec, isconstant, conditional_call, elidable, unroll_safe, dont_look_inside, conditional_call_elidable, - enter_portal_frame, leave_portal_frame, record_known_result) + enter_portal_frame, leave_portal_frame, record_known_result, + record_exact_value) from rpython.rlib.rarithmetic import r_uint from rpython.rtyper.test.tool import BaseRtypingTest from rpython.rtyper.lltypesystem import lltype @@ -121,6 +122,15 @@ def f(): pass +def test_record_exact_value(): + class A(object): + pass + a = A() + b = A() + record_exact_value(a,a) # assume not crash + with pytest.raises(AssertionError): + record_exact_value(a, b) + class TestJIT(BaseRtypingTest): def test_hint(self): def f(): @@ -351,6 +361,15 @@ t = Translation(g, []) t.compile_c() # does not crash + def test_record_exact_value(self): + class A(object): + pass + def g(): + a = A() + b = A() + record_exact_value(a,a) # assume not crash + self.interpret(g, []) + def test_record_known_result(self): from rpython.translator.interactive import Translation @elidable diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py --- a/rpython/rtyper/llinterp.py +++ b/rpython/rtyper/llinterp.py @@ -563,6 +563,9 @@ def op_jit_record_exact_class(self, *args): pass + def op_jit_record_exact_value(self, *args): + pass + def op_jit_conditional_call(self, *args): raise NotImplementedError("should not be called while not jitted") 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 @@ -456,6 +456,7 @@ 'jit_is_virtual': LLOp(canrun=True), 'jit_force_quasi_immutable': LLOp(canrun=True), 'jit_record_exact_class' : LLOp(canrun=True), + 'jit_record_exact_value' : LLOp(canrun=True), 'jit_ffi_save_result': LLOp(canrun=True), 'jit_conditional_call': LLOp(), 'jit_conditional_call_value': 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 @@ -636,6 +636,9 @@ def op_jit_record_exact_class(x, y): pass +def op_jit_record_exact_value(x, y): + pass + def op_jit_ffi_save_result(*args): pass diff --git a/rpython/translator/c/src/support.h b/rpython/translator/c/src/support.h --- a/rpython/translator/c/src/support.h +++ b/rpython/translator/c/src/support.h @@ -7,6 +7,7 @@ #define RUNNING_ON_LLINTERP 0 #define OP_JIT_RECORD_EXACT_CLASS(i, c, r) /* nothing */ +#define OP_JIT_RECORD_EXACT_VALUE(i, c, r) /* nothing */ #define FAIL_OVF(msg) _RPyRaiseSimpleException(RPyExc_OverflowError) From pypy.commits at gmail.com Tue Oct 1 15:13:01 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 01 Oct 2019 12:13:01 -0700 (PDT) Subject: [pypy-commit] pypy record-known-result: support for integer values in record_exact_value Message-ID: <5d93a53d.1c69fb81.3125f.4daf@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: record-known-result Changeset: r97696:d6d92c45bc0b Date: 2019-10-01 15:20 +0200 http://bitbucket.org/pypy/pypy/changeset/d6d92c45bc0b/ Log: support for integer values in record_exact_value diff --git a/rpython/jit/codewriter/jtransform.py b/rpython/jit/codewriter/jtransform.py --- a/rpython/jit/codewriter/jtransform.py +++ b/rpython/jit/codewriter/jtransform.py @@ -310,7 +310,13 @@ return op1 def rewrite_op_jit_record_exact_value(self, op): - return SpaceOperation("record_exact_value", [op.args[0], op.args[1]], None) + if getkind(op.args[0].concretetype) == "int": + assert getkind(op.args[1].concretetype) == "int" + return SpaceOperation("record_exact_value_i", [op.args[0], op.args[1]], None) + else: + assert getkind(op.args[0].concretetype) == "ref" + assert getkind(op.args[1].concretetype) == "ref" + return SpaceOperation("record_exact_value_r", [op.args[0], op.args[1]], None) def rewrite_op_debug_assert_not_none(self, op): if isinstance(op.args[0], Variable): diff --git a/rpython/jit/metainterp/blackhole.py b/rpython/jit/metainterp/blackhole.py --- a/rpython/jit/metainterp/blackhole.py +++ b/rpython/jit/metainterp/blackhole.py @@ -603,7 +603,11 @@ pass @arguments("r", "r") - def bhimpl_record_exact_value(a, b): + def bhimpl_record_exact_value_r(a, b): + pass + + @arguments("i", "i") + def bhimpl_record_exact_value_i(a, b): pass @arguments("i", returns="i") diff --git a/rpython/jit/metainterp/optimizeopt/rewrite.py b/rpython/jit/metainterp/optimizeopt/rewrite.py --- a/rpython/jit/metainterp/optimizeopt/rewrite.py +++ b/rpython/jit/metainterp/optimizeopt/rewrite.py @@ -541,12 +541,15 @@ self.make_constant_class(op.getarg(0), expectedclassbox, update_last_guard=False) - def optimize_RECORD_EXACT_VALUE(self, op): + def optimize_record_exact_value(self, op): box = op.getarg(0) expectedconstbox = op.getarg(1) assert isinstance(expectedconstbox, Const) self.make_constant(box, expectedconstbox) + optimize_RECORD_EXACT_VALUE_R = optimize_record_exact_value + optimize_RECORD_EXACT_VALUE_I = optimize_record_exact_value + def optimize_GUARD_CLASS(self, op): expectedclassbox = op.getarg(1) info = self.ensure_ptr_info_arg0(op) diff --git a/rpython/jit/metainterp/optimizeopt/simplify.py b/rpython/jit/metainterp/optimizeopt/simplify.py --- a/rpython/jit/metainterp/optimizeopt/simplify.py +++ b/rpython/jit/metainterp/optimizeopt/simplify.py @@ -43,7 +43,10 @@ def optimize_RECORD_EXACT_CLASS(self, op): pass - def optimize_RECORD_EXACT_VALUE(self, op): + def optimize_RECORD_EXACT_VALUE_R(self, op): + pass + + def optimize_RECORD_EXACT_VALUE_I(self, op): pass def optimize_GUARD_FUTURE_CONDITION(self, op): diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py --- a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py +++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py @@ -7215,7 +7215,7 @@ def test_record_exact_value(self): ops = """ [p0] - record_exact_value(p0, ConstPtr(myptr3)) + record_exact_value_r(p0, ConstPtr(myptr3)) i1 = getfield_gc_i(p0, descr=valuedescr3) escape_i(i1) jump(p0) @@ -7227,6 +7227,20 @@ """ self.optimize_loop(ops, expected) + def test_record_exact_value_int(self): + ops = """ + [i0] + record_exact_value_i(i0, 15) + escape_i(i0) + jump(i0) + """ + expected = """ + [] + escape_i(15) + jump() + """ + self.optimize_loop(ops, expected) + def test_quasi_immut(self): ops = """ [p0, p1, i0] diff --git a/rpython/jit/metainterp/pyjitpl.py b/rpython/jit/metainterp/pyjitpl.py --- a/rpython/jit/metainterp/pyjitpl.py +++ b/rpython/jit/metainterp/pyjitpl.py @@ -314,9 +314,15 @@ self.metainterp._record_helper_nonpure_varargs(rop.RECORD_KNOWN_RESULT, None, calldescr, allboxes) @arguments("box", "box") - def opimpl_record_exact_value(self, box, const_box): + def opimpl_record_exact_value_r(self, box, const_box): + return self._record_exact_value(rop.RECORD_EXACT_VALUE_R, box, const_box) + @arguments("box", "box") + def opimpl_record_exact_value_i(self, box, const_box): + return self._record_exact_value(rop.RECORD_EXACT_VALUE_I, box, const_box) + + def _record_exact_value(self, opname, box, const_box): if isinstance(const_box, Const): - self.execute(rop.RECORD_EXACT_VALUE, box, const_box) + self.execute(opname, box, const_box) elif have_debug_prints(): if len(self.metainterp.framestack) >= 2: # caller of ll_record_exact_value diff --git a/rpython/jit/metainterp/resoperation.py b/rpython/jit/metainterp/resoperation.py --- a/rpython/jit/metainterp/resoperation.py +++ b/rpython/jit/metainterp/resoperation.py @@ -1143,7 +1143,8 @@ 'QUASIIMMUT_FIELD/1d/n', # [objptr], descr=SlowMutateDescr 'ASSERT_NOT_NONE/1/n', # [objptr] 'RECORD_EXACT_CLASS/2/n', # [objptr, clsptr] - 'RECORD_EXACT_VALUE/2/n', # [objptr, objptr] + 'RECORD_EXACT_VALUE_R/2/n', # [objptr, objptr] + 'RECORD_EXACT_VALUE_I/2/n', # [int, int] 'KEEPALIVE/1/n', 'SAVE_EXCEPTION/0/r', 'SAVE_EXC_CLASS/0/i', # XXX kill me diff --git a/rpython/jit/metainterp/test/test_ajit.py b/rpython/jit/metainterp/test/test_ajit.py --- a/rpython/jit/metainterp/test/test_ajit.py +++ b/rpython/jit/metainterp/test/test_ajit.py @@ -4041,7 +4041,22 @@ return inst.x res = self.interp_operations(f, [1]) assert res == 42 - self.check_operations_history(record_exact_value=1) + self.check_operations_history(record_exact_value_r=1) + + def test_record_exact_value_int(self): + @dont_look_inside + def make(x): + if x > 0: + return 16 + else: + return x + def f(x): + y = make(x) + record_exact_value(y >= 0, True) + return y < 0 + res = self.interp_operations(f, [1]) + assert res == 0 + self.check_operations_history(record_exact_value_i=1) def test_generator(self): def g(n): diff --git a/rpython/rlib/jit.py b/rpython/rlib/jit.py --- a/rpython/rlib/jit.py +++ b/rpython/rlib/jit.py @@ -1236,22 +1236,21 @@ """ Assure the JIT that value is the same as const_value """ - assert value is const_value + assert value == const_value def ll_record_exact_value(ll_value, ll_const_value): from rpython.rlib.debug import ll_assert from rpython.rtyper.lltypesystem.lloperation import llop from rpython.rtyper.lltypesystem import lltype - ll_assert(ll_value is ll_const_value, "record_exact_value called with two different arguments") + ll_assert(ll_value == ll_const_value, "record_exact_value called with two different arguments") llop.jit_record_exact_value(lltype.Void, ll_value, ll_const_value) class Entry(ExtRegistryEntry): _about_ = record_exact_value - def compute_result_annotation(self, s_inst, s_const_inst): + def compute_result_annotation(self, s_val, s_const_val): from rpython.annotator import model as annmodel - assert isinstance(s_inst, annmodel.SomeInstance) - assert isinstance(s_const_inst, annmodel.SomeInstance) + annmodel.unionof(s_val, s_const_val) # produce error if types are incompatible def specialize_call(self, hop): from rpython.rtyper.lltypesystem import lltype diff --git a/rpython/rlib/test/test_jit.py b/rpython/rlib/test/test_jit.py --- a/rpython/rlib/test/test_jit.py +++ b/rpython/rlib/test/test_jit.py @@ -367,7 +367,16 @@ def g(): a = A() b = A() - record_exact_value(a,a) # assume not crash + record_exact_value(a, a) # assume not crash + self.interpret(g, []) + + def test_record_exact_value_int(self): + @dont_look_inside + def f(): + return 4 + def g(): + a = f() + record_exact_value(a >= 0, True) # does not crash self.interpret(g, []) def test_record_known_result(self): From pypy.commits at gmail.com Tue Oct 1 15:13:03 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 01 Oct 2019 12:13:03 -0700 (PDT) Subject: [pypy-commit] pypy record-known-result: tell the jit that the result of str.find is always smaller than the length of Message-ID: <5d93a53f.1c69fb81.a9b76.1df6@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: record-known-result Changeset: r97697:0085d3e74e02 Date: 2019-10-01 15:51 +0200 http://bitbucket.org/pypy/pypy/changeset/0085d3e74e02/ Log: tell the jit that the result of str.find is always smaller than the length of the string diff --git a/rpython/jit/metainterp/test/test_string.py b/rpython/jit/metainterp/test/test_string.py --- a/rpython/jit/metainterp/test/test_string.py +++ b/rpython/jit/metainterp/test/test_string.py @@ -990,7 +990,48 @@ return len(d[s]) assert self.interp_operations(f, [222]) == 6 - def test_codepoint_index_at_byte_position_record_known_result(self): + def test_str_find(self): + jitdriver = JitDriver(greens=['x'], reds=['z', 'res']) + s1 = "aaaaaaef" + s2 = "aaaaaaaaaaaaaaaaaaaaeebbbbbbbbbbbbbbbbbbef" + + @dont_look_inside + def pick(x): + if x > 0: + return s1 + else: + return s2 + + @dont_look_inside + def pick_s(z): + if z < 5: + return "e" + return "ef" + + def f(x): + z = 0 + res = 0 + while z < 10: + jitdriver.jit_merge_point(x=x, res=res, z=z) + s = pick(x) + # the following lines emulate unicode.find + byteindex = s.find(pick_s(x)) + if byteindex < 0: + return -1001 + if byteindex >= len(s): # no guard + return -106 + res += ord(s[byteindex]) + z += 1 + return res + res = self.meta_interp(f, [1], backendopt=True) + assert res == f(1) + self.check_simple_loop(guard_false=1) # only one guard, to check for -1 + res = self.meta_interp(f, [10], backendopt=True) + assert res == f(10) + # one guard to check whether the search string is len == 1, one to check for result -1 + self.check_simple_loop(guard_false=2) + + def test_codepoint_index_at_byte_position_record_known_result_and_invariants(self): from rpython.rlib import rutf8 jitdriver = JitDriver(greens=['x'], reds=['z', 'res']) s1 = u"abceüf".encode("utf-8") diff --git a/rpython/rtyper/lltypesystem/rstr.py b/rpython/rtyper/lltypesystem/rstr.py --- a/rpython/rtyper/lltypesystem/rstr.py +++ b/rpython/rtyper/lltypesystem/rstr.py @@ -721,16 +721,22 @@ def ll_find(s1, s2, start, end): if start < 0: start = 0 - if end > len(s1.chars): - end = len(s1.chars) + n = len(s1.chars) + if end > n: + end = n if end - start < 0: return -1 m = len(s2.chars) if m == 1: - return LLHelpers.ll_find_char(s1, s2.chars[0], start, end) + res = LLHelpers.ll_find_char(s1, s2.chars[0], start, end) + jit.record_exact_value(res < end, True) + return res - return LLHelpers.ll_search(s1, s2, start, end, FAST_FIND) + res = LLHelpers.ll_search(s1, s2, start, end, FAST_FIND) + jit.record_exact_value(res < end, True) + jit.record_exact_value(res + m <= n, True) + return res @staticmethod @signature(types.any(), types.any(), types.int(), types.int(), returns=types.int()) From pypy.commits at gmail.com Tue Oct 1 15:13:05 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 01 Oct 2019 12:13:05 -0700 (PDT) Subject: [pypy-commit] pypy record-known-result: hints on the bounds of the results of some of the rutf8 functions Message-ID: <5d93a541.1c69fb81.f850b.ba97@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: record-known-result Changeset: r97698:7151aca5645a Date: 2019-10-01 15:53 +0200 http://bitbucket.org/pypy/pypy/changeset/7151aca5645a/ Log: hints on the bounds of the results of some of the rutf8 functions diff --git a/dotviewer/graphclient.py b/dotviewer/graphclient.py --- a/dotviewer/graphclient.py +++ b/dotviewer/graphclient.py @@ -95,8 +95,8 @@ except EOFError: ioerror = ioerror or IOError("connection unexpectedly closed " "(graphserver crash?)") - if ioerror is not None: - raise ioerror + #if ioerror is not None: + # raise ioerror def send_error(io, e): try: 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 @@ -760,7 +760,7 @@ assert stop >= 0 byte_start = 0 for i in range(start): - byte_start = next_codepoint_pos_dont_look_inside(self._utf8, byte_start) + byte_start = self.next_codepoint_pos_dont_look_inside(byte_start) byte_stop = len(self._utf8) for i in range(self._len() - stop): byte_stop = prev_codepoint_pos_dont_look_inside(self._utf8, byte_stop) @@ -920,7 +920,7 @@ if not self.is_ascii(): storage = self._get_index_storage() jit.record_known_result( - end, rutf8.codepoint_position_at_index, self._utf8, storage, index + 1) + end, rutf8._codepoint_position_at_index, self._utf8, storage, index + 1) return W_UnicodeObject(self._utf8[start:end], 1) @jit.unroll_safe @@ -952,6 +952,7 @@ return self._length == len(self._utf8) def _index_to_byte(self, index): + assert 0 <= index < self._len() if self.is_ascii(): assert index >= 0 return index @@ -974,6 +975,7 @@ """ this returns index such that self._index_to_byte(index) == bytepos NB: this is slow! roughly logarithmic with a big constant """ + assert 0 <= bytepos < len(self._utf8) if self.is_ascii(): return bytepos return rutf8.codepoint_index_at_byte_position( @@ -982,7 +984,10 @@ def next_codepoint_pos_dont_look_inside(self, pos): if self.is_ascii(): return pos + 1 - return next_codepoint_pos_dont_look_inside(self._utf8, pos) + res = next_codepoint_pos_dont_look_inside(self._utf8, pos) + jit.record_exact_value(res >= 0, True) + jit.record_exact_value(res <= len(self._utf8), True) + return res def prev_codepoint_pos_dont_look_inside(self, pos): if self.is_ascii(): @@ -1013,7 +1018,6 @@ if res_index < 0: return None res = self._byte_to_index(res_index) - jit.promote(res >= 0) # always true! assert res >= 0 return space.newint(res) diff --git a/rpython/jit/metainterp/test/test_string.py b/rpython/jit/metainterp/test/test_string.py --- a/rpython/jit/metainterp/test/test_string.py +++ b/rpython/jit/metainterp/test/test_string.py @@ -1053,9 +1053,14 @@ s = pick(x) # the following lines emulate unicode.find byteindex = s.find(search[z]) - assert byteindex >= 0 + if byteindex < 0: + return -1001 storage = rutf8.create_utf8_index_storage(s, len(s) - 1) index = rutf8.codepoint_index_at_byte_position(s, storage, byteindex, len(s) - 1) + if index < 0: # no guard + return -1000 + if index >= len(s) - 1: # no guard + return -1004 # then we use the resulting codepoint index in conjunction with # the string to get at a byte index b = rutf8.codepoint_position_at_index(s, storage, index) @@ -1068,5 +1073,5 @@ f(1) res = self.meta_interp(f, [1], backendopt=True) assert res == f(1) - self.check_simple_loop(int_sub=1) + self.check_simple_loop(int_sub=1, guard_false=1) diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -545,13 +545,20 @@ break return storage - at jit.elidable def codepoint_position_at_index(utf8, storage, index): """ Return byte index of a character inside utf8 encoded string, given storage of type UTF8_INDEX_STORAGE. The index must be smaller than or equal to the utf8 length: if needed, check explicitly before calling this function. """ + res = _codepoint_position_at_index(utf8, storage, index) + # tell the jit about the invariants of the result + jit.record_exact_value(res >= 0, True) + jit.record_exact_value(res < len(utf8), True) + return res + + at jit.elidable +def _codepoint_position_at_index(utf8, storage, index): current = index >> 6 ofs = ord(storage[current].ofs[(index >> 2) & 0x0F]) bytepos = storage[current].baseindex + ofs @@ -599,7 +606,10 @@ is not tiny either. """ res = _codepoint_index_at_byte_position(utf8, storage, bytepos, num_codepoints) - jit.record_known_result(bytepos, codepoint_position_at_index, utf8, storage, res) + jit.record_known_result(bytepos, _codepoint_position_at_index, utf8, storage, res) + # tell the JIT that there are no bounds checks needed on the resulting indices + jit.record_exact_value(res >= 0, True) + jit.record_exact_value(res < num_codepoints, True) return res @jit.elidable From pypy.commits at gmail.com Tue Oct 1 15:13:07 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 01 Oct 2019 12:13:07 -0700 (PDT) Subject: [pypy-commit] pypy record-known-result: merge default Message-ID: <5d93a543.1c69fb81.2acc6.fe28@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: record-known-result Changeset: r97699:d07336d23a84 Date: 2019-10-01 21:12 +0200 http://bitbucket.org/pypy/pypy/changeset/d07336d23a84/ Log: merge default 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 @@ -41,6 +41,10 @@ def prev_codepoint_pos_dont_look_inside(utf8, p): return rutf8.prev_codepoint_pos(utf8, p) + at jit.elidable +def codepoint_at_pos_dont_look_inside(utf8, p): + return rutf8.codepoint_at_pos(utf8, p) + class W_UnicodeObject(W_Root): import_from_mixin(StringMethods) @@ -135,7 +139,7 @@ raise oefmt(space.w_TypeError, "ord() expected a character, but string of length %d " "found", self._len()) - return space.newint(rutf8.codepoint_at_pos(self._utf8, 0)) + return space.newint(self.codepoint_at_pos_dont_look_inside(0)) def _empty(self): return W_UnicodeObject.EMPTY @@ -433,7 +437,7 @@ if self._length == 0: return space.w_False if self._length == 1: - return space.newbool(func(rutf8.codepoint_at_pos(self._utf8, 0))) + return space.newbool(func(self.codepoint_at_pos_dont_look_inside(0))) else: return self._is_generic_loop(space, self._utf8, func_name) @@ -994,6 +998,11 @@ return pos - 1 return prev_codepoint_pos_dont_look_inside(self._utf8, pos) + def codepoint_at_pos_dont_look_inside(self, pos): + if self.is_ascii(): + return ord(self._utf8[pos]) + return codepoint_at_pos_dont_look_inside(self._utf8, pos) + @always_inline def _unwrap_and_search(self, space, w_sub, w_start, w_end, forward=True): w_sub = self.convert_arg_to_w_unicode(space, w_sub) From pypy.commits at gmail.com Tue Oct 1 16:27:45 2019 From: pypy.commits at gmail.com (rlamy) Date: Tue, 01 Oct 2019 13:27:45 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: Add failing apptest related to the firstiter asyncgen_hook Message-ID: <5d93b6c1.1c69fb81.692c0.f4c1@mx.google.com> Author: Ronan Lamy Branch: py3.6 Changeset: r97700:0078687570a6 Date: 2019-10-01 20:05 +0100 http://bitbucket.org/pypy/pypy/changeset/0078687570a6/ Log: Add failing apptest related to the firstiter asyncgen_hook diff --git a/pypy/interpreter/test/apptest_coroutine.py b/pypy/interpreter/test/apptest_coroutine.py --- a/pypy/interpreter/test/apptest_coroutine.py +++ b/pypy/interpreter/test/apptest_coroutine.py @@ -1,6 +1,8 @@ import pytest from pytest import raises +import sys + def test_cannot_iterate(): async def f(x): @@ -14,6 +16,7 @@ class X: def __aiter__(self): return MyAIter() + class MyAIter: async def __anext__(self): return 42 @@ -94,7 +97,6 @@ assert isinstance(c.value.__cause__, ZeroDivisionError) def test_set_coroutine_wrapper(): - import sys async def f(): pass seen = [] @@ -610,7 +612,6 @@ def test_async_aclose_in_finalize_hook_await_in_finally(): import gc - import sys import types @types.coroutine @@ -728,3 +729,49 @@ assert val2.value == 2 run_async(run()) + +class suspend: + """ + A simple awaitable that returns control to the "event loop" with `msg` + as value. + """ + def __init__(self, msg=None): + self.msg = msg + + def __await__(self): + yield self.msg + +def test_asyncgen_hooks_shutdown(): + finalized = 0 + asyncgens = [] + + def register_agen(agen): + asyncgens.append(agen) + + async def waiter(timeout): + nonlocal finalized + try: + await suspend('running waiter') + yield 1 + finally: + await suspend('closing waiter') + finalized += 1 + + async def wait(): + async for _ in waiter(1): + pass + + task1 = wait() + task2 = wait() + old_hooks = sys.get_asyncgen_hooks() + try: + sys.set_asyncgen_hooks(firstiter=register_agen) + assert task1.send(None) == 'running waiter' + assert task2.send(None) == 'running waiter' + assert len(asyncgens) == 2 + + assert run_async(asyncgens[0].aclose()) == (['closing waiter'], None) + assert run_async(asyncgens[1].aclose()) == (['closing waiter'], None) + assert finalized == 2 + finally: + sys.set_asyncgen_hooks(*old_hooks) From pypy.commits at gmail.com Tue Oct 1 16:27:47 2019 From: pypy.commits at gmail.com (rlamy) Date: Tue, 01 Oct 2019 13:27:47 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: Use suspend() helper in more tests to avoid importing types from lib-python Message-ID: <5d93b6c3.1c69fb81.26ba4.7b20@mx.google.com> Author: Ronan Lamy Branch: py3.6 Changeset: r97701:d3b503bab689 Date: 2019-10-01 21:26 +0100 http://bitbucket.org/pypy/pypy/changeset/d3b503bab689/ Log: Use suspend() helper in more tests to avoid importing types from lib-python diff --git a/pypy/interpreter/test/apptest_coroutine.py b/pypy/interpreter/test/apptest_coroutine.py --- a/pypy/interpreter/test/apptest_coroutine.py +++ b/pypy/interpreter/test/apptest_coroutine.py @@ -4,6 +4,18 @@ import sys +class suspend: + """ + A simple awaitable that returns control to the "event loop" with `msg` + as value. + """ + def __init__(self, msg=None): + self.msg = msg + + def __await__(self): + yield self.msg + + def test_cannot_iterate(): async def f(x): pass @@ -111,7 +123,6 @@ sys.set_coroutine_wrapper(None) assert sys.get_coroutine_wrapper() is None - def test_async_with(): seen = [] class X: @@ -519,12 +530,6 @@ raises(RuntimeError, run().send, None) def test_async_aclose_await_in_finally(): - import types - - @types.coroutine - def coro(): - yield 'coro' - state = 0 async def ag(): nonlocal state @@ -532,7 +537,7 @@ yield finally: state = 1 - await coro() + await suspend('coro') state = 2 async def run(): @@ -551,12 +556,6 @@ assert state == 2 def test_async_aclose_await_in_finally_with_exception(): - import types - - @types.coroutine - def coro(): - yield 'coro' - state = 0 async def ag(): nonlocal state @@ -565,7 +564,7 @@ finally: state = 1 try: - await coro() + await suspend('coro') except Exception as exc: state = exc @@ -586,17 +585,12 @@ assert state == exc def test_agen_aclose_await_and_yield_in_finally(): - import types - @types.coroutine - def bar(): - yield 42 - async def foo(): try: yield 1 1 / 0 finally: - await bar() + await suspend(42) yield 12 async def run(): @@ -612,12 +606,6 @@ def test_async_aclose_in_finalize_hook_await_in_finally(): import gc - import types - - @types.coroutine - def coro(): - yield 'coro' - state = 0 async def ag(): nonlocal state @@ -625,7 +613,7 @@ yield finally: state = 1 - await coro() + await suspend('coro') state = 2 async def run(): @@ -730,17 +718,6 @@ run_async(run()) -class suspend: - """ - A simple awaitable that returns control to the "event loop" with `msg` - as value. - """ - def __init__(self, msg=None): - self.msg = msg - - def __await__(self): - yield self.msg - def test_asyncgen_hooks_shutdown(): finalized = 0 asyncgens = [] From pypy.commits at gmail.com Tue Oct 1 16:27:49 2019 From: pypy.commits at gmail.com (rlamy) Date: Tue, 01 Oct 2019 13:27:49 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: Use built-in module _io instead of io Message-ID: <5d93b6c5.1c69fb81.e0627.d642@mx.google.com> Author: Ronan Lamy Branch: py3.6 Changeset: r97702:f7ddde9c4602 Date: 2019-10-01 21:26 +0100 http://bitbucket.org/pypy/pypy/changeset/f7ddde9c4602/ Log: Use built-in module _io instead of io diff --git a/pypy/module/sys/std_test.py b/pypy/module/sys/std_test.py --- a/pypy/module/sys/std_test.py +++ b/pypy/module/sys/std_test.py @@ -1,18 +1,18 @@ # Install standard streams for tests that don't call app_main. Always # use line buffering, even for tests that capture standard descriptors. -import io +import _io -stdin = io.open(0, "r", encoding="ascii", +stdin = _io.open(0, "r", encoding="ascii", closefd=False) stdin.buffer.raw.name = "" -stdout = io.open(1, "w", encoding="ascii", +stdout = _io.open(1, "w", encoding="ascii", buffering=1, closefd=False) stdout.buffer.raw.name = "" -stderr = io.open(2, "w", encoding="ascii", +stderr = _io.open(2, "w", encoding="ascii", errors="backslashreplace", buffering=1, closefd=False) From pypy.commits at gmail.com Tue Oct 1 16:33:15 2019 From: pypy.commits at gmail.com (rlamy) Date: Tue, 01 Oct 2019 13:33:15 -0700 (PDT) Subject: [pypy-commit] pypy py3.7: hg merge py3.6 Message-ID: <5d93b80b.1c69fb81.c9312.4227@mx.google.com> Author: Ronan Lamy Branch: py3.7 Changeset: r97703:11316d0ce627 Date: 2019-10-01 21:32 +0100 http://bitbucket.org/pypy/pypy/changeset/11316d0ce627/ Log: hg merge py3.6 diff too long, truncating to 2000 out of 21056 lines diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -40,11 +40,11 @@ Armin Rigo Maciej Fijalkowski Carl Friedrich Bolz-Tereick + Matti Picus Antonio Cuni Amaury Forgeot d'Arc - Matti Picus + Ronan Lamy Samuele Pedroni - Ronan Lamy Alex Gaynor Philip Jenvey Richard Plangger @@ -94,6 +94,7 @@ Jason Creighton Mark Young Alex Martelli + Andrew Lawrence Spenser Bauman Michal Bendowski Jan de Mooij @@ -106,11 +107,11 @@ Stefan Schwarzer Tomek Meka Valentino Volonghi + Stefan Beyer Patrick Maupin Devin Jeanpierre Bob Ippolito Bruno Gola - Andrew Lawrence David Malcolm Squeaky Edd Barrett @@ -124,7 +125,6 @@ Wenzhu Man Konstantin Lopuhin John Witulski - Stefan Beyer Jeremy Thurgood Greg Price Ivan Sichmann Freitas @@ -138,6 +138,7 @@ Pavel Vinogradov William Leslie Paweł Piotr Przeradowski + Stian Andreassen marky1991 Ilya Osadchiy Tobias Oberstein @@ -146,14 +147,13 @@ Taavi Burns Adrian Kuhn tav - Stian Andreassen Georg Brandl Joannah Nanjekye + Julian Berman Bert Freudenberg Wanja Saatkamp Mike Blume Gerald Klix - Julian Berman Oscar Nierstrasz Rami Chowdhury Stefan H. Muller @@ -204,6 +204,7 @@ Andrews Medina Aaron Iles Toby Watson + Lin Cheng Daniel Patrick Stuart Williams Antoine Pitrou @@ -245,6 +246,7 @@ Valentina Mukhamedzhanova Stefano Parmesan touilleMan + Anthony Sottile Marc Abramowitz Arjun Naik Aaron Gallagher @@ -254,7 +256,6 @@ Omer Katz Jacek Generowicz Tomasz Dziopa - Lin Cheng Sylvain Thenault Jakub Stasiak Andrew Dalke @@ -285,7 +286,6 @@ Lene Wagner Tomo Cocoa Miro Hrončok - Anthony Sottile David Lievens Neil Blakey-Milner Henrik Vendelbo @@ -294,11 +294,14 @@ Christoph Gerum Miguel de Val Borro Artur Lisiecki + joserubiovidales at gmail.com afteryu Toni Mattis + Vincent Michel Laurens Van Houtven Bobby Impollonia Roberto De Ioris + Yannick Jadoul Jeong YunWon Christopher Armstrong Aaron Tubbs @@ -312,6 +315,7 @@ Fabio Niephaus Akira Li Gustavo Niemeyer + joachim-ballmann at bitbucket.org Nate Bragg Lucas Stadler roberto at goyle @@ -331,8 +335,12 @@ Ben Darnell Juan Francisco Cantero Hurtado Godefroid Chappelle + Paul Ganssle + Michal Kuffa Stephan Busemann + Bystroushaak Dan Colish + Ram Rachum timo Volodymyr Vladymyrov Daniel Neuhäuser @@ -342,18 +350,22 @@ Chris Lambacher John Aldis coolbutuseless at gmail.com + Yasen Kiprov Mike Bayer Rodrigo Araújo Daniil Yarancev Min RK OlivierBlanvillain + dakarpov at gmail.com Jonas Pfannschmidt Zearin Johan Forsberg Andrey Churin Dan Crosta reubano at gmail.com + Ryan Hileman Stanisław Halik + DeVerne Jones Julien Phalip Roman Podoliaka Steve Papanik @@ -369,17 +381,20 @@ Jim Hunziker shoma hosaka Buck Golemon + whitequark Iraklis D. JohnDoe yrttyr Michael Chermside Anna Ravencroft remarkablerocket + Ivan Petre Vijiac Berker Peksag Christian Muirhead soareschen Matthew Miller + Jesdi Konrad Delong Dinu Gherman pizi @@ -398,13 +413,16 @@ Markus Unterwaditzer Kristoffer Kleine Graham Markall + paugier Dan Loewenherz werat Filip Salomonsson Niclas Olofsson + Zsolt Cserna Chris Pressey Tobias Diaz Paul Graydon + mkuffa Nikolaos-Digenis Karagiannis Kurt Griffiths Ben Mather diff --git a/extra_tests/cffi_tests/cffi1/test_re_python.py b/extra_tests/cffi_tests/cffi1/test_re_python.py --- a/extra_tests/cffi_tests/cffi1/test_re_python.py +++ b/extra_tests/cffi_tests/cffi1/test_re_python.py @@ -66,7 +66,7 @@ int add43(int, ...); int globalvar42; const int globalconst42; - const char *const globalconsthello = "hello"; + const char *const globalconsthello; int no_such_function(int); int no_such_globalvar; struct foo_s; diff --git a/extra_tests/cffi_tests/test_egg_version.py b/extra_tests/cffi_tests/test_version.py rename from extra_tests/cffi_tests/test_egg_version.py rename to extra_tests/cffi_tests/test_version.py --- a/extra_tests/cffi_tests/test_egg_version.py +++ b/extra_tests/cffi_tests/test_version.py @@ -1,6 +1,7 @@ from email.parser import Parser import py +from urllib.request import urlopen import cffi import pypy @@ -10,3 +11,11 @@ def test_egg_version(): info = Parser().parsestr(egg_info.read()) assert info['version'] == cffi.__version__ + +def test_pycparser_version(): + url = 'https://raw.githubusercontent.com/eliben/pycparser/master/pycparser/__init__.py' + source = urlopen(url).read().decode('utf8') + dest = py.path.local(__file__).join('..', '..', '..', 'lib_pypy', 'cffi', + '_pycparser', '__init__.py').read() + # if this fails, the vendored pycparser is not the latest version + assert source.strip() == dest.strip() diff --git a/extra_tests/test_semlock.py b/extra_tests/test_semlock.py new file mode 100644 --- /dev/null +++ b/extra_tests/test_semlock.py @@ -0,0 +1,36 @@ +from _multiprocessing import SemLock +from threading import Thread +import _thread +import time + + +def test_notify_all(): + """A low-level variation on test_notify_all() in lib-python's + _test_multiprocessing.py + """ + N_THREADS = 1000 + lock = SemLock(0, 1, 1, "/test_notify_all", True) + results = [] + + def f(n): + if lock.acquire(timeout=5.): + results.append(n) + lock.release() + + threads = [Thread(target=f, args=(i,)) for i in range(N_THREADS)] + n_started = N_THREADS + with lock: + for t in threads: + try: + t.start() + except _thread.error: + # too many threads for this system + t.started = False + n_started -= 1 + else: + t.started = True + time.sleep(0.1) + for t in threads: + if t.started: + t.join() + assert len(results) == n_started diff --git a/lib-python/3/stat.py b/lib-python/3/stat.py --- a/lib-python/3/stat.py +++ b/lib-python/3/stat.py @@ -40,6 +40,10 @@ S_IFIFO = 0o010000 # fifo (named pipe) S_IFLNK = 0o120000 # symbolic link S_IFSOCK = 0o140000 # socket file +# Fallbacks for uncommon platform-specific constants +S_IFDOOR = 0 +S_IFPORT = 0 +S_IFWHT = 0 # Functions to test for each file type @@ -71,6 +75,18 @@ """Return True if mode is from a socket.""" return S_IFMT(mode) == S_IFSOCK +def S_ISDOOR(mode): + """Return True if mode is from a door.""" + return False + +def S_ISPORT(mode): + """Return True if mode is from an event port.""" + return False + +def S_ISWHT(mode): + """Return True if mode is from a whiteout.""" + return False + # Names for permission bits S_ISUID = 0o4000 # set UID bit diff --git a/lib-python/3/test/test_concurrent_futures.py b/lib-python/3/test/test_concurrent_futures.py --- a/lib-python/3/test/test_concurrent_futures.py +++ b/lib-python/3/test/test_concurrent_futures.py @@ -636,6 +636,7 @@ futures_list.remove(future) wr = weakref.ref(future) del future + test.support.gc_collect() self.assertIsNone(wr()) futures_list[0].set_result("test") @@ -643,6 +644,7 @@ futures_list.remove(future) wr = weakref.ref(future) del future + test.support.gc_collect() self.assertIsNone(wr()) if futures_list: futures_list[0].set_result("test") @@ -742,6 +744,7 @@ for obj in self.executor.map(make_dummy_object, range(10)): wr = weakref.ref(obj) del obj + test.support.gc_collect() self.assertIsNone(wr()) diff --git a/lib-python/3/test/test_dis.py b/lib-python/3/test/test_dis.py --- a/lib-python/3/test/test_dis.py +++ b/lib-python/3/test/test_dis.py @@ -145,7 +145,7 @@ 1) pass -_bug1333982 = """\ +dis_bug1333982 = """\ %3d 0 LOAD_CONST 1 (0) 2 POP_JUMP_IF_TRUE 26 4 LOAD_GLOBAL 0 (AssertionError) diff --git a/lib-python/3/test/test_peepholer.py b/lib-python/3/test/test_peepholer.py --- a/lib-python/3/test/test_peepholer.py +++ b/lib-python/3/test/test_peepholer.py @@ -186,9 +186,11 @@ code = compile('a=1<<1000', '', 'single') self.assertInBytecode(code, 'LOAD_CONST', 1000) self.assertNotIn(1<<1000, code.co_consts) - code = compile('a=2**1000', '', 'single') - self.assertInBytecode(code, 'LOAD_CONST', 1000) - self.assertNotIn(2**1000, code.co_consts) + # difference to CPython: PyPy allows slightly larger constants to be + # created + code = compile('a=2**10000', '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', 10000) + self.assertNotIn(2**10000, code.co_consts) @cpython_only # we currently not bother to implement that def test_binary_subscr_on_unicode(self): diff --git a/lib-python/3/test/test_readline.py b/lib-python/3/test/test_readline.py --- a/lib-python/3/test/test_readline.py +++ b/lib-python/3/test/test_readline.py @@ -243,7 +243,7 @@ # See https://cnswww.cns.cwru.edu/php/chet/readline/CHANGES # - editline: history size is broken on OS X 10.11.6. # Newer versions were not tested yet. - @unittest.skipIf(readline._READLINE_VERSION < 0x600, + @unittest.skipIf(getattr(readline, "_READLINE_VERSION", 0x601) < 0x600, "this readline version does not support history-size") @unittest.skipIf(is_editline, "editline history size configuration is broken") diff --git a/lib-python/3/test/test_regrtest.py b/lib-python/3/test/test_regrtest.py --- a/lib-python/3/test/test_regrtest.py +++ b/lib-python/3/test/test_regrtest.py @@ -258,7 +258,6 @@ self.checkError([opt, '0', '-T'], "don't go together") self.checkError([opt, '0', '-T'], "don't go together") self.checkError([opt, '0', '-l'], "don't go together") - self.checkError([opt, '0', '-M', '4G'], "don't go together") def test_coverage(self): for opt in '-T', '--coverage': diff --git a/lib-python/3/test/test_threading.py b/lib-python/3/test/test_threading.py --- a/lib-python/3/test/test_threading.py +++ b/lib-python/3/test/test_threading.py @@ -561,6 +561,7 @@ self.assertEqual(err, b"") self.assertEqual(data, "Thread-1\nTrue\nTrue\n") + @test.support.cpython_only @requires_type_collecting def test_main_thread_during_shutdown(self): # bpo-31516: current_thread() should still point to the main thread diff --git a/lib-python/3/test/test_time.py b/lib-python/3/test/test_time.py --- a/lib-python/3/test/test_time.py +++ b/lib-python/3/test/test_time.py @@ -588,6 +588,8 @@ self.skipTest('could not set locale.LC_ALL to fr_FR') # This should not cause an exception time.strftime("%B", (2009,2,1,0,0,0,0,0,0)) + # PyPy addition: + time.strftime("%B", (2009,8,1,0,0,0,0,0,0)).lower() class _TestAsctimeYear: diff --git a/lib-python/3/unittest/test/testmock/testhelpers.py b/lib-python/3/unittest/test/testmock/testhelpers.py --- a/lib-python/3/unittest/test/testmock/testhelpers.py +++ b/lib-python/3/unittest/test/testmock/testhelpers.py @@ -3,6 +3,8 @@ import types import unittest +from test.support import cpython_only + from unittest.mock import ( call, _Call, create_autospec, MagicMock, Mock, ANY, _CallList, patch, PropertyMock, _callable @@ -875,7 +877,7 @@ # plain data descriptor check_data_descriptor(foo.desc) - + @cpython_only # PyPy can easily extract a spec from a builtin function def test_autospec_on_bound_builtin_function(self): meth = types.MethodType(time.ctime, time.time()) self.assertIsInstance(meth(), str) diff --git a/lib_pypy/_curses_build.py b/lib_pypy/_curses_build.py --- a/lib_pypy/_curses_build.py +++ b/lib_pypy/_curses_build.py @@ -34,6 +34,13 @@ #define NCURSES_OPAQUE 0 #endif + +/* ncurses 6 change behaviour and makes all pointers opaque, + lets define backward compatibility. It doesn't harm + previous versions */ + +#define NCURSES_INTERNALS 1 +#define NCURSES_REENTRANT 0 #include #include #include diff --git a/lib_pypy/_dbm.py b/lib_pypy/_dbm.py --- a/lib_pypy/_dbm.py +++ b/lib_pypy/_dbm.py @@ -149,7 +149,7 @@ lib = CDLL("/usr/lib/libdbm.dylib") # OS X _platform = 'osx' -library = "GNU gdbm" +library = "Berkeley DB" funcs = {} _init_func('open', (c_char_p, c_int, c_int), restype=c_void_p) diff --git a/lib_pypy/_pypy_testcapi.py b/lib_pypy/_pypy_testcapi.py --- a/lib_pypy/_pypy_testcapi.py +++ b/lib_pypy/_pypy_testcapi.py @@ -61,6 +61,8 @@ assert output_dir is not None from distutils.ccompiler import new_compiler + from distutils import log + log.set_verbosity(3) compiler = new_compiler() compiler.output_dir = output_dir @@ -72,7 +74,8 @@ ccflags = ['-fPIC', '-Wimplicit-function-declaration'] res = compiler.compile([os.path.join(thisdir, csource)], include_dirs=[include_dir], - extra_preargs=ccflags) + extra_preargs=ccflags, + ) object_filename = res[0] # set link options diff --git a/lib_pypy/_sqlite3.py b/lib_pypy/_sqlite3.py --- a/lib_pypy/_sqlite3.py +++ b/lib_pypy/_sqlite3.py @@ -746,6 +746,8 @@ self.__initialized = True def close(self): + if not self.__initialized: + raise ProgrammingError("Base Cursor.__init__ not called.") self.__connection._check_thread() self.__connection._check_closed() if self.__statement: @@ -1043,6 +1045,7 @@ return list(self) def __get_connection(self): + self.__check_cursor() return self.__connection connection = property(__get_connection) diff --git a/lib_pypy/_stat.py b/lib_pypy/_stat.py deleted file mode 100644 --- a/lib_pypy/_stat.py +++ /dev/null @@ -1,6 +0,0 @@ -# Assume not Solaris - -S_IFDOOR = 0 - -def S_ISDOOR(mode): - return False diff --git a/lib_pypy/_testcapimodule.c b/lib_pypy/_testcapimodule.c --- a/lib_pypy/_testcapimodule.c +++ b/lib_pypy/_testcapimodule.c @@ -3132,6 +3132,8 @@ return PyLong_FromLong(r); } +#endif /* PYPY_VERSION */ + static int check_time_rounding(int round) { @@ -3192,6 +3194,8 @@ return Py_BuildValue("Nl", _PyLong_FromTime_t(sec), nsec); } +#ifndef PYPY_VERSION + static void slot_tp_del(PyObject *self) { @@ -4048,8 +4052,6 @@ Py_RETURN_NONE; } -#ifndef PYPY_VERSION - static PyObject * test_pytime_fromseconds(PyObject *self, PyObject *args) { @@ -4187,6 +4189,8 @@ return _PyTime_AsNanosecondsObject(ms); } +#ifndef PYPY_VERSION + static PyObject* get_recursion_depth(PyObject *self, PyObject *args) { @@ -4809,9 +4813,11 @@ {"crash_no_current_thread", (PyCFunction)crash_no_current_thread, METH_NOARGS}, #ifndef PYPY_VERSION {"run_in_subinterp", run_in_subinterp, METH_VARARGS}, +#endif {"pytime_object_to_time_t", test_pytime_object_to_time_t, METH_VARARGS}, {"pytime_object_to_timeval", test_pytime_object_to_timeval, METH_VARARGS}, {"pytime_object_to_timespec", test_pytime_object_to_timespec, METH_VARARGS}, +#ifndef PYPY_VERSION {"with_tp_del", with_tp_del, METH_VARARGS}, #endif {"create_cfunction", create_cfunction, METH_NOARGS}, @@ -4877,7 +4883,6 @@ return_null_without_error, METH_NOARGS}, {"return_result_with_error", return_result_with_error, METH_NOARGS}, -#ifndef PYPY_VERSION {"PyTime_FromSeconds", test_pytime_fromseconds, METH_VARARGS}, {"PyTime_FromSecondsObject", test_pytime_fromsecondsobject, METH_VARARGS}, {"PyTime_AsSecondsDouble", test_pytime_assecondsdouble, METH_VARARGS}, @@ -4887,6 +4892,7 @@ #endif {"PyTime_AsMilliseconds", test_PyTime_AsMilliseconds, METH_VARARGS}, {"PyTime_AsMicroseconds", test_PyTime_AsMicroseconds, METH_VARARGS}, +#ifndef PYPY_VERSION {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, {"pymem_buffer_overflow", pymem_buffer_overflow, METH_NOARGS}, {"pymem_api_misuse", pymem_api_misuse, METH_NOARGS}, @@ -4906,10 +4912,10 @@ {"pyobject_fastcalldict", test_pyobject_fastcalldict, METH_VARARGS}, {"pyobject_fastcallkeywords", test_pyobject_fastcallkeywords, METH_VARARGS}, {"stack_pointer", stack_pointer, METH_NOARGS}, +#endif #ifdef W_STOPCODE {"W_STOPCODE", py_w_stopcode, METH_VARARGS}, #endif -#endif /* PYPY_VERSION */ {"get_mapping_keys", get_mapping_keys, METH_O}, {"get_mapping_values", get_mapping_values, METH_O}, {"get_mapping_items", get_mapping_items, METH_O}, diff --git a/lib_pypy/_tkinter/tklib_build.py b/lib_pypy/_tkinter/tklib_build.py --- a/lib_pypy/_tkinter/tklib_build.py +++ b/lib_pypy/_tkinter/tklib_build.py @@ -36,8 +36,11 @@ for _ver in ['8.6', '8.5', '']: incdirs = [] linklibs = ['tcl' + _ver, 'tk' + _ver] - if os.path.isfile(''.join(['/usr/lib/lib', linklibs[1], '.so'])): - found = True + for lib in ['/usr/lib/lib', '/usr/lib64/lib']: + if os.path.isfile(''.join([lib, linklibs[1], '.so'])): + found = True + break + if found: break if not found: sys.stderr.write("*** TCL libraries not found! Falling back...\n") diff --git a/lib_pypy/cffi/_pycparser/README b/lib_pypy/cffi/_pycparser/README --- a/lib_pypy/cffi/_pycparser/README +++ b/lib_pypy/cffi/_pycparser/README @@ -10,3 +10,8 @@ ^^^^^^^^^^^^^^^ yacctab='cffi._pycparser.yacctab', ^^^^^^^^^^^^^^^ + +Also, when updating the version of this in-place, you must regenerate the +lextab.py and yacctab.py files. They will be regenerated on import if they +are not found, so they should be removed, then regenrated, then the new +versions committed. diff --git a/lib_pypy/cffi/_pycparser/__init__.py b/lib_pypy/cffi/_pycparser/__init__.py --- a/lib_pypy/cffi/_pycparser/__init__.py +++ b/lib_pypy/cffi/_pycparser/__init__.py @@ -4,12 +4,14 @@ # This package file exports some convenience functions for # interacting with pycparser # -# Copyright (C) 2008-2015, Eli Bendersky +# Eli Bendersky [https://eli.thegreenplace.net/] # License: BSD #----------------------------------------------------------------- __all__ = ['c_lexer', 'c_parser', 'c_ast'] -__version__ = '2.14' +__version__ = '2.19' +import io +from subprocess import check_output from .c_parser import CParser @@ -27,7 +29,6 @@ When successful, returns the preprocessed file's contents. Errors from cpp will be printed out. """ - from subprocess import Popen, PIPE path_list = [cpp_path] if isinstance(cpp_args, list): path_list += cpp_args @@ -38,11 +39,7 @@ try: # Note the use of universal_newlines to treat all newlines # as \n for Python's purpose - # - pipe = Popen( path_list, - stdout=PIPE, - universal_newlines=True) - text = pipe.communicate()[0] + text = check_output(path_list, universal_newlines=True) except OSError as e: raise RuntimeError("Unable to invoke 'cpp'. " + 'Make sure its path was passed correctly\n' + @@ -85,7 +82,7 @@ if use_cpp: text = preprocess_file(filename, cpp_path, cpp_args) else: - with open(filename, 'rU') as f: + with io.open(filename) as f: text = f.read() if parser is None: diff --git a/lib_pypy/cffi/_pycparser/_ast_gen.py b/lib_pypy/cffi/_pycparser/_ast_gen.py --- a/lib_pypy/cffi/_pycparser/_ast_gen.py +++ b/lib_pypy/cffi/_pycparser/_ast_gen.py @@ -7,7 +7,7 @@ # The design of this module was inspired by astgen.py from the # Python 2.5 code-base. # -# Copyright (C) 2008-2015, Eli Bendersky +# Eli Bendersky [https://eli.thegreenplace.net/] # License: BSD #----------------------------------------------------------------- import pprint @@ -63,6 +63,7 @@ contents: a list of contents - attributes and child nodes See comment at the top of the configuration file for details. """ + def __init__(self, name, contents): self.name = name self.all_entries = [] @@ -84,6 +85,8 @@ def generate_source(self): src = self._gen_init() src += '\n' + self._gen_children() + src += '\n' + self._gen_iter() + src += '\n' + self._gen_attr_names() return src @@ -131,6 +134,33 @@ return src + def _gen_iter(self): + src = ' def __iter__(self):\n' + + if self.all_entries: + for child in self.child: + src += ( + ' if self.%(child)s is not None:\n' + + ' yield self.%(child)s\n') % (dict(child=child)) + + for seq_child in self.seq_child: + src += ( + ' for child in (self.%(child)s or []):\n' + ' yield child\n') % (dict(child=seq_child)) + + if not (self.child or self.seq_child): + # Empty generator + src += ( + ' return\n' + + ' yield\n') + else: + # Empty generator + src += ( + ' return\n' + + ' yield\n') + + return src + def _gen_attr_names(self): src = " attr_names = (" + ''.join("%r, " % nm for nm in self.attr) + ')' return src @@ -150,7 +180,7 @@ # # AST Node classes. # -# Copyright (C) 2008-2015, Eli Bendersky +# Eli Bendersky [https://eli.thegreenplace.net/] # License: BSD #----------------------------------------------------------------- @@ -159,11 +189,38 @@ _PROLOGUE_CODE = r''' import sys +def _repr(obj): + """ + Get the representation of an object, with dedicated pprint-like format for lists. + """ + if isinstance(obj, list): + return '[' + (',\n '.join((_repr(e).replace('\n', '\n ') for e in obj))) + '\n]' + else: + return repr(obj) class Node(object): __slots__ = () """ Abstract base class for AST nodes. """ + def __repr__(self): + """ Generates a python representation of the current node + """ + result = self.__class__.__name__ + '(' + + indent = '' + separator = '' + for name in self.__slots__[:-2]: + result += separator + result += indent + result += name + '=' + (_repr(getattr(self, name)).replace('\n', '\n ' + (' ' * (len(name) + len(self.__class__.__name__))))) + + separator = ',' + indent = '\n ' + (' ' * len(self.__class__.__name__)) + + result += indent + ')' + + return result + def children(self): """ A sequence of all children that are Nodes """ @@ -253,26 +310,29 @@ * Modeled after Python's own AST visiting facilities (the ast module of Python 3.0) """ + + _method_cache = None + def visit(self, node): """ Visit a node. """ - method = 'visit_' + node.__class__.__name__ - visitor = getattr(self, method, self.generic_visit) + + if self._method_cache is None: + self._method_cache = {} + + visitor = self._method_cache.get(node.__class__.__name__, None) + if visitor is None: + method = 'visit_' + node.__class__.__name__ + visitor = getattr(self, method, self.generic_visit) + self._method_cache[node.__class__.__name__] = visitor + return visitor(node) def generic_visit(self, node): """ Called if no explicit visitor function exists for a node. Implements preorder visiting of the node. """ - for c_name, c in node.children(): + for c in node: self.visit(c) - ''' - - -if __name__ == "__main__": - import sys - ast_gen = ASTCodeGenerator('_c_ast.cfg') - ast_gen.generate(open('c_ast.py', 'w')) - diff --git a/lib_pypy/cffi/_pycparser/_build_tables.py b/lib_pypy/cffi/_pycparser/_build_tables.py --- a/lib_pypy/cffi/_pycparser/_build_tables.py +++ b/lib_pypy/cffi/_pycparser/_build_tables.py @@ -6,17 +6,21 @@ # Also generates AST code from the configuration file. # Should be called from the pycparser directory. # -# Copyright (C) 2008-2015, Eli Bendersky +# Eli Bendersky [https://eli.thegreenplace.net/] # License: BSD #----------------------------------------------------------------- +# Insert '.' and '..' as first entries to the search path for modules. +# Restricted environments like embeddable python do not include the +# current working directory on startup. +import sys +sys.path[0:0] = ['.', '..'] + # Generate c_ast.py from _ast_gen import ASTCodeGenerator ast_gen = ASTCodeGenerator('_c_ast.cfg') ast_gen.generate(open('c_ast.py', 'w')) -import sys -sys.path[0:0] = ['.', '..'] from pycparser import c_parser # Generates the tables diff --git a/lib_pypy/cffi/_pycparser/_c_ast.cfg b/lib_pypy/cffi/_pycparser/_c_ast.cfg --- a/lib_pypy/cffi/_pycparser/_c_ast.cfg +++ b/lib_pypy/cffi/_pycparser/_c_ast.cfg @@ -9,7 +9,7 @@ # ** - a sequence of child nodes # - an attribute # -# Copyright (C) 2008-2015, Eli Bendersky +# Eli Bendersky [https://eli.thegreenplace.net/] # License: BSD #----------------------------------------------------------------- @@ -187,3 +187,5 @@ Union: [name, decls**] While: [cond*, stmt*] + +Pragma: [string] diff --git a/lib_pypy/cffi/_pycparser/ast_transforms.py b/lib_pypy/cffi/_pycparser/ast_transforms.py --- a/lib_pypy/cffi/_pycparser/ast_transforms.py +++ b/lib_pypy/cffi/_pycparser/ast_transforms.py @@ -3,7 +3,7 @@ # # Some utilities used by the parser to create a friendlier AST. # -# Copyright (C) 2008-2015, Eli Bendersky +# Eli Bendersky [https://eli.thegreenplace.net/] # License: BSD #------------------------------------------------------------------------------ @@ -43,7 +43,7 @@ Default: break - The goal of this transform it to fix this mess, turning it into the + The goal of this transform is to fix this mess, turning it into the following: Switch @@ -74,7 +74,8 @@ # Goes over the children of the Compound below the Switch, adding them # either directly below new_compound or below the last Case as appropriate - for child in switch_node.stmt.block_items: + # (for `switch(cond) {}`, block_items would have been None) + for child in (switch_node.stmt.block_items or []): if isinstance(child, (c_ast.Case, c_ast.Default)): # If it's a Case/Default: # 1. Add it to the Compound and mark as "last case" diff --git a/lib_pypy/cffi/_pycparser/c_ast.py b/lib_pypy/cffi/_pycparser/c_ast.py --- a/lib_pypy/cffi/_pycparser/c_ast.py +++ b/lib_pypy/cffi/_pycparser/c_ast.py @@ -11,18 +11,45 @@ # # AST Node classes. # -# Copyright (C) 2008-2015, Eli Bendersky +# Eli Bendersky [https://eli.thegreenplace.net/] # License: BSD #----------------------------------------------------------------- import sys +def _repr(obj): + """ + Get the representation of an object, with dedicated pprint-like format for lists. + """ + if isinstance(obj, list): + return '[' + (',\n '.join((_repr(e).replace('\n', '\n ') for e in obj))) + '\n]' + else: + return repr(obj) class Node(object): __slots__ = () """ Abstract base class for AST nodes. """ + def __repr__(self): + """ Generates a python representation of the current node + """ + result = self.__class__.__name__ + '(' + + indent = '' + separator = '' + for name in self.__slots__[:-2]: + result += separator + result += indent + result += name + '=' + (_repr(getattr(self, name)).replace('\n', '\n ' + (' ' * (len(name) + len(self.__class__.__name__))))) + + separator = ',' + indent = '\n ' + (' ' * len(self.__class__.__name__)) + + result += indent + ')' + + return result + def children(self): """ A sequence of all children that are Nodes """ @@ -112,21 +139,31 @@ * Modeled after Python's own AST visiting facilities (the ast module of Python 3.0) """ + + _method_cache = None + def visit(self, node): """ Visit a node. """ - method = 'visit_' + node.__class__.__name__ - visitor = getattr(self, method, self.generic_visit) + + if self._method_cache is None: + self._method_cache = {} + + visitor = self._method_cache.get(node.__class__.__name__, None) + if visitor is None: + method = 'visit_' + node.__class__.__name__ + visitor = getattr(self, method, self.generic_visit) + self._method_cache[node.__class__.__name__] = visitor + return visitor(node) def generic_visit(self, node): """ Called if no explicit visitor function exists for a node. Implements preorder visiting of the node. """ - for c_name, c in node.children(): + for c in node: self.visit(c) - class ArrayDecl(Node): __slots__ = ('type', 'dim', 'dim_quals', 'coord', '__weakref__') def __init__(self, type, dim, dim_quals, coord=None): @@ -141,6 +178,12 @@ if self.dim is not None: nodelist.append(("dim", self.dim)) return tuple(nodelist) + def __iter__(self): + if self.type is not None: + yield self.type + if self.dim is not None: + yield self.dim + attr_names = ('dim_quals', ) class ArrayRef(Node): @@ -156,6 +199,12 @@ if self.subscript is not None: nodelist.append(("subscript", self.subscript)) return tuple(nodelist) + def __iter__(self): + if self.name is not None: + yield self.name + if self.subscript is not None: + yield self.subscript + attr_names = () class Assignment(Node): @@ -172,6 +221,12 @@ if self.rvalue is not None: nodelist.append(("rvalue", self.rvalue)) return tuple(nodelist) + def __iter__(self): + if self.lvalue is not None: + yield self.lvalue + if self.rvalue is not None: + yield self.rvalue + attr_names = ('op', ) class BinaryOp(Node): @@ -188,6 +243,12 @@ if self.right is not None: nodelist.append(("right", self.right)) return tuple(nodelist) + def __iter__(self): + if self.left is not None: + yield self.left + if self.right is not None: + yield self.right + attr_names = ('op', ) class Break(Node): @@ -198,6 +259,10 @@ def children(self): return () + def __iter__(self): + return + yield + attr_names = () class Case(Node): @@ -214,6 +279,12 @@ nodelist.append(("stmts[%d]" % i, child)) return tuple(nodelist) + def __iter__(self): + if self.expr is not None: + yield self.expr + for child in (self.stmts or []): + yield child + attr_names = () class Cast(Node): @@ -229,6 +300,12 @@ if self.expr is not None: nodelist.append(("expr", self.expr)) return tuple(nodelist) + def __iter__(self): + if self.to_type is not None: + yield self.to_type + if self.expr is not None: + yield self.expr + attr_names = () class Compound(Node): @@ -243,6 +320,10 @@ nodelist.append(("block_items[%d]" % i, child)) return tuple(nodelist) + def __iter__(self): + for child in (self.block_items or []): + yield child + attr_names = () class CompoundLiteral(Node): @@ -258,6 +339,12 @@ if self.init is not None: nodelist.append(("init", self.init)) return tuple(nodelist) + def __iter__(self): + if self.type is not None: + yield self.type + if self.init is not None: + yield self.init + attr_names = () class Constant(Node): @@ -271,6 +358,10 @@ nodelist = [] return tuple(nodelist) + def __iter__(self): + return + yield + attr_names = ('type', 'value', ) class Continue(Node): @@ -281,6 +372,10 @@ def children(self): return () + def __iter__(self): + return + yield + attr_names = () class Decl(Node): @@ -302,6 +397,14 @@ if self.bitsize is not None: nodelist.append(("bitsize", self.bitsize)) return tuple(nodelist) + def __iter__(self): + if self.type is not None: + yield self.type + if self.init is not None: + yield self.init + if self.bitsize is not None: + yield self.bitsize + attr_names = ('name', 'quals', 'storage', 'funcspec', ) class DeclList(Node): @@ -316,6 +419,10 @@ nodelist.append(("decls[%d]" % i, child)) return tuple(nodelist) + def __iter__(self): + for child in (self.decls or []): + yield child + attr_names = () class Default(Node): @@ -330,6 +437,10 @@ nodelist.append(("stmts[%d]" % i, child)) return tuple(nodelist) + def __iter__(self): + for child in (self.stmts or []): + yield child + attr_names = () class DoWhile(Node): @@ -345,6 +456,12 @@ if self.stmt is not None: nodelist.append(("stmt", self.stmt)) return tuple(nodelist) + def __iter__(self): + if self.cond is not None: + yield self.cond + if self.stmt is not None: + yield self.stmt + attr_names = () class EllipsisParam(Node): @@ -355,6 +472,10 @@ def children(self): return () + def __iter__(self): + return + yield + attr_names = () class EmptyStatement(Node): @@ -365,6 +486,10 @@ def children(self): return () + def __iter__(self): + return + yield + attr_names = () class Enum(Node): @@ -379,6 +504,10 @@ if self.values is not None: nodelist.append(("values", self.values)) return tuple(nodelist) + def __iter__(self): + if self.values is not None: + yield self.values + attr_names = ('name', ) class Enumerator(Node): @@ -393,6 +522,10 @@ if self.value is not None: nodelist.append(("value", self.value)) return tuple(nodelist) + def __iter__(self): + if self.value is not None: + yield self.value + attr_names = ('name', ) class EnumeratorList(Node): @@ -407,6 +540,10 @@ nodelist.append(("enumerators[%d]" % i, child)) return tuple(nodelist) + def __iter__(self): + for child in (self.enumerators or []): + yield child + attr_names = () class ExprList(Node): @@ -421,6 +558,10 @@ nodelist.append(("exprs[%d]" % i, child)) return tuple(nodelist) + def __iter__(self): + for child in (self.exprs or []): + yield child + attr_names = () class FileAST(Node): @@ -435,6 +576,10 @@ nodelist.append(("ext[%d]" % i, child)) return tuple(nodelist) + def __iter__(self): + for child in (self.ext or []): + yield child + attr_names = () class For(Node): @@ -454,6 +599,16 @@ if self.stmt is not None: nodelist.append(("stmt", self.stmt)) return tuple(nodelist) + def __iter__(self): + if self.init is not None: + yield self.init + if self.cond is not None: + yield self.cond + if self.next is not None: + yield self.next + if self.stmt is not None: + yield self.stmt + attr_names = () class FuncCall(Node): @@ -469,6 +624,12 @@ if self.args is not None: nodelist.append(("args", self.args)) return tuple(nodelist) + def __iter__(self): + if self.name is not None: + yield self.name + if self.args is not None: + yield self.args + attr_names = () class FuncDecl(Node): @@ -484,6 +645,12 @@ if self.type is not None: nodelist.append(("type", self.type)) return tuple(nodelist) + def __iter__(self): + if self.args is not None: + yield self.args + if self.type is not None: + yield self.type + attr_names = () class FuncDef(Node): @@ -502,6 +669,14 @@ nodelist.append(("param_decls[%d]" % i, child)) return tuple(nodelist) + def __iter__(self): + if self.decl is not None: + yield self.decl + if self.body is not None: + yield self.body + for child in (self.param_decls or []): + yield child + attr_names = () class Goto(Node): @@ -514,6 +689,10 @@ nodelist = [] return tuple(nodelist) + def __iter__(self): + return + yield + attr_names = ('name', ) class ID(Node): @@ -526,6 +705,10 @@ nodelist = [] return tuple(nodelist) + def __iter__(self): + return + yield + attr_names = ('name', ) class IdentifierType(Node): @@ -538,6 +721,10 @@ nodelist = [] return tuple(nodelist) + def __iter__(self): + return + yield + attr_names = ('names', ) class If(Node): @@ -555,6 +742,14 @@ if self.iffalse is not None: nodelist.append(("iffalse", self.iffalse)) return tuple(nodelist) + def __iter__(self): + if self.cond is not None: + yield self.cond + if self.iftrue is not None: + yield self.iftrue + if self.iffalse is not None: + yield self.iffalse + attr_names = () class InitList(Node): @@ -569,6 +764,10 @@ nodelist.append(("exprs[%d]" % i, child)) return tuple(nodelist) + def __iter__(self): + for child in (self.exprs or []): + yield child + attr_names = () class Label(Node): @@ -583,6 +782,10 @@ if self.stmt is not None: nodelist.append(("stmt", self.stmt)) return tuple(nodelist) + def __iter__(self): + if self.stmt is not None: + yield self.stmt + attr_names = ('name', ) class NamedInitializer(Node): @@ -599,6 +802,12 @@ nodelist.append(("name[%d]" % i, child)) return tuple(nodelist) + def __iter__(self): + if self.expr is not None: + yield self.expr + for child in (self.name or []): + yield child + attr_names = () class ParamList(Node): @@ -613,6 +822,10 @@ nodelist.append(("params[%d]" % i, child)) return tuple(nodelist) + def __iter__(self): + for child in (self.params or []): + yield child + attr_names = () class PtrDecl(Node): @@ -627,6 +840,10 @@ if self.type is not None: nodelist.append(("type", self.type)) return tuple(nodelist) + def __iter__(self): + if self.type is not None: + yield self.type + attr_names = ('quals', ) class Return(Node): @@ -640,6 +857,10 @@ if self.expr is not None: nodelist.append(("expr", self.expr)) return tuple(nodelist) + def __iter__(self): + if self.expr is not None: + yield self.expr + attr_names = () class Struct(Node): @@ -655,6 +876,10 @@ nodelist.append(("decls[%d]" % i, child)) return tuple(nodelist) + def __iter__(self): + for child in (self.decls or []): + yield child + attr_names = ('name', ) class StructRef(Node): @@ -671,6 +896,12 @@ if self.field is not None: nodelist.append(("field", self.field)) return tuple(nodelist) + def __iter__(self): + if self.name is not None: + yield self.name + if self.field is not None: + yield self.field + attr_names = ('type', ) class Switch(Node): @@ -686,6 +917,12 @@ if self.stmt is not None: nodelist.append(("stmt", self.stmt)) return tuple(nodelist) + def __iter__(self): + if self.cond is not None: + yield self.cond + if self.stmt is not None: + yield self.stmt + attr_names = () class TernaryOp(Node): @@ -703,6 +940,14 @@ if self.iffalse is not None: nodelist.append(("iffalse", self.iffalse)) return tuple(nodelist) + def __iter__(self): + if self.cond is not None: + yield self.cond + if self.iftrue is not None: + yield self.iftrue + if self.iffalse is not None: + yield self.iffalse + attr_names = () class TypeDecl(Node): @@ -718,6 +963,10 @@ if self.type is not None: nodelist.append(("type", self.type)) return tuple(nodelist) + def __iter__(self): + if self.type is not None: + yield self.type + attr_names = ('declname', 'quals', ) class Typedef(Node): @@ -734,6 +983,10 @@ if self.type is not None: nodelist.append(("type", self.type)) return tuple(nodelist) + def __iter__(self): + if self.type is not None: + yield self.type + attr_names = ('name', 'quals', 'storage', ) class Typename(Node): @@ -749,6 +1002,10 @@ if self.type is not None: nodelist.append(("type", self.type)) return tuple(nodelist) + def __iter__(self): + if self.type is not None: + yield self.type + attr_names = ('name', 'quals', ) class UnaryOp(Node): @@ -763,6 +1020,10 @@ if self.expr is not None: nodelist.append(("expr", self.expr)) return tuple(nodelist) + def __iter__(self): + if self.expr is not None: + yield self.expr + attr_names = ('op', ) class Union(Node): @@ -778,6 +1039,10 @@ nodelist.append(("decls[%d]" % i, child)) return tuple(nodelist) + def __iter__(self): + for child in (self.decls or []): + yield child + attr_names = ('name', ) class While(Node): @@ -793,5 +1058,27 @@ if self.stmt is not None: nodelist.append(("stmt", self.stmt)) return tuple(nodelist) + def __iter__(self): + if self.cond is not None: + yield self.cond + if self.stmt is not None: + yield self.stmt + attr_names = () +class Pragma(Node): + __slots__ = ('string', 'coord', '__weakref__') + def __init__(self, string, coord=None): + self.string = string + self.coord = coord + + def children(self): + nodelist = [] + return tuple(nodelist) + + def __iter__(self): + return + yield + + attr_names = ('string', ) + diff --git a/lib_pypy/cffi/_pycparser/c_generator.py b/lib_pypy/cffi/_pycparser/c_generator.py --- a/lib_pypy/cffi/_pycparser/c_generator.py +++ b/lib_pypy/cffi/_pycparser/c_generator.py @@ -3,7 +3,7 @@ # # C code generator from pycparser AST nodes. # -# Copyright (C) 2008-2015, Eli Bendersky +# Eli Bendersky [https://eli.thegreenplace.net/] # License: BSD #------------------------------------------------------------------------------ from . import c_ast @@ -40,6 +40,12 @@ def visit_ID(self, n): return n.name + def visit_Pragma(self, n): + ret = '#pragma' + if n.string: + ret += ' ' + n.string + return ret + def visit_ArrayRef(self, n): arrref = self._parenthesize_unless_simple(n.name) return arrref + '[' + self.visit(n.subscript) + ']' @@ -113,7 +119,7 @@ return s def visit_Cast(self, n): - s = '(' + self._generate_type(n.to_type) + ')' + s = '(' + self._generate_type(n.to_type, emit_declname=False) + ')' return s + ' ' + self._parenthesize_unless_simple(n.expr) def visit_ExprList(self, n): @@ -129,18 +135,20 @@ return ', '.join(visited_subexprs) def visit_Enum(self, n): - s = 'enum' - if n.name: s += ' ' + n.name - if n.values: - s += ' {' - for i, enumerator in enumerate(n.values.enumerators): - s += enumerator.name - if enumerator.value: - s += ' = ' + self.visit(enumerator.value) - if i != len(n.values.enumerators) - 1: - s += ', ' - s += '}' - return s + return self._generate_struct_union_enum(n, name='enum') + + def visit_Enumerator(self, n): + if not n.value: + return '{indent}{name},\n'.format( + indent=self._make_indent(), + name=n.name, + ) + else: + return '{indent}{name} = {value},\n'.format( + indent=self._make_indent(), + name=n.name, + value=self.visit(n.value), + ) def visit_FuncDef(self, n): decl = self.visit(n.decl) @@ -157,6 +165,8 @@ for ext in n.ext: if isinstance(ext, c_ast.FuncDef): s += self.visit(ext) + elif isinstance(ext, c_ast.Pragma): + s += self.visit(ext) + '\n' else: s += self.visit(ext) + ';\n' return s @@ -170,6 +180,10 @@ s += self._make_indent() + '}\n' return s + def visit_CompoundLiteral(self, n): + return '(' + self.visit(n.type) + '){' + self.visit(n.init) + '}' + + def visit_EmptyStatement(self, n): return ';' @@ -188,9 +202,9 @@ return 'continue;' def visit_TernaryOp(self, n): - s = self._visit_expr(n.cond) + ' ? ' - s += self._visit_expr(n.iftrue) + ' : ' - s += self._visit_expr(n.iffalse) + s = '(' + self._visit_expr(n.cond) + ') ? ' + s += '(' + self._visit_expr(n.iftrue) + ') : ' + s += '(' + self._visit_expr(n.iffalse) + ')' return s def visit_If(self, n): @@ -256,43 +270,67 @@ return '...' def visit_Struct(self, n): - return self._generate_struct_union(n, 'struct') + return self._generate_struct_union_enum(n, 'struct') def visit_Typename(self, n): return self._generate_type(n.type) def visit_Union(self, n): - return self._generate_struct_union(n, 'union') + return self._generate_struct_union_enum(n, 'union') def visit_NamedInitializer(self, n): s = '' for name in n.name: if isinstance(name, c_ast.ID): s += '.' + name.name - elif isinstance(name, c_ast.Constant): - s += '[' + name.value + ']' - s += ' = ' + self.visit(n.expr) + else: + s += '[' + self.visit(name) + ']' + s += ' = ' + self._visit_expr(n.expr) return s def visit_FuncDecl(self, n): return self._generate_type(n) - def _generate_struct_union(self, n, name): - """ Generates code for structs and unions. name should be either - 'struct' or union. + def visit_ArrayDecl(self, n): + return self._generate_type(n, emit_declname=False) + + def visit_TypeDecl(self, n): + return self._generate_type(n, emit_declname=False) + + def visit_PtrDecl(self, n): + return self._generate_type(n, emit_declname=False) + + def _generate_struct_union_enum(self, n, name): + """ Generates code for structs, unions, and enums. name should be + 'struct', 'union', or 'enum'. """ + if name in ('struct', 'union'): + members = n.decls + body_function = self._generate_struct_union_body + else: + assert name == 'enum' + members = None if n.values is None else n.values.enumerators + body_function = self._generate_enum_body s = name + ' ' + (n.name or '') - if n.decls: + if members is not None: + # None means no members + # Empty sequence means an empty list of members s += '\n' s += self._make_indent() self.indent_level += 2 s += '{\n' - for decl in n.decls: - s += self._generate_stmt(decl) + s += body_function(members) self.indent_level -= 2 s += self._make_indent() + '}' return s + def _generate_struct_union_body(self, members): + return ''.join(self._generate_stmt(decl) for decl in members) + + def _generate_enum_body(self, members): + # `[:-2] + '\n'` removes the final `,` from the enumerator list + return ''.join(self.visit(value) for value in members)[:-2] + '\n' + def _generate_stmt(self, n, add_indent=False): """ Generation from a statement node. This method exists as a wrapper for individual visit_* methods to handle different treatment of @@ -330,7 +368,7 @@ s += self._generate_type(n.type) return s - def _generate_type(self, n, modifiers=[]): + def _generate_type(self, n, modifiers=[], emit_declname = True): """ Recursive generation from a type node. n is the type node. modifiers collects the PtrDecl, ArrayDecl and FuncDecl modifiers encountered on the way down to a TypeDecl, to allow proper @@ -344,23 +382,29 @@ if n.quals: s += ' '.join(n.quals) + ' ' s += self.visit(n.type) - nstr = n.declname if n.declname else '' + nstr = n.declname if n.declname and emit_declname else '' # Resolve modifiers. # Wrap in parens to distinguish pointer to array and pointer to # function syntax. # for i, modifier in enumerate(modifiers): if isinstance(modifier, c_ast.ArrayDecl): - if (i != 0 and isinstance(modifiers[i - 1], c_ast.PtrDecl)): - nstr = '(' + nstr + ')' - nstr += '[' + self.visit(modifier.dim) + ']' + if (i != 0 and + isinstance(modifiers[i - 1], c_ast.PtrDecl)): + nstr = '(' + nstr + ')' + nstr += '[' + if modifier.dim_quals: + nstr += ' '.join(modifier.dim_quals) + ' ' + nstr += self.visit(modifier.dim) + ']' elif isinstance(modifier, c_ast.FuncDecl): - if (i != 0 and isinstance(modifiers[i - 1], c_ast.PtrDecl)): - nstr = '(' + nstr + ')' + if (i != 0 and + isinstance(modifiers[i - 1], c_ast.PtrDecl)): + nstr = '(' + nstr + ')' nstr += '(' + self.visit(modifier.args) + ')' elif isinstance(modifier, c_ast.PtrDecl): if modifier.quals: - nstr = '* %s %s' % (' '.join(modifier.quals), nstr) + nstr = '* %s%s' % (' '.join(modifier.quals), + ' ' + nstr if nstr else '') else: nstr = '*' + nstr if nstr: s += ' ' + nstr @@ -368,11 +412,12 @@ elif typ == c_ast.Decl: return self._generate_decl(n.type) elif typ == c_ast.Typename: - return self._generate_type(n.type) + return self._generate_type(n.type, emit_declname = emit_declname) elif typ == c_ast.IdentifierType: return ' '.join(n.names) + ' ' elif typ in (c_ast.ArrayDecl, c_ast.PtrDecl, c_ast.FuncDecl): - return self._generate_type(n.type, modifiers + [n]) + return self._generate_type(n.type, modifiers + [n], + emit_declname = emit_declname) else: return self.visit(n) @@ -395,5 +440,5 @@ """ Returns True for nodes that are "simple" - i.e. nodes that always have higher precedence than operators. """ - return isinstance(n,( c_ast.Constant, c_ast.ID, c_ast.ArrayRef, - c_ast.StructRef, c_ast.FuncCall)) + return isinstance(n, (c_ast.Constant, c_ast.ID, c_ast.ArrayRef, + c_ast.StructRef, c_ast.FuncCall)) diff --git a/lib_pypy/cffi/_pycparser/c_lexer.py b/lib_pypy/cffi/_pycparser/c_lexer.py --- a/lib_pypy/cffi/_pycparser/c_lexer.py +++ b/lib_pypy/cffi/_pycparser/c_lexer.py @@ -3,7 +3,7 @@ # # CLexer class: lexer for the C language # -# Copyright (C) 2008-2015, Eli Bendersky +# Eli Bendersky [https://eli.thegreenplace.net/] # License: BSD #------------------------------------------------------------------------------ import re @@ -19,7 +19,7 @@ tokens. The public attribute filename can be set to an initial - filaneme, but the lexer will update it upon #line + filename, but the lexer will update it upon #line directives. """ def __init__(self, error_func, on_lbrace_func, on_rbrace_func, @@ -52,8 +52,8 @@ # Allow either "# line" or "# " to support GCC's # cpp output # - self.line_pattern = re.compile('([ \t]*line\W)|([ \t]*\d+)') - self.pragma_pattern = re.compile('[ \t]*pragma\W') + self.line_pattern = re.compile(r'([ \t]*line\W)|([ \t]*\d+)') + self.pragma_pattern = re.compile(r'[ \t]*pragma\W') def build(self, **kwargs): """ Builds the lexer from the specification. Must be @@ -102,11 +102,11 @@ keywords = ( '_BOOL', '_COMPLEX', 'AUTO', 'BREAK', 'CASE', 'CHAR', 'CONST', 'CONTINUE', 'DEFAULT', 'DO', 'DOUBLE', 'ELSE', 'ENUM', 'EXTERN', - 'FLOAT', 'FOR', 'GOTO', 'IF', 'INLINE', 'INT', 'LONG', + 'FLOAT', 'FOR', 'GOTO', 'IF', 'INLINE', 'INT', 'LONG', 'REGISTER', 'OFFSETOF', 'RESTRICT', 'RETURN', 'SHORT', 'SIGNED', 'SIZEOF', 'STATIC', 'STRUCT', 'SWITCH', 'TYPEDEF', 'UNION', 'UNSIGNED', 'VOID', - 'VOLATILE', 'WHILE', + 'VOLATILE', 'WHILE', '__INT128', ) keyword_map = {} @@ -171,7 +171,9 @@ 'ELLIPSIS', # pre-processor - 'PPHASH', # '#' + 'PPHASH', # '#' + 'PPPRAGMA', # 'pragma' + 'PPPRAGMASTR', ) ## @@ -203,12 +205,37 @@ # parse all correct code, even if it means to sometimes parse incorrect # code. # - simple_escape = r"""([a-zA-Z._~!=&\^\-\\?'"])""" - decimal_escape = r"""(\d+)""" - hex_escape = r"""(x[0-9a-fA-F]+)""" - bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-7])""" + # The original regexes were taken verbatim from the C syntax definition, + # and were later modified to avoid worst-case exponential running time. + # + # simple_escape = r"""([a-zA-Z._~!=&\^\-\\?'"])""" + # decimal_escape = r"""(\d+)""" + # hex_escape = r"""(x[0-9a-fA-F]+)""" + # bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-7])""" + # + # The following modifications were made to avoid the ambiguity that allowed backtracking: + # (https://github.com/eliben/pycparser/issues/61) + # + # - \x was removed from simple_escape, unless it was not followed by a hex digit, to avoid ambiguity with hex_escape. + # - hex_escape allows one or more hex characters, but requires that the next character(if any) is not hex + # - decimal_escape allows one or more decimal characters, but requires that the next character(if any) is not a decimal + # - bad_escape does not allow any decimals (8-9), to avoid conflicting with the permissive decimal_escape. + # + # Without this change, python's `re` module would recursively try parsing each ambiguous escape sequence in multiple ways. + # e.g. `\123` could be parsed as `\1`+`23`, `\12`+`3`, and `\123`. + + simple_escape = r"""([a-wyzA-Z._~!=&\^\-\\?'"]|x(?![0-9a-fA-F]))""" + decimal_escape = r"""(\d+)(?!\d)""" + hex_escape = r"""(x[0-9a-fA-F]+)(?![0-9a-fA-F])""" + bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-9])""" escape_sequence = r"""(\\("""+simple_escape+'|'+decimal_escape+'|'+hex_escape+'))' + + # This complicated regex with lookahead might be slow for strings, so because all of the valid escapes (including \x) allowed + # 0 or more non-escaped characters after the first character, simple_escape+decimal_escape+hex_escape got simplified to + + escape_sequence_start_in_string = r"""(\\[0-9a-zA-Z._~!=&\^\-\\?'"])""" + cconst_char = r"""([^'\\\n]|"""+escape_sequence+')' char_const = "'"+cconst_char+"'" wchar_const = 'L'+char_const @@ -216,7 +243,7 @@ bad_char_const = r"""('"""+cconst_char+"""[^'\n]+')|('')|('"""+bad_escape+r"""[^'\n]*')""" # string literals (K&R2: A.2.6) - string_char = r"""([^"\\\n]|"""+escape_sequence+')' + string_char = r"""([^"\\\n]|"""+escape_sequence_start_in_string+')' string_literal = '"'+string_char+'*"' wstring_literal = 'L'+string_literal bad_string_literal = '"'+string_char+'*'+bad_escape+string_char+'*"' @@ -274,7 +301,6 @@ def t_ppline_NEWLINE(self, t): r'\n' - if self.pp_line is None: self._error('line number missing in #line', t) else: @@ -304,15 +330,14 @@ def t_pppragma_PPPRAGMA(self, t): r'pragma' - pass + return t - t_pppragma_ignore = ' \t<>.-{}();=+-*/$%@&^~!?:,0123456789' + t_pppragma_ignore = ' \t' - @TOKEN(string_literal) - def t_pppragma_STR(self, t): pass - - @TOKEN(identifier) - def t_pppragma_ID(self, t): pass + def t_pppragma_STR(self, t): + '.+' + t.type = 'PPPRAGMASTR' + return t def t_pppragma_error(self, t): self._error('invalid #pragma directive', t) @@ -482,4 +507,3 @@ def t_error(self, t): msg = 'Illegal character %s' % repr(t.value[0]) self._error(msg, t) - diff --git a/lib_pypy/cffi/_pycparser/c_parser.py b/lib_pypy/cffi/_pycparser/c_parser.py --- a/lib_pypy/cffi/_pycparser/c_parser.py +++ b/lib_pypy/cffi/_pycparser/c_parser.py @@ -3,7 +3,7 @@ # # CParser class: Parser and AST builder for the C language # -# Copyright (C) 2008-2015, Eli Bendersky +# Eli Bendersky [https://eli.thegreenplace.net/] # License: BSD #------------------------------------------------------------------------------ import re @@ -12,14 +12,16 @@ from . import c_ast from .c_lexer import CLexer -from .plyparser import PLYParser, Coord, ParseError +from .plyparser import PLYParser, Coord, ParseError, parameterized, template from .ast_transforms import fix_switch_cases + at template class CParser(PLYParser): def __init__( self, lex_optimize=True, + lexer=CLexer, lextab='cffi._pycparser.lextab', yacc_optimize=True, yacctab='cffi._pycparser.yacctab', @@ -42,6 +44,10 @@ to save the re-generation of the lexer table on each run. + lexer: + Set this parameter to define the lexer to use if + you're not using the default CLexer. + lextab: Points to the lex table that's used for optimized mode. Only if you're modifying the lexer and want @@ -70,7 +76,7 @@ Set this parameter to control the location of generated lextab and yacctab files. """ - self.clex = CLexer( + self.clex = lexer( error_func=self._lex_error_func, on_lbrace_func=self._lex_on_lbrace_func, From pypy.commits at gmail.com Tue Oct 1 16:53:28 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 01 Oct 2019 13:53:28 -0700 (PDT) Subject: [pypy-commit] pypy default: tag, update repackage script Message-ID: <5d93bcc8.1c69fb81.72847.8653@mx.google.com> Author: Matti Picus Branch: Changeset: r97704:18a5bcf36924 Date: 2019-10-01 23:25 +0300 http://bitbucket.org/pypy/pypy/changeset/18a5bcf36924/ Log: tag, update repackage script diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -50,4 +50,5 @@ de061d87e39c7df4e436974096d7982c676a859d release-pypy3.6-v7.1.0 784b254d669919c872a505b807db8462b6140973 release-pypy3.6-v7.1.1 8cdda8b8cdb8ff29d9e620cccd6c5edd2f2a23ec release-pypy2.7-v7.1.1 - +85dae4fd5c234b482feff834c73e089872194541 release-pypy2.7-v7.2.0rc0 +7ffb92269488f37c707ce66076f50ffd8613f8e2 release-pypy3.6-v7.2.0rc0 diff --git a/pypy/tool/release/repackage.sh b/pypy/tool/release/repackage.sh --- a/pypy/tool/release/repackage.sh +++ b/pypy/tool/release/repackage.sh @@ -1,15 +1,14 @@ # Edit these appropriately before running this script pmaj=2 # python main version: 2 or 3 pmin=7 # python minor version -exe=pypy3 # pypy3 or pypy +exe=pypy # pypy3 or pypy maj=7 min=2 -rev=0 +rev=0rc0 branchname=release-pypy$pmaj.$pmin-v$maj.x # ==OR== release-v$maj.x # ==OR== release-v$maj.$min.x -tagname=release-candidate-pypy$pmaj.$pmin-v$maj.$min.$rev # ==OR== release-$maj.$min -# tagname=release-pypy$pmaj.$pmin-v$maj.$min.$rev # ==OR== release-$maj.$min +tagname=release-pypy$pmaj.$pmin-v$maj.$min.$rev # ==OR== release-$maj.$min echo checking hg log -r $branchname hg log -r $branchname || exit 1 From pypy.commits at gmail.com Tue Oct 1 17:03:51 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 01 Oct 2019 14:03:51 -0700 (PDT) Subject: [pypy-commit] pypy default: corner case in the json decoder: like regular object maps, don't make the json Message-ID: <5d93bf37.1c69fb81.4e3a3.3f0d@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r97705:38ede7e5cb5a Date: 2019-10-01 22:59 +0200 http://bitbucket.org/pypy/pypy/changeset/38ede7e5cb5a/ Log: corner case in the json decoder: like regular object maps, don't make the json maps arbitrarily huge diff --git a/pypy/module/_pypyjson/interp_decoder.py b/pypy/module/_pypyjson/interp_decoder.py --- a/pypy/module/_pypyjson/interp_decoder.py +++ b/pypy/module/_pypyjson/interp_decoder.py @@ -69,6 +69,9 @@ # hit in the cache STRING_CACHE_USEFULNESS_FACTOR = 4 + # don't make arbitrarily huge maps + MAX_MAP_SIZE = 100 + def __init__(self, space, s): self.space = space @@ -368,7 +371,7 @@ return w_res elif ch == ',': i = self.skip_whitespace(i) - if currmap.is_state_blocked(): + if currmap.is_state_blocked() or nextindex > self.MAX_MAP_SIZE: self.scratch.append(values_w) # can reuse next time dict_w = self._switch_to_dict(currmap, values_w, nextindex) return self.decode_object_dict(i, start, dict_w) diff --git a/pypy/module/_pypyjson/test/test__pypyjson.py b/pypy/module/_pypyjson/test/test__pypyjson.py --- a/pypy/module/_pypyjson/test/test__pypyjson.py +++ b/pypy/module/_pypyjson/test/test__pypyjson.py @@ -469,6 +469,14 @@ res = _pypyjson.loads(json) assert res == [{u'a': 1}, {u'a': 2}] + def test_huge_map(self): + import _pypyjson + import __pypy__ + s = '{' + ",".join('"%s": %s' % (i, i) for i in range(200)) + '}' + res = _pypyjson.loads(s) + assert len(res) == 200 + assert __pypy__.strategy(res) == "UnicodeDictStrategy" + def test_tab_in_string_should_fail(self): import _pypyjson # http://json.org/JSON_checker/test/fail25.json From pypy.commits at gmail.com Wed Oct 2 03:47:51 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 02 Oct 2019 00:47:51 -0700 (PDT) Subject: [pypy-commit] pypy default: Added tag release-pypy3.6-v7.2.0rc1 for changeset 4d6761df14ff Message-ID: <5d945627.1c69fb81.b44fd.5048@mx.google.com> Author: Matti Picus Branch: Changeset: r97706:51d5d7494591 Date: 2019-10-02 10:46 +0300 http://bitbucket.org/pypy/pypy/changeset/51d5d7494591/ Log: Added tag release-pypy3.6-v7.2.0rc1 for changeset 4d6761df14ff diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -52,3 +52,4 @@ 8cdda8b8cdb8ff29d9e620cccd6c5edd2f2a23ec release-pypy2.7-v7.1.1 85dae4fd5c234b482feff834c73e089872194541 release-pypy2.7-v7.2.0rc0 7ffb92269488f37c707ce66076f50ffd8613f8e2 release-pypy3.6-v7.2.0rc0 +4d6761df14ffd6f38450f183ac1fad32c946c21b release-pypy3.6-v7.2.0rc1 From pypy.commits at gmail.com Wed Oct 2 09:53:48 2019 From: pypy.commits at gmail.com (arigo) Date: Wed, 02 Oct 2019 06:53:48 -0700 (PDT) Subject: [pypy-commit] pypy default: Issue #3084 Message-ID: <5d94abec.1c69fb81.4a5d6.99a4@mx.google.com> Author: Armin Rigo Branch: Changeset: r97707:cd96ab5b8d1e Date: 2019-10-02 15:51 +0200 http://bitbucket.org/pypy/pypy/changeset/cd96ab5b8d1e/ Log: Issue #3084 Fix compilation error when building revdb diff --git a/rpython/translator/revdb/src-revdb/revdb_include.h b/rpython/translator/revdb/src-revdb/revdb_include.h --- a/rpython/translator/revdb/src-revdb/revdb_include.h +++ b/rpython/translator/revdb/src-revdb/revdb_include.h @@ -285,6 +285,8 @@ #define OP_GC_RAWREFCOUNT_NEXT_DEAD(r) \ r = rpy_reverse_db_rawrefcount_next_dead() +#define OP_GC_INCREASE_ROOT_STACK_DEPTH(depth, r) /* nothing */ + RPY_EXTERN void rpy_reverse_db_flush(void); /* must be called with the lock */ RPY_EXTERN void rpy_reverse_db_fetch(const char *file, int line); From pypy.commits at gmail.com Wed Oct 2 11:40:01 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 02 Oct 2019 08:40:01 -0700 (PDT) Subject: [pypy-commit] pypy release-pypy2.7-v7.x: corner case in the json decoder: like regular object maps, don't make the json Message-ID: <5d94c4d1.1c69fb81.20e2c.8264@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: release-pypy2.7-v7.x Changeset: r97708:6fe1105c1e17 Date: 2019-10-01 22:59 +0200 http://bitbucket.org/pypy/pypy/changeset/6fe1105c1e17/ Log: corner case in the json decoder: like regular object maps, don't make the json maps arbitrarily huge (grafted from 38ede7e5cb5ad44fdc637b8d5ee5f15849f84dbb) diff --git a/pypy/module/_pypyjson/interp_decoder.py b/pypy/module/_pypyjson/interp_decoder.py --- a/pypy/module/_pypyjson/interp_decoder.py +++ b/pypy/module/_pypyjson/interp_decoder.py @@ -69,6 +69,9 @@ # hit in the cache STRING_CACHE_USEFULNESS_FACTOR = 4 + # don't make arbitrarily huge maps + MAX_MAP_SIZE = 100 + def __init__(self, space, s): self.space = space @@ -368,7 +371,7 @@ return w_res elif ch == ',': i = self.skip_whitespace(i) - if currmap.is_state_blocked(): + if currmap.is_state_blocked() or nextindex > self.MAX_MAP_SIZE: self.scratch.append(values_w) # can reuse next time dict_w = self._switch_to_dict(currmap, values_w, nextindex) return self.decode_object_dict(i, start, dict_w) diff --git a/pypy/module/_pypyjson/test/test__pypyjson.py b/pypy/module/_pypyjson/test/test__pypyjson.py --- a/pypy/module/_pypyjson/test/test__pypyjson.py +++ b/pypy/module/_pypyjson/test/test__pypyjson.py @@ -469,6 +469,14 @@ res = _pypyjson.loads(json) assert res == [{u'a': 1}, {u'a': 2}] + def test_huge_map(self): + import _pypyjson + import __pypy__ + s = '{' + ",".join('"%s": %s' % (i, i) for i in range(200)) + '}' + res = _pypyjson.loads(s) + assert len(res) == 200 + assert __pypy__.strategy(res) == "UnicodeDictStrategy" + def test_tab_in_string_should_fail(self): import _pypyjson # http://json.org/JSON_checker/test/fail25.json From pypy.commits at gmail.com Wed Oct 2 11:40:03 2019 From: pypy.commits at gmail.com (arigo) Date: Wed, 02 Oct 2019 08:40:03 -0700 (PDT) Subject: [pypy-commit] pypy release-pypy2.7-v7.x: Issue #3084 Message-ID: <5d94c4d3.1c69fb81.716f5.9537@mx.google.com> Author: Armin Rigo Branch: release-pypy2.7-v7.x Changeset: r97709:6a59a88053cc Date: 2019-10-02 15:51 +0200 http://bitbucket.org/pypy/pypy/changeset/6a59a88053cc/ Log: Issue #3084 Fix compilation error when building revdb (grafted from cd96ab5b8d1e4364105cb4a3c21a31b5dc1a5c87) diff --git a/rpython/translator/revdb/src-revdb/revdb_include.h b/rpython/translator/revdb/src-revdb/revdb_include.h --- a/rpython/translator/revdb/src-revdb/revdb_include.h +++ b/rpython/translator/revdb/src-revdb/revdb_include.h @@ -285,6 +285,8 @@ #define OP_GC_RAWREFCOUNT_NEXT_DEAD(r) \ r = rpy_reverse_db_rawrefcount_next_dead() +#define OP_GC_INCREASE_ROOT_STACK_DEPTH(depth, r) /* nothing */ + RPY_EXTERN void rpy_reverse_db_flush(void); /* must be called with the lock */ RPY_EXTERN void rpy_reverse_db_fetch(const char *file, int line); From pypy.commits at gmail.com Wed Oct 2 11:40:05 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 02 Oct 2019 08:40:05 -0700 (PDT) Subject: [pypy-commit] pypy release-pypy3.6-v7.x: corner case in the json decoder: like regular object maps, don't make the json Message-ID: <5d94c4d5.1c69fb81.2bcce.2241@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: release-pypy3.6-v7.x Changeset: r97710:bd5961409c41 Date: 2019-10-01 22:59 +0200 http://bitbucket.org/pypy/pypy/changeset/bd5961409c41/ Log: corner case in the json decoder: like regular object maps, don't make the json maps arbitrarily huge (grafted from 38ede7e5cb5ad44fdc637b8d5ee5f15849f84dbb) diff --git a/pypy/module/_pypyjson/interp_decoder.py b/pypy/module/_pypyjson/interp_decoder.py --- a/pypy/module/_pypyjson/interp_decoder.py +++ b/pypy/module/_pypyjson/interp_decoder.py @@ -73,6 +73,9 @@ # hit in the cache STRING_CACHE_USEFULNESS_FACTOR = 4 + # don't make arbitrarily huge maps + MAX_MAP_SIZE = 100 + def __init__(self, space, s): self.space = space @@ -369,7 +372,7 @@ return w_res elif ch == ',': i = self.skip_whitespace(i) - if currmap.is_state_blocked(): + if currmap.is_state_blocked() or nextindex > self.MAX_MAP_SIZE: self.scratch.append(values_w) # can reuse next time dict_w = self._switch_to_dict(currmap, values_w, nextindex) return self.decode_object_dict(i, start, dict_w) diff --git a/pypy/module/_pypyjson/test/test__pypyjson.py b/pypy/module/_pypyjson/test/test__pypyjson.py --- a/pypy/module/_pypyjson/test/test__pypyjson.py +++ b/pypy/module/_pypyjson/test/test__pypyjson.py @@ -467,6 +467,14 @@ res = _pypyjson.loads(json) assert res == [{u'a': 1}, {u'a': 2}] + def test_huge_map(self): + import _pypyjson + import __pypy__ + s = '{' + ",".join('"%s": %s' % (i, i) for i in range(200)) + '}' + res = _pypyjson.loads(s) + assert len(res) == 200 + assert __pypy__.strategy(res) == "UnicodeDictStrategy" + def test_tab_in_string_should_fail(self): import _pypyjson # http://json.org/JSON_checker/test/fail25.json From pypy.commits at gmail.com Wed Oct 2 11:40:07 2019 From: pypy.commits at gmail.com (arigo) Date: Wed, 02 Oct 2019 08:40:07 -0700 (PDT) Subject: [pypy-commit] pypy release-pypy3.6-v7.x: Issue #3084 Message-ID: <5d94c4d7.1c69fb81.c62ef.33ed@mx.google.com> Author: Armin Rigo Branch: release-pypy3.6-v7.x Changeset: r97711:7fbabb23dfec Date: 2019-10-02 15:51 +0200 http://bitbucket.org/pypy/pypy/changeset/7fbabb23dfec/ Log: Issue #3084 Fix compilation error when building revdb (grafted from cd96ab5b8d1e4364105cb4a3c21a31b5dc1a5c87) diff --git a/rpython/translator/revdb/src-revdb/revdb_include.h b/rpython/translator/revdb/src-revdb/revdb_include.h --- a/rpython/translator/revdb/src-revdb/revdb_include.h +++ b/rpython/translator/revdb/src-revdb/revdb_include.h @@ -285,6 +285,8 @@ #define OP_GC_RAWREFCOUNT_NEXT_DEAD(r) \ r = rpy_reverse_db_rawrefcount_next_dead() +#define OP_GC_INCREASE_ROOT_STACK_DEPTH(depth, r) /* nothing */ + RPY_EXTERN void rpy_reverse_db_flush(void); /* must be called with the lock */ RPY_EXTERN void rpy_reverse_db_fetch(const char *file, int line); From pypy.commits at gmail.com Wed Oct 2 15:52:29 2019 From: pypy.commits at gmail.com (rlamy) Date: Wed, 02 Oct 2019 12:52:29 -0700 (PDT) Subject: [pypy-commit] pypy py3.6-asyncgen: Add space.callable_w() method Message-ID: <5d94fffd.1c69fb81.696f3.1ec2@mx.google.com> Author: Ronan Lamy Branch: py3.6-asyncgen Changeset: r97712:4fb21ab8a5c6 Date: 2019-10-02 20:50 +0100 http://bitbucket.org/pypy/pypy/changeset/4fb21ab8a5c6/ Log: Add space.callable_w() method diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -1232,8 +1232,11 @@ from pypy.interpreter.generator import GeneratorIterator return isinstance(w_obj, GeneratorIterator) + def callable_w(self, w_obj): + return self.lookup(w_obj, "__call__") is not None + def callable(self, w_obj): - return self.newbool(self.lookup(w_obj, "__call__") is not None) + return self.newbool(self.callable_w(w_obj)) def issequence_w(self, w_obj): flag = self.type(w_obj).flag_map_or_seq From pypy.commits at gmail.com Wed Oct 2 15:52:31 2019 From: pypy.commits at gmail.com (rlamy) Date: Wed, 02 Oct 2019 12:52:31 -0700 (PDT) Subject: [pypy-commit] pypy py3.6-asyncgen: Make asyncgen_hooks thread-local Message-ID: <5d94ffff.1c69fb81.11cbb.503a@mx.google.com> Author: Ronan Lamy Branch: py3.6-asyncgen Changeset: r97713:647cc06b34f4 Date: 2019-10-02 20:51 +0100 http://bitbucket.org/pypy/pypy/changeset/647cc06b34f4/ Log: Make asyncgen_hooks thread-local diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py --- a/pypy/interpreter/executioncontext.py +++ b/pypy/interpreter/executioncontext.py @@ -23,8 +23,9 @@ # XXX [fijal] but they're not. is_being_profiled is guarded a bit all # over the place as well as w_tracefunc - _immutable_fields_ = ['profilefunc?', 'w_tracefunc?', - 'w_coroutine_wrapper_fn?'] + _immutable_fields_ = [ + 'profilefunc?', 'w_tracefunc?', 'w_coroutine_wrapper_fn?', + 'w_asyncgen_firstiter_fn?', 'w_asyncgen_finalizer_fn?'] def __init__(self, space): self.space = space @@ -41,6 +42,8 @@ self.thread_disappeared = False # might be set to True after os.fork() self.w_coroutine_wrapper_fn = None self.in_coroutine_wrapper = False + self.w_asyncgen_firstiter_fn = None + self.w_asyncgen_finalizer_fn = None @staticmethod def _mark_thread_disappeared(space): diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py --- a/pypy/interpreter/generator.py +++ b/pypy/interpreter/generator.py @@ -594,15 +594,12 @@ if self.hooks_inited: return self.hooks_inited = True - - self.w_finalizer = self.space.appexec([], '''(): - import sys - hooks = sys.get_asyncgen_hooks() - return hooks.finalizer''') + ec = self.space.getexecutioncontext() + self.w_finalizer = ec.w_asyncgen_firstiter_fn def _finalize_(self): if self.frame is not None and self.frame.lastblock is not None: - if self.w_finalizer is not self.space.w_None: + if self.w_finalizer is not None: # XXX: this is a hack to resurrect the weakref that was cleared # before running _finalize_() if self.space.config.translation.rweakref: 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 @@ -75,7 +75,7 @@ If it is another kind of object, it will be printed and the system exit status will be one (i.e., failure).""" # note that we cannot simply use SystemExit(exitcode) here. - # in the default branch, we use "raise SystemExit, exitcode", + # in the default branch, we use "raise SystemExit, exitcode", # which leads to an extra de-tupelizing # in normalize_exception, which is exactly like CPython's. if isinstance(exitcode, tuple): @@ -109,7 +109,6 @@ # This is tested in test_app_main.py class sysflags(metaclass=structseqtype): - name = "sys.flags" debug = structseqfield(0) @@ -130,29 +129,6 @@ null__xoptions = {} -class asyncgen_hooks(metaclass=structseqtype): - name = "asyncgen_hooks" - - firstiter = structseqfield(0) - finalizer = structseqfield(1) - -# FIXME: make this thread-local -_current_asyncgen_hooks = asyncgen_hooks((None, None)) - -def get_asyncgen_hooks(): - return _current_asyncgen_hooks - -_default_arg = object() - -def set_asyncgen_hooks(firstiter=_default_arg, finalizer=_default_arg): - global _current_asyncgen_hooks - if firstiter is _default_arg: - firstiter = _current_asyncgen_hooks.firstiter - if finalizer is _default_arg: - finalizer = _current_asyncgen_hooks.finalizer - _current_asyncgen_hooks = asyncgen_hooks((firstiter, finalizer)) - - implementation = SimpleNamespace( name='pypy', version=sys.version_info, diff --git a/pypy/module/sys/moduledef.py b/pypy/module/sys/moduledef.py --- a/pypy/module/sys/moduledef.py +++ b/pypy/module/sys/moduledef.py @@ -95,6 +95,8 @@ 'get_coroutine_wrapper' : 'vm.get_coroutine_wrapper', 'set_coroutine_wrapper' : 'vm.set_coroutine_wrapper', + 'get_asyncgen_hooks' : 'vm.get_asyncgen_hooks', + 'set_asyncgen_hooks' : 'vm.set_asyncgen_hooks', 'is_finalizing' : 'vm.is_finalizing', } @@ -115,8 +117,6 @@ 'flags' : 'app.null_sysflags', '_xoptions' : 'app.null__xoptions', 'implementation' : 'app.implementation', - 'get_asyncgen_hooks' : 'app.get_asyncgen_hooks', - 'set_asyncgen_hooks' : 'app.set_asyncgen_hooks', # these six attributes are here only during tests; # they are removed before translation @@ -184,7 +184,7 @@ if w_file is w_stdout: e.write_unraisable(space, '', w_file) ret = -1 - return ret + return ret def _file_is_closed(self, space, w_file): try: diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py --- a/pypy/module/sys/vm.py +++ b/pypy/module/sys/vm.py @@ -7,7 +7,7 @@ from pypy.interpreter import gateway from pypy.interpreter.error import oefmt -from pypy.interpreter.gateway import unwrap_spec +from pypy.interpreter.gateway import unwrap_spec, WrappedDefault # ____________________________________________________________ @@ -240,6 +240,14 @@ suite_mask = structseqfield(12, "Bit mask identifying available product suites") product_type = structseqfield(13, "System product type") platform_version = structseqfield(14, "Diagnostic version number") + + +class asyncgen_hooks(metaclass=structseqtype): + name = "asyncgen_hooks" + + firstiter = structseqfield(0) + finalizer = structseqfield(1) + ''') @@ -286,7 +294,7 @@ getsizeof_missing = """getsizeof(...) getsizeof(object, default) -> int - + Return the size of object in bytes. sys.getsizeof(object, default) will always return default on PyPy, and @@ -351,5 +359,45 @@ else: raise oefmt(space.w_TypeError, "callable expected, got %T", w_wrapper) +def get_asyncgen_hooks(space): + """get_asyncgen_hooks() + +Return a namedtuple of installed asynchronous generators hooks (firstiter, finalizer).""" + ec = space.getexecutioncontext() + w_firstiter = ec.w_asyncgen_firstiter_fn + if w_firstiter is None: + w_firstiter = space.w_None + w_finalizer = ec.w_asyncgen_finalizer_fn + if w_finalizer is None: + w_finalizer = space.w_None + w_asyncgen_hooks = app.wget(space, "asyncgen_hooks") + return space.call_function( + w_asyncgen_hooks, + space.newtuple([w_firstiter, w_finalizer])) + +# Note: the docstring is wrong on CPython +def set_asyncgen_hooks(space, w_firstiter=None, w_finalizer=None): + """set_asyncgen_hooks(firstiter=None, finalizer=None) + +Set a finalizer for async generators objects.""" + ec = space.getexecutioncontext() + if space.is_w(w_finalizer, space.w_None): + ec.w_asyncgen_finalizer_fn = None + elif w_finalizer is not None: + if space.callable_w(w_finalizer): + ec.w_asyncgen_finalizer_fn = w_finalizer + else: + raise oefmt(space.w_TypeError, + "callable finalizer expected, got %T", w_finalizer) + if space.is_w(w_firstiter, space.w_None): + ec.w_asyncgen_firstiter_fn = None + elif w_firstiter is not None: + if space.callable_w(w_firstiter): + ec.w_asyncgen_firstiter_fn = w_firstiter + else: + raise oefmt(space.w_TypeError, + "callable firstiter expected, got %T", w_firstiter) + + def is_finalizing(space): return space.newbool(space.sys.finalizing) From pypy.commits at gmail.com Thu Oct 3 07:44:59 2019 From: pypy.commits at gmail.com (rlamy) Date: Thu, 03 Oct 2019 04:44:59 -0700 (PDT) Subject: [pypy-commit] pypy py3.6-asyncgen: fix comment Message-ID: <5d95df3b.1c69fb81.6b8e2.f6c8@mx.google.com> Author: Ronan Lamy Branch: py3.6-asyncgen Changeset: r97714:bb53568cb83f Date: 2019-10-03 12:43 +0100 http://bitbucket.org/pypy/pypy/changeset/bb53568cb83f/ Log: fix comment diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py --- a/pypy/interpreter/pyopcode.py +++ b/pypy/interpreter/pyopcode.py @@ -1121,7 +1121,7 @@ # sub-iterable first before continuing on the next bytecode. in_generator = self.get_generator() if in_generator is None: - # Issue #2768: rare case involving __del__ methods. + # Issue #2786: rare case involving __del__ methods. # XXX This is a workaround, not a proper solution. raise oefmt(self.space.w_RuntimeError, "PyPy limitation: cannot use 'yield from' or 'await' " From pypy.commits at gmail.com Thu Oct 3 13:15:38 2019 From: pypy.commits at gmail.com (rlamy) Date: Thu, 03 Oct 2019 10:15:38 -0700 (PDT) Subject: [pypy-commit] pypy py3.6-asyncgen: test cleanup Message-ID: <5d962cba.1c69fb81.fceb0.08aa@mx.google.com> Author: Ronan Lamy Branch: py3.6-asyncgen Changeset: r97715:7c0d5129bf7b Date: 2019-10-03 15:39 +0100 http://bitbucket.org/pypy/pypy/changeset/7c0d5129bf7b/ Log: test cleanup diff --git a/pypy/interpreter/test/apptest_coroutine.py b/pypy/interpreter/test/apptest_coroutine.py --- a/pypy/interpreter/test/apptest_coroutine.py +++ b/pypy/interpreter/test/apptest_coroutine.py @@ -633,16 +633,12 @@ a2 = g.aclose() sys.set_asyncgen_hooks(finalizer=_finalize) assert state == 0 - try: + with pytest.raises(StopIteration): a.send(None) - except StopIteration: - pass assert a2.send(None) == 'coro' assert state == 1 - try: + with pytest.raises(StopIteration): a2.send(None) - except StopIteration: - pass assert state == 2 sys.set_asyncgen_hooks(None, None) From pypy.commits at gmail.com Thu Oct 3 13:15:40 2019 From: pypy.commits at gmail.com (rlamy) Date: Thu, 03 Oct 2019 10:15:40 -0700 (PDT) Subject: [pypy-commit] pypy py3.6-asyncgen: Begin refactoring handling of .w_yielded_from Message-ID: <5d962cbc.1c69fb81.33933.c7f4@mx.google.com> Author: Ronan Lamy Branch: py3.6-asyncgen Changeset: r97716:d157cd5db6ca Date: 2019-10-03 16:35 +0100 http://bitbucket.org/pypy/pypy/changeset/d157cd5db6ca/ Log: Begin refactoring handling of .w_yielded_from diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py --- a/pypy/interpreter/generator.py +++ b/pypy/interpreter/generator.py @@ -11,7 +11,7 @@ class GeneratorOrCoroutine(W_Root): _immutable_fields_ = ['pycode'] - w_yielded_from = None + _w_yielded_from = None def __init__(self, frame, name=None, qualname=None): self.space = frame.space @@ -153,9 +153,9 @@ # Called from execute_frame() just before resuming the bytecode # interpretation. space = self.space - w_yf = self.w_yielded_from + w_yf = self.get_delegate() if w_yf is not None: - self.w_yielded_from = None + self.set_delegate(None) try: self.next_yield_from(frame, w_yf, w_arg_or_err) except OperationError as operr: @@ -179,6 +179,12 @@ else: return r_uint(0) + def get_delegate(self): + return self._w_yielded_from + + def set_delegate(self, w_delegate): + self._w_yielded_from = w_delegate + def next_yield_from(self, frame, w_yf, w_inputvalue_or_err): """Fetch the next item of the current 'yield from', push it on the frame stack, and raises Yield. If there isn't one, push @@ -209,7 +215,7 @@ return else: frame.pushvalue(w_retval) - self.w_yielded_from = w_yf + self.set_delegate(w_yf) raise Yield def _leak_stopiteration(self, e): @@ -259,8 +265,8 @@ operr = OperationError(w_type, w_val, tb) operr.normalize_exception(space) - # note: w_yielded_from is always None if 'self.running' - if (self.w_yielded_from is not None and + # note: _w_yielded_from is always None if 'self.running' + if (self.get_delegate() is not None and operr.match(space, space.w_GeneratorExit)): try: self._gen_close_iter(space) @@ -276,8 +282,8 @@ def _gen_close_iter(self, space): assert not self.running - w_yf = self.w_yielded_from - self.w_yielded_from = None + w_yf = self.get_delegate() + self.set_delegate(None) self.running = True try: gen_close_iter(space, w_yf) @@ -290,8 +296,8 @@ return # nothing to do in this case space = self.space operr = None - # note: w_yielded_from is always None if 'self.running' - w_yf = self.w_yielded_from + # note: _w_yielded_from is always None if 'self.running' + w_yf = self.get_delegate() if w_yf is not None: try: self._gen_close_iter(space) diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py --- a/pypy/interpreter/pyopcode.py +++ b/pypy/interpreter/pyopcode.py @@ -1556,7 +1556,7 @@ w_iterable = self.popvalue() w_iter = get_awaitable_iter(self.space, w_iterable) if isinstance(w_iter, Coroutine): - if w_iter.w_yielded_from is not None: + if w_iter.get_delegate() is not None: # 'w_iter' is a coroutine object that is being awaited, # '.w_yielded_from' is the current awaitable being awaited on. raise oefmt(self.space.w_RuntimeError, diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py --- a/pypy/interpreter/typedef.py +++ b/pypy/interpreter/typedef.py @@ -838,7 +838,7 @@ gi_running = interp_attrproperty('running', cls=GeneratorIterator, wrapfn="newbool"), gi_frame = GetSetProperty(GeneratorIterator.descr_gicr_frame), gi_code = interp_attrproperty_w('pycode', cls=GeneratorIterator), - gi_yieldfrom=interp_attrproperty_w('w_yielded_from', cls=GeneratorIterator), + gi_yieldfrom=GetSetProperty(GeneratorIterator.get_delegate), __name__ = GetSetProperty(GeneratorIterator.descr__name__, GeneratorIterator.descr_set__name__), __qualname__ = GetSetProperty(GeneratorIterator.descr__qualname__, @@ -862,7 +862,7 @@ cr_running = interp_attrproperty('running', cls=Coroutine, wrapfn="newbool"), cr_frame = GetSetProperty(Coroutine.descr_gicr_frame), cr_code = interp_attrproperty_w('pycode', cls=Coroutine), - cr_await = interp_attrproperty_w('w_yielded_from', cls=Coroutine), + cr_await=GetSetProperty(Coroutine.get_delegate), __name__ = GetSetProperty(Coroutine.descr__name__, Coroutine.descr_set__name__, doc="name of the coroutine"), @@ -890,7 +890,7 @@ ag_running = interp_attrproperty('running', cls=AsyncGenerator, wrapfn="newbool"), ag_frame = GetSetProperty(AsyncGenerator.descr_gicr_frame), ag_code = interp_attrproperty_w('pycode', cls=AsyncGenerator), - ag_await = interp_attrproperty_w('w_yielded_from', cls=AsyncGenerator), + ag_await=GetSetProperty(AsyncGenerator.get_delegate), __name__ = GetSetProperty(AsyncGenerator.descr__name__, AsyncGenerator.descr_set__name__, doc="name of the async generator"), From pypy.commits at gmail.com Thu Oct 3 13:15:42 2019 From: pypy.commits at gmail.com (rlamy) Date: Thu, 03 Oct 2019 10:15:42 -0700 (PDT) Subject: [pypy-commit] pypy py3.6-asyncgen: Store w_yf on the frame instead of the generator Message-ID: <5d962cbe.1c69fb81.11cbb.7115@mx.google.com> Author: Ronan Lamy Branch: py3.6-asyncgen Changeset: r97717:b1d846ebee27 Date: 2019-10-03 16:52 +0100 http://bitbucket.org/pypy/pypy/changeset/b1d846ebee27/ Log: Store w_yf on the frame instead of the generator diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py --- a/pypy/interpreter/generator.py +++ b/pypy/interpreter/generator.py @@ -11,8 +11,6 @@ class GeneratorOrCoroutine(W_Root): _immutable_fields_ = ['pycode'] - _w_yielded_from = None - def __init__(self, frame, name=None, qualname=None): self.space = frame.space self.frame = frame # turned into None when frame_finished_execution @@ -180,10 +178,10 @@ return r_uint(0) def get_delegate(self): - return self._w_yielded_from + return self.frame.w_yielding_from def set_delegate(self, w_delegate): - self._w_yielded_from = w_delegate + self.frame.w_yielding_from = w_delegate def next_yield_from(self, frame, w_yf, w_inputvalue_or_err): """Fetch the next item of the current 'yield from', push it on diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py --- a/pypy/interpreter/pyframe.py +++ b/pypy/interpreter/pyframe.py @@ -68,6 +68,7 @@ frame_finished_execution = False f_generator_wref = rweakref.dead_ref # for generators/coroutines f_generator_nowref = None # (only one of the two attrs) + w_yielding_from = None last_instr = -1 f_backref = jit.vref_None From pypy.commits at gmail.com Thu Oct 3 13:15:44 2019 From: pypy.commits at gmail.com (rlamy) Date: Thu, 03 Oct 2019 10:15:44 -0700 (PDT) Subject: [pypy-commit] pypy py3.6-asyncgen: Allow YIELD_FROM to run even if the generator owning the frame died Message-ID: <5d962cc0.1c69fb81.9134d.00d6@mx.google.com> Author: Ronan Lamy Branch: py3.6-asyncgen Changeset: r97718:c2f8aa002dda Date: 2019-10-03 18:14 +0100 http://bitbucket.org/pypy/pypy/changeset/c2f8aa002dda/ Log: Allow YIELD_FROM to run even if the generator owning the frame died diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py --- a/pypy/interpreter/generator.py +++ b/pypy/interpreter/generator.py @@ -155,7 +155,7 @@ if w_yf is not None: self.set_delegate(None) try: - self.next_yield_from(frame, w_yf, w_arg_or_err) + frame.next_yield_from(w_yf, w_arg_or_err) except OperationError as operr: operr.record_context(space, space.getexecutioncontext()) return frame.handle_generator_error(operr) @@ -178,43 +178,27 @@ return r_uint(0) def get_delegate(self): + if self.frame is None: + return None return self.frame.w_yielding_from + def descr_delegate(self, space): + w_yf = self.get_delegate() + if w_yf is None: + return space.w_None + return w_yf + def set_delegate(self, w_delegate): self.frame.w_yielding_from = w_delegate + + def next_yield_from(self, frame, w_yf, w_inputvalue_or_err): """Fetch the next item of the current 'yield from', push it on the frame stack, and raises Yield. If there isn't one, push w_stopiteration_value and returns. May also just raise. """ - space = self.space - try: - if isinstance(w_yf, GeneratorOrCoroutine): - w_retval = w_yf.send_ex(w_inputvalue_or_err) - elif isinstance(w_yf, AsyncGenASend): # performance only - w_retval = w_yf.do_send(w_inputvalue_or_err) - elif space.is_w(w_inputvalue_or_err, space.w_None): - w_retval = space.next(w_yf) - else: - w_retval = delegate_to_nongen(space, w_yf, w_inputvalue_or_err) - except OperationError as e: - if not e.match(space, space.w_StopIteration): - raise - frame._report_stopiteration_sometimes(w_yf, e) - try: - w_stop_value = space.getattr(e.get_w_value(space), - space.newtext("value")) - except OperationError as e: - if not e.match(space, space.w_AttributeError): - raise - w_stop_value = space.w_None - frame.pushvalue(w_stop_value) - return - else: - frame.pushvalue(w_retval) - self.set_delegate(w_yf) - raise Yield + frame.next_yield_from(w_yf, w_inputvalue_or_err) def _leak_stopiteration(self, e): # Check for __future__ generator_stop and conditionally turn @@ -493,27 +477,6 @@ else: space.call_function(w_close) -def delegate_to_nongen(space, w_yf, w_inputvalue_or_err): - # invoke a "send" or "throw" by method name to a non-generator w_yf - if isinstance(w_inputvalue_or_err, SApplicationException): - operr = w_inputvalue_or_err.operr - try: - w_meth = space.getattr(w_yf, space.newtext("throw")) - except OperationError as e: - if not e.match(space, space.w_AttributeError): - raise - raise operr - # bah, CPython calls here with the exact same arguments as - # originally passed to throw(). In our case it is far removed. - # Let's hope nobody will complain... - operr.normalize_exception(space) - w_exc = operr.w_type - w_val = operr.get_w_value(space) - w_tb = operr.get_w_traceback(space) - return space.call_function(w_meth, w_exc, w_val, w_tb) - else: - return space.call_method(w_yf, "send", w_inputvalue_or_err) - def gen_is_coroutine(w_obj): return (isinstance(w_obj, GeneratorIterator) and (w_obj.pycode.co_flags & consts.CO_ITERABLE_COROUTINE) != 0) diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py --- a/pypy/interpreter/pyopcode.py +++ b/pypy/interpreter/pyopcode.py @@ -1113,24 +1113,51 @@ self.pushvalue(w_value) raise Yield + def next_yield_from(self, w_yf, w_inputvalue_or_err): + """Fetch the next item of the current 'yield from', push it on + the frame stack, and raises Yield. If there isn't one, push + w_stopiteration_value and returns. May also just raise. + """ + from pypy.interpreter.generator import ( + GeneratorOrCoroutine, AsyncGenASend) + space = self.space + try: + if isinstance(w_yf, GeneratorOrCoroutine): + w_retval = w_yf.send_ex(w_inputvalue_or_err) + elif isinstance(w_yf, AsyncGenASend): # performance only + w_retval = w_yf.do_send(w_inputvalue_or_err) + elif space.is_w(w_inputvalue_or_err, space.w_None): + w_retval = space.next(w_yf) + else: + w_retval = delegate_to_nongen(space, w_yf, w_inputvalue_or_err) + except OperationError as e: + if not e.match(space, space.w_StopIteration): + raise + self._report_stopiteration_sometimes(w_yf, e) + try: + w_stop_value = space.getattr(e.get_w_value(space), + space.newtext("value")) + except OperationError as e: + if not e.match(space, space.w_AttributeError): + raise + w_stop_value = space.w_None + self.pushvalue(w_stop_value) + return + else: + self.pushvalue(w_retval) + self.w_yielding_from = w_yf + raise Yield + def YIELD_FROM(self, oparg, next_instr): # Unlike CPython, we handle this not by repeating the same # bytecode over and over until the inner iterator is exhausted. # Instead, we directly set the generator's w_yielded_from. # This asks generator.resume_execute_frame() to exhaust that # sub-iterable first before continuing on the next bytecode. - in_generator = self.get_generator() - if in_generator is None: - # Issue #2786: rare case involving __del__ methods. - # XXX This is a workaround, not a proper solution. - raise oefmt(self.space.w_RuntimeError, - "PyPy limitation: cannot use 'yield from' or 'await' " - "in a generator/coroutine that has been partially " - "deallocated already, typically seen via __del__") w_inputvalue = self.popvalue() # that's always w_None, actually w_gen = self.popvalue() # - in_generator.next_yield_from(self, w_gen, w_inputvalue) + self.next_yield_from(w_gen, w_inputvalue) # Common case: the call above raises Yield. # If instead the iterable is empty, next_yield_from() pushed the # final result and returns. In that case, we can just continue @@ -1694,6 +1721,27 @@ else: self.MISSING_OPCODE(oparg, next_instr) +def delegate_to_nongen(space, w_yf, w_inputvalue_or_err): + # invoke a "send" or "throw" by method name to a non-generator w_yf + if isinstance(w_inputvalue_or_err, SApplicationException): + operr = w_inputvalue_or_err.operr + try: + w_meth = space.getattr(w_yf, space.newtext("throw")) + except OperationError as e: + if not e.match(space, space.w_AttributeError): + raise + raise operr + # bah, CPython calls here with the exact same arguments as + # originally passed to throw(). In our case it is far removed. + # Let's hope nobody will complain... + operr.normalize_exception(space) + w_exc = operr.w_type + w_val = operr.get_w_value(space) + w_tb = operr.get_w_traceback(space) + return space.call_function(w_meth, w_exc, w_val, w_tb) + else: + return space.call_method(w_yf, "send", w_inputvalue_or_err) + ### ____________________________________________________________ ### diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py --- a/pypy/interpreter/typedef.py +++ b/pypy/interpreter/typedef.py @@ -838,7 +838,7 @@ gi_running = interp_attrproperty('running', cls=GeneratorIterator, wrapfn="newbool"), gi_frame = GetSetProperty(GeneratorIterator.descr_gicr_frame), gi_code = interp_attrproperty_w('pycode', cls=GeneratorIterator), - gi_yieldfrom=GetSetProperty(GeneratorIterator.get_delegate), + gi_yieldfrom=GetSetProperty(GeneratorIterator.descr_delegate), __name__ = GetSetProperty(GeneratorIterator.descr__name__, GeneratorIterator.descr_set__name__), __qualname__ = GetSetProperty(GeneratorIterator.descr__qualname__, @@ -862,7 +862,7 @@ cr_running = interp_attrproperty('running', cls=Coroutine, wrapfn="newbool"), cr_frame = GetSetProperty(Coroutine.descr_gicr_frame), cr_code = interp_attrproperty_w('pycode', cls=Coroutine), - cr_await=GetSetProperty(Coroutine.get_delegate), + cr_await=GetSetProperty(Coroutine.descr_delegate), __name__ = GetSetProperty(Coroutine.descr__name__, Coroutine.descr_set__name__, doc="name of the coroutine"), @@ -890,7 +890,7 @@ ag_running = interp_attrproperty('running', cls=AsyncGenerator, wrapfn="newbool"), ag_frame = GetSetProperty(AsyncGenerator.descr_gicr_frame), ag_code = interp_attrproperty_w('pycode', cls=AsyncGenerator), - ag_await=GetSetProperty(AsyncGenerator.get_delegate), + ag_await=GetSetProperty(AsyncGenerator.descr_delegate), __name__ = GetSetProperty(AsyncGenerator.descr__name__, AsyncGenerator.descr_set__name__, doc="name of the async generator"), From pypy.commits at gmail.com Fri Oct 4 04:03:00 2019 From: pypy.commits at gmail.com (stefanor) Date: Fri, 04 Oct 2019 01:03:00 -0700 (PDT) Subject: [pypy-commit] pypy default: Handle ProcessorAutodetectError in _pypyjson.simd Message-ID: <5d96fcb4.1c69fb81.9a21d.6ca2@mx.google.com> Author: Stefano Rivera Branch: Changeset: r97719:f3edc9623b51 Date: 2019-10-04 10:58 +0300 http://bitbucket.org/pypy/pypy/changeset/f3edc9623b51/ Log: Handle ProcessorAutodetectError in _pypyjson.simd diff --git a/pypy/module/_pypyjson/simd.py b/pypy/module/_pypyjson/simd.py --- a/pypy/module/_pypyjson/simd.py +++ b/pypy/module/_pypyjson/simd.py @@ -1,7 +1,7 @@ from rpython.rtyper.lltypesystem import lltype, rffi from rpython.rlib import objectmodel, unroll from rpython.rlib.rarithmetic import r_uint, intmask, LONG_BIT -from rpython.jit.backend.detect_cpu import autodetect +from rpython.jit.backend.detect_cpu import autodetect, ProcessorAutodetectError # accelerators for string operations using simd on regular word sizes (*not* # SSE instructions). this style is sometimes called SWAR (SIMD Within A @@ -15,8 +15,11 @@ WORD_SIZE = 8 EVERY_BYTE_ONE = 0x0101010101010101 EVERY_BYTE_HIGHEST_BIT = 0x8080808080808080 - if autodetect() == "x86-64": - USE_SIMD = True + try: + if autodetect() == "x86-64": + USE_SIMD = True + except ProcessorAutodetectError: + pass else: WORD_SIZE = 4 EVERY_BYTE_ONE = 0x01010101 From pypy.commits at gmail.com Fri Oct 4 05:53:33 2019 From: pypy.commits at gmail.com (stefanor) Date: Fri, 04 Oct 2019 02:53:33 -0700 (PDT) Subject: [pypy-commit] pypy release-pypy3.6-v7.x: Handle ProcessorAutodetectError in _pypyjson.simd Message-ID: <5d97169d.1c69fb81.1b5c1.ea74@mx.google.com> Author: Stefano Rivera Branch: release-pypy3.6-v7.x Changeset: r97720:6042bdab85aa Date: 2019-10-04 10:58 +0300 http://bitbucket.org/pypy/pypy/changeset/6042bdab85aa/ Log: Handle ProcessorAutodetectError in _pypyjson.simd (grafted from f3edc9623b51656f56f4a855327e77103050ef9e) diff --git a/pypy/module/_pypyjson/simd.py b/pypy/module/_pypyjson/simd.py --- a/pypy/module/_pypyjson/simd.py +++ b/pypy/module/_pypyjson/simd.py @@ -1,7 +1,7 @@ from rpython.rtyper.lltypesystem import lltype, rffi from rpython.rlib import objectmodel, unroll from rpython.rlib.rarithmetic import r_uint, intmask, LONG_BIT -from rpython.jit.backend.detect_cpu import autodetect +from rpython.jit.backend.detect_cpu import autodetect, ProcessorAutodetectError # accelerators for string operations using simd on regular word sizes (*not* # SSE instructions). this style is sometimes called SWAR (SIMD Within A @@ -15,8 +15,11 @@ WORD_SIZE = 8 EVERY_BYTE_ONE = 0x0101010101010101 EVERY_BYTE_HIGHEST_BIT = 0x8080808080808080 - if autodetect() == "x86-64": - USE_SIMD = True + try: + if autodetect() == "x86-64": + USE_SIMD = True + except ProcessorAutodetectError: + pass else: WORD_SIZE = 4 EVERY_BYTE_ONE = 0x01010101 From pypy.commits at gmail.com Fri Oct 4 05:53:36 2019 From: pypy.commits at gmail.com (stefanor) Date: Fri, 04 Oct 2019 02:53:36 -0700 (PDT) Subject: [pypy-commit] pypy release-pypy2.7-v7.x: Handle ProcessorAutodetectError in _pypyjson.simd Message-ID: <5d9716a0.1c69fb81.c26ea.371f@mx.google.com> Author: Stefano Rivera Branch: release-pypy2.7-v7.x Changeset: r97721:73aff81440f6 Date: 2019-10-04 10:58 +0300 http://bitbucket.org/pypy/pypy/changeset/73aff81440f6/ Log: Handle ProcessorAutodetectError in _pypyjson.simd (grafted from f3edc9623b51656f56f4a855327e77103050ef9e) diff --git a/pypy/module/_pypyjson/simd.py b/pypy/module/_pypyjson/simd.py --- a/pypy/module/_pypyjson/simd.py +++ b/pypy/module/_pypyjson/simd.py @@ -1,7 +1,7 @@ from rpython.rtyper.lltypesystem import lltype, rffi from rpython.rlib import objectmodel, unroll from rpython.rlib.rarithmetic import r_uint, intmask, LONG_BIT -from rpython.jit.backend.detect_cpu import autodetect +from rpython.jit.backend.detect_cpu import autodetect, ProcessorAutodetectError # accelerators for string operations using simd on regular word sizes (*not* # SSE instructions). this style is sometimes called SWAR (SIMD Within A @@ -15,8 +15,11 @@ WORD_SIZE = 8 EVERY_BYTE_ONE = 0x0101010101010101 EVERY_BYTE_HIGHEST_BIT = 0x8080808080808080 - if autodetect() == "x86-64": - USE_SIMD = True + try: + if autodetect() == "x86-64": + USE_SIMD = True + except ProcessorAutodetectError: + pass else: WORD_SIZE = 4 EVERY_BYTE_ONE = 0x01010101 From pypy.commits at gmail.com Fri Oct 4 13:03:36 2019 From: pypy.commits at gmail.com (rlamy) Date: Fri, 04 Oct 2019 10:03:36 -0700 (PDT) Subject: [pypy-commit] pypy default: Add _PyDict_GetItemWithError (part of the public API in py3) Message-ID: <5d977b68.1c69fb81.9b336.662e@mx.google.com> Author: Ronan Lamy Branch: Changeset: r97722:d663ce56919c Date: 2019-10-04 18:02 +0100 http://bitbucket.org/pypy/pypy/changeset/d663ce56919c/ Log: Add _PyDict_GetItemWithError (part of the public API in py3) diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -80,6 +80,13 @@ # XXX this is wrong with IntMutableCell. Hope it works... return w_dict.getitem(w_key) + at cpython_api([PyObject, PyObject], PyObject, result_borrowed=True) +def _PyDict_GetItemWithError(space, w_dict, w_key): + # Like PyDict_GetItem(), but doesn't swallow the error + if not isinstance(w_dict, W_DictMultiObject): + PyErr_BadInternalCall(space) + return w_dict.getitem(w_key) + @cpython_api([PyObject, PyObject, PyObject], rffi.INT_real, error=-1) def PyDict_SetItem(space, w_dict, w_key, w_obj): if not isinstance(w_dict, W_DictMultiObject): 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 @@ -173,6 +173,26 @@ ]) assert module.dict_proxy({'a': 1, 'b': 2}) == 2 + def test_getitemwitherror(self): + module = self.import_extension('foo', [ + ("dict_getitem", "METH_VARARGS", + """ + PyObject *d, *key, *result; + if (!PyArg_ParseTuple(args, "OO", &d, &key)) { + return NULL; + } + result = _PyDict_GetItemWithError(d, key); + if (result == NULL && !PyErr_Occurred()) + Py_RETURN_NONE; + Py_XINCREF(result); + return result; + """)]) + d = {'foo': 'bar'} + assert module.dict_getitem(d, 'foo') == 'bar' + assert module.dict_getitem(d, 'missing') is None + with raises(TypeError): + module.dict_getitem(d, []) + def test_update(self): module = self.import_extension('foo', [ ("update", "METH_VARARGS", From pypy.commits at gmail.com Fri Oct 4 13:30:57 2019 From: pypy.commits at gmail.com (rlamy) Date: Fri, 04 Oct 2019 10:30:57 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: hg merge default Message-ID: <5d9781d1.1c69fb81.92d17.2137@mx.google.com> Author: Ronan Lamy Branch: py3.6 Changeset: r97723:a8752c53c0d2 Date: 2019-10-04 18:16 +0100 http://bitbucket.org/pypy/pypy/changeset/a8752c53c0d2/ Log: hg merge default diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -50,4 +50,6 @@ de061d87e39c7df4e436974096d7982c676a859d release-pypy3.6-v7.1.0 784b254d669919c872a505b807db8462b6140973 release-pypy3.6-v7.1.1 8cdda8b8cdb8ff29d9e620cccd6c5edd2f2a23ec release-pypy2.7-v7.1.1 - +85dae4fd5c234b482feff834c73e089872194541 release-pypy2.7-v7.2.0rc0 +7ffb92269488f37c707ce66076f50ffd8613f8e2 release-pypy3.6-v7.2.0rc0 +4d6761df14ffd6f38450f183ac1fad32c946c21b release-pypy3.6-v7.2.0rc1 diff --git a/pypy/module/_pypyjson/interp_decoder.py b/pypy/module/_pypyjson/interp_decoder.py --- a/pypy/module/_pypyjson/interp_decoder.py +++ b/pypy/module/_pypyjson/interp_decoder.py @@ -73,6 +73,9 @@ # hit in the cache STRING_CACHE_USEFULNESS_FACTOR = 4 + # don't make arbitrarily huge maps + MAX_MAP_SIZE = 100 + def __init__(self, space, s): self.space = space @@ -369,7 +372,7 @@ return w_res elif ch == ',': i = self.skip_whitespace(i) - if currmap.is_state_blocked(): + if currmap.is_state_blocked() or nextindex > self.MAX_MAP_SIZE: self.scratch.append(values_w) # can reuse next time dict_w = self._switch_to_dict(currmap, values_w, nextindex) return self.decode_object_dict(i, start, dict_w) diff --git a/pypy/module/_pypyjson/simd.py b/pypy/module/_pypyjson/simd.py --- a/pypy/module/_pypyjson/simd.py +++ b/pypy/module/_pypyjson/simd.py @@ -1,7 +1,7 @@ from rpython.rtyper.lltypesystem import lltype, rffi from rpython.rlib import objectmodel, unroll from rpython.rlib.rarithmetic import r_uint, intmask, LONG_BIT -from rpython.jit.backend.detect_cpu import autodetect +from rpython.jit.backend.detect_cpu import autodetect, ProcessorAutodetectError # accelerators for string operations using simd on regular word sizes (*not* # SSE instructions). this style is sometimes called SWAR (SIMD Within A @@ -15,8 +15,11 @@ WORD_SIZE = 8 EVERY_BYTE_ONE = 0x0101010101010101 EVERY_BYTE_HIGHEST_BIT = 0x8080808080808080 - if autodetect() == "x86-64": - USE_SIMD = True + try: + if autodetect() == "x86-64": + USE_SIMD = True + except ProcessorAutodetectError: + pass else: WORD_SIZE = 4 EVERY_BYTE_ONE = 0x01010101 diff --git a/pypy/module/_pypyjson/test/test__pypyjson.py b/pypy/module/_pypyjson/test/test__pypyjson.py --- a/pypy/module/_pypyjson/test/test__pypyjson.py +++ b/pypy/module/_pypyjson/test/test__pypyjson.py @@ -467,6 +467,14 @@ res = _pypyjson.loads(json) assert res == [{u'a': 1}, {u'a': 2}] + def test_huge_map(self): + import _pypyjson + import __pypy__ + s = '{' + ",".join('"%s": %s' % (i, i) for i in range(200)) + '}' + res = _pypyjson.loads(s) + assert len(res) == 200 + assert __pypy__.strategy(res) == "UnicodeDictStrategy" + def test_tab_in_string_should_fail(self): import _pypyjson # http://json.org/JSON_checker/test/fail25.json diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -80,6 +80,13 @@ # XXX this is wrong with IntMutableCell. Hope it works... return w_dict.getitem(w_key) + at cpython_api([PyObject, PyObject], PyObject, result_borrowed=True) +def _PyDict_GetItemWithError(space, w_dict, w_key): + # Like PyDict_GetItem(), but doesn't swallow the error + if not isinstance(w_dict, W_DictMultiObject): + PyErr_BadInternalCall(space) + return w_dict.getitem(w_key) + @cpython_api([PyObject, PyObject, PyObject], rffi.INT_real, error=-1) def PyDict_SetItem(space, w_dict, w_key, w_obj): if not isinstance(w_dict, W_DictMultiObject): 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 @@ -175,6 +175,26 @@ ]) assert module.dict_proxy({'a': 1, 'b': 2}) == 2 + def test_getitemwitherror(self): + module = self.import_extension('foo', [ + ("dict_getitem", "METH_VARARGS", + """ + PyObject *d, *key, *result; + if (!PyArg_ParseTuple(args, "OO", &d, &key)) { + return NULL; + } + result = _PyDict_GetItemWithError(d, key); + if (result == NULL && !PyErr_Occurred()) + Py_RETURN_NONE; + Py_XINCREF(result); + return result; + """)]) + d = {'foo': 'bar'} + assert module.dict_getitem(d, 'foo') == 'bar' + assert module.dict_getitem(d, 'missing') is None + with raises(TypeError): + module.dict_getitem(d, []) + def test_setdefault(self): module = self.import_extension('foo', [ ("setdefault", "METH_VARARGS", 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 @@ -36,6 +36,10 @@ def prev_codepoint_pos_dont_look_inside(utf8, p): return rutf8.prev_codepoint_pos(utf8, p) + at jit.elidable +def codepoint_at_pos_dont_look_inside(utf8, p): + return rutf8.codepoint_at_pos(utf8, p) + class W_UnicodeObject(W_Root): import_from_mixin(StringMethods) @@ -122,7 +126,7 @@ raise oefmt(space.w_TypeError, "ord() expected a character, but string of length %d " "found", self._len()) - return space.newint(rutf8.codepoint_at_pos(self._utf8, 0)) + return space.newint(self.codepoint_at_pos_dont_look_inside(0)) def _empty(self): return W_UnicodeObject.EMPTY @@ -548,7 +552,7 @@ if self._length == 0: return space.w_False if self._length == 1: - return space.newbool(func(rutf8.codepoint_at_pos(self._utf8, 0))) + return space.newbool(func(self.codepoint_at_pos_dont_look_inside(0))) else: return self._is_generic_loop(space, self._utf8, func_name) @@ -1127,6 +1131,11 @@ return pos - 1 return prev_codepoint_pos_dont_look_inside(self._utf8, pos) + def codepoint_at_pos_dont_look_inside(self, pos): + if self.is_ascii(): + return ord(self._utf8[pos]) + return codepoint_at_pos_dont_look_inside(self._utf8, pos) + @always_inline def _unwrap_and_search(self, space, w_sub, w_start, w_end, forward=True): w_sub = self.convert_arg_to_w_unicode(space, w_sub) diff --git a/rpython/translator/revdb/src-revdb/revdb_include.h b/rpython/translator/revdb/src-revdb/revdb_include.h --- a/rpython/translator/revdb/src-revdb/revdb_include.h +++ b/rpython/translator/revdb/src-revdb/revdb_include.h @@ -285,6 +285,8 @@ #define OP_GC_RAWREFCOUNT_NEXT_DEAD(r) \ r = rpy_reverse_db_rawrefcount_next_dead() +#define OP_GC_INCREASE_ROOT_STACK_DEPTH(depth, r) /* nothing */ + RPY_EXTERN void rpy_reverse_db_flush(void); /* must be called with the lock */ RPY_EXTERN void rpy_reverse_db_fetch(const char *file, int line); From pypy.commits at gmail.com Fri Oct 4 13:30:59 2019 From: pypy.commits at gmail.com (rlamy) Date: Fri, 04 Oct 2019 10:30:59 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: Rename to PyDict_GetItemWithError (no leading underscore) Message-ID: <5d9781d3.1c69fb81.2c32d.d9b5@mx.google.com> Author: Ronan Lamy Branch: py3.6 Changeset: r97724:6e344fc026c3 Date: 2019-10-04 18:29 +0100 http://bitbucket.org/pypy/pypy/changeset/6e344fc026c3/ Log: Rename to PyDict_GetItemWithError (no leading underscore) diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -81,8 +81,11 @@ return w_dict.getitem(w_key) @cpython_api([PyObject, PyObject], PyObject, result_borrowed=True) -def _PyDict_GetItemWithError(space, w_dict, w_key): - # Like PyDict_GetItem(), but doesn't swallow the error +def PyDict_GetItemWithError(space, w_dict, w_key): + """Variant of PyDict_GetItem() that does not suppress + exceptions. Return NULL with an exception set if an exception + occurred. Return NULL without an exception set if the key + wasn't present.""" if not isinstance(w_dict, W_DictMultiObject): PyErr_BadInternalCall(space) return w_dict.getitem(w_key) diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py --- a/pypy/module/cpyext/stubs.py +++ b/pypy/module/cpyext/stubs.py @@ -243,14 +243,6 @@ def PyWrapper_New(space, w_d, w_self): raise NotImplementedError - at cpython_api([PyObject, PyObject], PyObject) -def PyDict_GetItemWithError(space, p, key): - """Variant of PyDict_GetItem() that does not suppress - exceptions. Return NULL with an exception set if an exception - occurred. Return NULL without an exception set if the key - wasn't present.""" - raise NotImplementedError - @cpython_api([PyObject, PyObject, rffi.INT_real], rffi.INT_real, error=-1) def PyDict_MergeFromSeq2(space, a, seq2, override): """Update or merge into dictionary a, from the key-value pairs in seq2. 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 @@ -183,7 +183,7 @@ if (!PyArg_ParseTuple(args, "OO", &d, &key)) { return NULL; } - result = _PyDict_GetItemWithError(d, key); + result = PyDict_GetItemWithError(d, key); if (result == NULL && !PyErr_Occurred()) Py_RETURN_NONE; Py_XINCREF(result); From pypy.commits at gmail.com Fri Oct 4 16:20:50 2019 From: pypy.commits at gmail.com (rlamy) Date: Fri, 04 Oct 2019 13:20:50 -0700 (PDT) Subject: [pypy-commit] pypy py3.6-asyncgen: Move .resume_execute_frame() to PyFrame and simplify code Message-ID: <5d97a9a2.1c69fb81.622ac.7563@mx.google.com> Author: Ronan Lamy Branch: py3.6-asyncgen Changeset: r97727:ce7a9b715879 Date: 2019-10-04 21:19 +0100 http://bitbucket.org/pypy/pypy/changeset/ce7a9b715879/ Log: Move .resume_execute_frame() to PyFrame and simplify code diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py --- a/pypy/interpreter/generator.py +++ b/pypy/interpreter/generator.py @@ -82,7 +82,7 @@ operr = OperationError(space.w_StopIteration, space.w_None) raise operr - w_result = self._invoke_execute_frame(frame, w_arg_or_err) + w_result = self._invoke_execute_frame(w_arg_or_err) assert w_result is not None # if the frame is now marked as finished, it was RETURNed from @@ -100,8 +100,9 @@ else: return w_result # YIELDed - def _invoke_execute_frame(self, frame, w_arg_or_err): + def _invoke_execute_frame(self, w_arg_or_err): space = self.space + frame = self.frame if self.running: raise oefmt(space.w_ValueError, "%s already executing", self.KIND) ec = space.getexecutioncontext() @@ -147,36 +148,6 @@ ec.set_sys_exc_info(current_exc_info) return w_result - def resume_execute_frame(self, frame, w_arg_or_err): - # Called from execute_frame() just before resuming the bytecode - # interpretation. - space = self.space - w_yf = self.get_delegate() - if w_yf is not None: - self.set_delegate(None) - try: - frame.next_yield_from(w_yf, w_arg_or_err) - except OperationError as operr: - operr.record_context(space, space.getexecutioncontext()) - return frame.handle_generator_error(operr) - # Normal case: the call above raises Yield. - # We reach this point if the iterable is exhausted. - last_instr = jit.promote(frame.last_instr) - assert last_instr & 1 == 0 - assert last_instr >= 0 - return r_uint(last_instr + 2) - - if isinstance(w_arg_or_err, SApplicationException): - return frame.handle_generator_error(w_arg_or_err.operr) - - last_instr = jit.promote(frame.last_instr) - if last_instr != -1: - assert last_instr & 1 == 0 - frame.pushvalue(w_arg_or_err) - return r_uint(last_instr + 2) - else: - return r_uint(0) - def get_delegate(self): if self.frame is None: return None @@ -191,15 +162,6 @@ def set_delegate(self, w_delegate): self.frame.w_yielding_from = w_delegate - - - def next_yield_from(self, frame, w_yf, w_inputvalue_or_err): - """Fetch the next item of the current 'yield from', push it on - the frame stack, and raises Yield. If there isn't one, push - w_stopiteration_value and returns. May also just raise. - """ - frame.next_yield_from(w_yf, w_inputvalue_or_err) - def _leak_stopiteration(self, e): # Check for __future__ generator_stop and conditionally turn # a leaking StopIteration into RuntimeError (with its cause @@ -363,7 +325,7 @@ # generate 2 versions of the function and 2 jit drivers. def _create_unpack_into(): jitdriver = jit.JitDriver(greens=['pycode'], - reds=['self', 'frame', 'results'], + reds='auto', name='unpack_into') def unpack_into(self, results): @@ -374,12 +336,10 @@ return pycode = self.pycode while True: - jitdriver.jit_merge_point(self=self, frame=frame, - results=results, pycode=pycode) + jitdriver.jit_merge_point(pycode=pycode) space = self.space try: - w_result = self._invoke_execute_frame( - frame, space.w_None) + w_result = self._invoke_execute_frame(space.w_None) except OperationError as e: if not e.match(space, space.w_StopIteration): raise diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py --- a/pypy/interpreter/pyframe.py +++ b/pypy/interpreter/pyframe.py @@ -320,6 +320,37 @@ ec.in_coroutine_wrapper = False return w_gen + def resume_execute_frame(self, w_arg_or_err): + # Called from execute_frame() just before resuming the bytecode + # interpretation. + from pypy.interpreter.pyopcode import SApplicationException + space = self.space + w_yf = self.w_yielding_from + if w_yf is not None: + self.w_yielding_from = None + try: + self.next_yield_from(w_yf, w_arg_or_err) + except OperationError as operr: + operr.record_context(space, space.getexecutioncontext()) + return self.handle_generator_error(operr) + # Normal case: the call above raises Yield. + # We reach this point if the iterable is exhausted. + last_instr = jit.promote(self.last_instr) + assert last_instr & 1 == 0 + assert last_instr >= 0 + return r_uint(last_instr + 2) + + if isinstance(w_arg_or_err, SApplicationException): + return self.handle_generator_error(w_arg_or_err.operr) + + last_instr = jit.promote(self.last_instr) + if last_instr != -1: + assert last_instr & 1 == 0 + self.pushvalue(w_arg_or_err) + return r_uint(last_instr + 2) + else: + return r_uint(0) + def execute_frame(self, in_generator=None, w_arg_or_err=None): """Execute this frame. Main entry point to the interpreter. 'in_generator' is non-None iff we are starting or resuming @@ -348,8 +379,7 @@ assert self.last_instr == -1 next_instr = r_uint(0) else: - next_instr = in_generator.resume_execute_frame( - self, w_arg_or_err) + next_instr = self.resume_execute_frame(w_arg_or_err) except pyopcode.Yield: w_exitvalue = self.popvalue() else: From pypy.commits at gmail.com Sat Oct 5 04:50:52 2019 From: pypy.commits at gmail.com (stevie_92) Date: Sat, 05 Oct 2019 01:50:52 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-gc-cycle: Fixed issues with rrc tuples Message-ID: <5d98596c.1c69fb81.3a337.9c12@mx.google.com> Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r97728:6e21fe036218 Date: 2019-10-05 10:50 +0200 http://bitbucket.org/pypy/pypy/changeset/6e21fe036218/ Log: Fixed issues with rrc tuples Implemented cpyext statistics diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py --- a/pypy/module/cpyext/state.py +++ b/pypy/module/cpyext/state.py @@ -8,6 +8,7 @@ from rpython.rlib import rawrefcount from rpython.rlib.debug import debug_print import sys +import time # Keep track of exceptions raised in cpyext for a particular execution @@ -159,6 +160,10 @@ space.actionflag.register_periodic_action(action, use_bytecode_counter=True) else: + module = space.builtin_modules['gc'] + attribute = space.newtext('cpyext_durations') + space.setattr(module, attribute, space.newlist([])) + pyobj_dealloc_action = PyObjDeallocAction(space) self.dealloc_trigger = lambda: pyobj_dealloc_action.fire() @@ -310,12 +315,14 @@ return True -def _rawrefcount_perform(space): +def _rawrefcount_perform(space): # TODO: measure time spent, make incremental?? from pypy.interpreter.baseobjspace import W_Root from pypy.module.cpyext.pyobject import (PyObject, incref, decref, finalize, from_ref, cts) from pypy.module.cpyext.api import generic_cpy_call + start = time.time() + while True: py_obj = rawrefcount.next_dead(PyObject) if not py_obj: @@ -337,11 +344,11 @@ pto = pyobj.c_ob_type if pto.c_tp_clear: incref(space, py_obj) - if pto and pto.c_tp_name: - tp_name = pto.c_tp_name - name = rffi.charp2str(cts.cast('char*', tp_name)) - debug_print("tp_clear", pyobj, ": type", pto, - ": name", name) + #if pto and pto.c_tp_name: + # tp_name = pto.c_tp_name + # name = rffi.charp2str(cts.cast('char*', tp_name)) + # debug_print("tp_clear", pyobj, ": type", pto, + # ": name", name) generic_cpy_call(space, pto.c_tp_clear, pyobj) decref(space, py_obj) head = rawrefcount.cyclic_garbage_head(PyObject) @@ -365,6 +372,11 @@ w_list) rawrefcount.end_garbage() + duration = time.time() - start + module = space.builtin_modules['gc'] + durations = space.getattr(module, space.newtext('cpyext_durations')) + durations.append(space.newfloat(duration)) + class PyObjDeallocAction(executioncontext.AsyncAction): """An action that invokes _Py_Dealloc() on the dying PyObjects. """ diff --git a/rpython/memory/gc/rrc/incmark.py b/rpython/memory/gc/rrc/incmark.py --- a/rpython/memory/gc/rrc/incmark.py +++ b/rpython/memory/gc/rrc/incmark.py @@ -258,7 +258,8 @@ addr = self.snapshot_refs[obj.refs_index + j] obj_ref = llmemory.cast_adr_to_ptr(addr, self.PYOBJ_SNAPSHOT_OBJ_PTR) - obj_ref.refcnt -= 1 + if obj_ref != lltype.nullptr(self.PYOBJ_SNAPSHOT_OBJ): + obj_ref.refcnt -= 1 # now all rawrefcounted roots or live border objects have a # refcount > 0 @@ -299,7 +300,8 @@ addr = self.snapshot_refs[snapobj.refs_index + j] obj_ref = llmemory.cast_adr_to_ptr(addr, self.PYOBJ_SNAPSHOT_OBJ_PTR) - obj_ref.refcnt += 1 + if obj_ref != lltype.nullptr(self.PYOBJ_SNAPSHOT_OBJ): + obj_ref.refcnt += 1 # mark recursively, if it is a pypyobj if snapobj.pypy_link <> 0: intobj = snapobj.pypy_link @@ -320,6 +322,10 @@ total_refcnt += self._take_snapshot_count_gc(pygchdr) total_objs += 1 pygchdr = pygchdr.c_gc_next + pygchdr = self.tuple_list.c_gc_next + while pygchdr <> self.tuple_list: + total_refcnt += self._take_snapshot_count_gc(pygchdr) + pygchdr = pygchdr.c_gc_next pygchdr = self.pyobj_isolate_old_list.c_gc_next while pygchdr <> self.pyobj_isolate_old_list: total_refcnt += self._take_snapshot_count_gc(pygchdr) @@ -364,7 +370,10 @@ pygchdr = self.pyobj_as_gc(pyobj) if (pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR) and pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED): - obj = self.snapshot_objs[pygchdr.c_gc_refs - 1] + if pygchdr.c_gc_refs > 0: + obj = self.snapshot_objs[pygchdr.c_gc_refs - 1] + else: + obj = lltype.nullptr(self.PYOBJ_SNAPSHOT_OBJ) else: obj = self.snapshot_objs[pyobj.c_ob_pypy_link - 1] self.snapshot_refs[i] = llmemory.cast_ptr_to_adr(obj) diff --git a/rpython/memory/gc/test/dot/free_cpython_tuple_1.dot b/rpython/memory/gc/test/dot/free_cpython_tuple_1.dot --- a/rpython/memory/gc/test/dot/free_cpython_tuple_1.dot +++ b/rpython/memory/gc/test/dot/free_cpython_tuple_1.dot @@ -1,5 +1,5 @@ digraph G { - "a" [type=C, alive=n, tuple=y]; + "a" [type=C, alive=n, tuple=1]; "b" [type=C, alive=n]; "a" -> "b"; "b" -> "a"; diff --git a/rpython/memory/gc/test/dot/free_cpython_tuple_1.dot b/rpython/memory/gc/test/dot/free_cpython_tuple_2.dot copy from rpython/memory/gc/test/dot/free_cpython_tuple_1.dot copy to rpython/memory/gc/test/dot/free_cpython_tuple_2.dot --- a/rpython/memory/gc/test/dot/free_cpython_tuple_1.dot +++ b/rpython/memory/gc/test/dot/free_cpython_tuple_2.dot @@ -1,6 +1,5 @@ digraph G { - "a" [type=C, alive=n, tuple=y]; - "b" [type=C, alive=n]; + "a" [type=C, alive=y, ext_refcnt=1]; + "b" [type=C, alive=y, tuple=0]; "a" -> "b"; - "b" -> "a"; } diff --git a/rpython/memory/gc/test/dot/free_cpython_tuple_1.dot b/rpython/memory/gc/test/dot/free_cpython_tuple_3.dot copy from rpython/memory/gc/test/dot/free_cpython_tuple_1.dot copy to rpython/memory/gc/test/dot/free_cpython_tuple_3.dot --- a/rpython/memory/gc/test/dot/free_cpython_tuple_1.dot +++ b/rpython/memory/gc/test/dot/free_cpython_tuple_3.dot @@ -1,6 +1,5 @@ digraph G { - "a" [type=C, alive=n, tuple=y]; - "b" [type=C, alive=n]; + "a" [type=C, alive=y, ext_refcnt=1]; + "b" [type=C, alive=y, tuple=2]; "a" -> "b"; - "b" -> "a"; } diff --git a/rpython/memory/gc/test/test_rawrefcount.py b/rpython/memory/gc/test/test_rawrefcount.py --- a/rpython/memory/gc/test/test_rawrefcount.py +++ b/rpython/memory/gc/test/test_rawrefcount.py @@ -43,6 +43,7 @@ self.pyobj_resurrect = {} self.pyobj_delete = {} self.is_pygc = [] + self.tupletypes = [] def rawrefcount_tp_traverse(obj, callback, args): refs = self.pyobj_refs[self.pyobjs.index(obj)] @@ -73,13 +74,19 @@ return RAWREFCOUNT_FINALIZER_NONE def rawrefcount_tuple_maybe_untrack(obj): - #if foo: - # gchdr = rawrefcount_pyobj_as_gc(obj) - # next = gchdr.c_gc_next - # next.c_gc_prev = gchdr.c_gc_prev - # gchdr.c_gc_prev.c_gc_next = next - # return 0 - return 1 # TODO: add tests for 0 ("plain" tuple) and 2 (uninitialized) + index = self.pyobjs.index(obj) + if self.tupletypes[index] == '0': + gchdr = self.gcobjs[index] + next = gchdr.c_gc_next + next.c_gc_prev = gchdr.c_gc_prev + gchdr.c_gc_prev.c_gc_next = next + return 0 + elif self.tupletypes[index] == '1': + return 1 + elif self.tupletypes[index] == '2': + return 2 + else: + assert False self.pyobj_list = lltype.malloc(PYOBJ_GC_HDR_PTR.TO, flavor='raw', immortal=True) @@ -146,7 +153,7 @@ return p1, p1ref, check_alive def _rawrefcount_pyobj(self, create_immortal=False, is_gc=True, - tracked=True, tuple=tuple): + tracked=True, tuple=False, tuple_type=None): r1 = lltype.malloc(PYOBJ_HDR, flavor='raw', immortal=create_immortal) r1.c_ob_refcnt = 0 @@ -157,6 +164,7 @@ self._rawrefcount_add_gc(tracked, tuple) else: self.gcobjs.append(lltype.nullptr(PYOBJ_GC_HDR)) + self.tupletypes.append(tuple_type) self.pyobjs.append(r1) self.is_pygc.append(is_gc) @@ -171,7 +179,7 @@ def _rawrefcount_pair(self, intval, is_light=False, is_pyobj=False, create_old=False, create_immortal=False, rooted=False, force_external=False, is_gc=True, - tracked=True, tuple=tuple): + tracked=True, tuple=False, tuple_type=None): if is_light: rc = REFCNT_FROM_PYPY_LIGHT else: @@ -207,6 +215,7 @@ self._rawrefcount_add_gc(tracked, tuple) else: self.gcobjs.append(lltype.nullptr(PYOBJ_GC_HDR)) + self.tupletypes.append(tuple_type) self.pyobjs.append(r1) self.is_pygc.append(is_gc) @@ -558,7 +567,8 @@ resurrect = attr['resurrect'] if 'resurrect' in attr else None delete = attr['delete'] if 'delete' in attr else None garbage = True if 'garbage' in attr else False - tuple = attr['tuple'] == "y" if 'tuple' in attr else False + tuple = True if 'tuple' in attr else False + tuple_type = attr['tuple'] if 'tuple' in attr else None gc = attr['gc'] == "y" if 'gc' in attr else True added = attr['added'] if 'added' in attr else None info = NodeInfo(type, alive, ext_refcnt, finalizer, resurrect, @@ -569,7 +579,7 @@ add_pyobj_after_snap.append(nodes[name]) else: r, raddr, check_alive = self._rawrefcount_pyobj( - tracked=tracked, tuple=tuple) + tracked=tracked, tuple=tuple, tuple_type=tuple_type) r.c_ob_refcnt += ext_refcnt nodes[name] = CPythonNode(r, raddr, check_alive, info) elif type == "P": @@ -600,7 +610,7 @@ self._rawrefcount_pair(42 + i, rooted=rooted, create_old=True, tracked=tracked, tuple=tuple, - is_gc=gc) + tuple_type=tuple_type, is_gc=gc) r.c_ob_refcnt += ext_refcnt nodes[name] = BorderNode(p, pref, r, raddr, check_alive, info) From pypy.commits at gmail.com Sat Oct 5 07:49:37 2019 From: pypy.commits at gmail.com (cfbolz) Date: Sat, 05 Oct 2019 04:49:37 -0700 (PDT) Subject: [pypy-commit] extradoc extradoc: some more text Message-ID: <5d988351.1c69fb81.be3e3.4465@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: extradoc Changeset: r5963:73fb50383067 Date: 2019-10-05 13:49 +0200 http://bitbucket.org/pypy/extradoc/changeset/73fb50383067/ Log: some more text diff --git a/blog/draft/2019-10-json.rst b/blog/draft/2019-10-json.rst --- a/blog/draft/2019-10-json.rst +++ b/blog/draft/2019-10-json.rst @@ -223,4 +223,119 @@ (simplejson), with ujson, with the JSON parser of Node/V8 and with RapidJSON (in DOM mode). -$$$ + +I collected a number of medium-to-large JSON files to try the JSON +parsers on: + +- `Censys `__: A subset of the Censys port and + protocol scan data for websites in the Alexa top million domains +- `Gharchive `__: Github activity from + January 15-23, 2015 from Github Archive +- `Reddit `__: Reddit + comments from May 2009 +- Rosie: The nested matches produced using the `Rosie pattern + language `__ ``all.things`` pattern on a log + file +- Nytimes: Metadata of a collection of New York Times articles +- Tpch: The TPC-H database benchmark's deals table as a JSON file +- Twitter: A JSON export of the @pypyproject Twitter account data +- Wikidata: A file storing a subset of the Wikidata fact dump from Nov + 11, 2014 +- `Yelp `__: A file of yelp + businesses + +Here are the file sizes of the benchmarks: + +.. raw:: html + + \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
BenchmarkFile Size [MiB]
Censys898.45
Gharchive276.34
NYTimes12.98
Reddit931.65
Rosie388.88
TPCH173.86
Wikidata119.75
Yelp167.61
+ + +I measured the times of each benchmark with a number of variations +of the improved PyPy algorithms: + +- PyPyBaseline: The PyPy JSON parser as it was before my work with JSON + parsing started (PyPy version 5.8) +- PyPyKeyStringCaching: Memoizing the key strings of dictionaries, but + not the other strings in a json file, and not using maps to represent + dictionaries (this is the JSON parser that PyPy has been shipping since + version 5.9, in the benchmarks I used 7.1). +- PyPyMapNoCache: Like PyPyKeyStringCaching, but using maps to + represent dictionaries. This includes speculatively parsing the next + key using memcmp, but does not use string caching of non-key strings. +- PyPyFull: Like PyPyMapNoCache but uses a string cache for all + strings, not just keys. This is equivalent to what is in PyPy 7.2 + +In addition to wall clock time of parsing, I also measured the increase +in memory use of each implementation after the input string has been +deserialized, i.e. the size of the in-memory representation of every +JSON file. + + +Contributions of Individual Optimizations +--------------------------------------------- + +Let's first look at the contributions of the individual optimizations to the +overall performance and memory usage. + +$$ two graphs + +All the benchmarks were run 30 times in new processes, all the numbers are +normalized to PyPyFull. + +The biggest individual improvement to both parsing time and memory used comes +from caching just the keys in parsed dictionaries. This is the optimization in +PyPy's JSON parser that has been implemented for a while already. To understand +why this optimization is so useful, let's look at some numbers about each +benchmark, namely the number of total keys across all dictionaries in each +file, as well as the number of unique keys. As we can see, for all benchmarks +the number of unique keys is significantly smaller than the number of keys in +total. + +.. raw:: html + + \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
BenchmarkNumber of keysNumber of unique keys
Censys14\u2009404\u2009234163
Gharchive6\u2009637\u2009881169
NYTimes417\u200933760
Reddit25\u2009226\u200939721
Rosie28\u2009500\u20091015
TPCH6\u2009700\u200900045
Wikidata6\u2009235\u20090881\u2009602
Yelp5\u2009133\u200991461
+ + +The next big jump in deserialization time and memory comes from introducing +maps to represent deserialized dictionaries. With PyPyMapNoCache +deserialization time goes down because it's much cheaper to walk the tree +of maps and store all deserialized objects into an array of values than to +build hashmaps with the same keys again and again. Memory use goes down +for the same reason: it takes a lot less memory to store the shared +structure of each set of keys in the map, as opposed to repeating it again +and again in every hashmap. + +We can look at some numbers about every benchmark again. The table shows how +many map-based dictionaries are deserialized for every benchmark, and how many +hashmap-backed dictionaries. We see that the number of hashmap-backed +dictionaries is often zero, or at most a small percentage of all dictionaries +in each benchmark. Yelp has the biggest number of hashmap-backed dictionaries. +The reason for this is that the input file contains hashmaps that store +combinations of various features of Yelp businesses, and a lot of these +combinations are totally unique to a business. Therefore the heuristics +determine that it's better to store these using hashmaps. + +.. raw:: html + + \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
BenchmarkMap DictsRegular Dicts% Regular Dicts
Censys4\u2009049\u20092351\u20090420.03
Gharchive955\u200930100.00
NYTimes80\u200939300.00
Reddit1\u2009201\u200925700.00
Rosie6\u2009248\u200996600.00
TPCH1\u2009000\u200900000.00
Wikidata1\u2009923\u200946046\u20099052.38
Yelp443\u200914052\u200905110.51
+ + +We can also look at numbers about how often the memcmp-based speculative +parsing of the next key of a given map succeeds. Looking at statistics +about each benchmark, we can see that the speculation of what key we +expect next pays off in a significant percentage of cases, between 63% for +Wikidata where the dictionary structures are quite irregular, and 99% for +Reddit, where all the dictionaries have the same set of keys. + +.. raw:: html + + \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
BenchmarkNumber of KeysMap Transitions% Successful Speculation
Censys14\u2009404\u200923414\u2009403\u200924365.79
Gharchive6\u2009637\u20098816\u2009637\u200988186.71
NYTimes417\u2009337417\u200933779.85
Reddit25\u2009226\u200939725\u2009226\u2009397100.00
Rosie28\u2009500\u200910128\u2009500\u200910190.37
TPCH6\u2009700\u20090006\u2009700\u200900086.57
Wikidata6\u2009235\u20090885\u2009267\u200974463.68
Yelp5\u2009133\u20099144\u2009593\u200998090.43
geomean82.04
+ +General string caching is the most unclear optimization. On the one hand its +impact on memory usage is quite substantial, leading to a 20% reduction for +Gharchive and Reddit, up to a 2× improvement for Yelp. On the other hand, the +effect on performance is less clear, since it even leads to a slowdown in +Gharchive and Reddit, and generally only a small improvement. Choosing the +right heuristic for when to disable the cache also has somewhat unclear effects +and is definitely a topic worthy of further investigation. From pypy.commits at gmail.com Sun Oct 6 10:40:32 2019 From: pypy.commits at gmail.com (Yannick_Jadoul) Date: Sun, 06 Oct 2019 07:40:32 -0700 (PDT) Subject: [pypy-commit] pypy py3.7: Implemented __mro_entries__ from PEP 560 Message-ID: <5d99fce0.1c69fb81.9c2e1.c62d@mx.google.com> Author: Yannick Jadoul Branch: py3.7 Changeset: r97729:02a99370f6b3 Date: 2019-09-24 17:27 +0200 http://bitbucket.org/pypy/pypy/changeset/02a99370f6b3/ Log: Implemented __mro_entries__ from PEP 560 diff --git a/pypy/module/__builtin__/compiling.py b/pypy/module/__builtin__/compiling.py --- a/pypy/module/__builtin__/compiling.py +++ b/pypy/module/__builtin__/compiling.py @@ -96,13 +96,33 @@ frame = space.getexecutioncontext().gettopframe() frame.exec_(w_prog, w_globals, w_locals) +def _update_bases(space, w_bases): + bases_w = space.listview(w_bases) + new_bases_w = [] + changed = False + for w_base in bases_w: + w_meth = space.lookup(w_base, '__mro_entries__') + if w_meth is not None: + new_base_w = space.get_and_call_function(w_meth, w_base, w_bases) + if not space.isinstance_w(new_base_w, space.w_tuple): + raise oefmt(space.w_TypeError, "__mro_entries__ must return a tuple") + new_bases_w.extend(space.fixedview(new_base_w)) + changed = True + else: + new_bases_w.append(w_base) + if not changed: + return bases_w + return new_bases_w + def build_class(space, w_func, w_name, __args__): from pypy.objspace.std.typeobject import _calculate_metaclass, W_TypeObject from pypy.interpreter.nestedscope import Cell if not isinstance(w_func, Function): raise oefmt(space.w_TypeError, "__build_class__: func must be a function") - bases_w, kwds_w = __args__.unpack() - w_bases = space.newtuple(bases_w) + orig_bases_w, kwds_w = __args__.unpack() + w_orig_bases = space.newtuple(orig_bases_w) + bases_w = _update_bases(space, w_orig_bases) + w_bases = space.newtuple(bases_w[:]) w_meta = kwds_w.pop('metaclass', None) if w_meta is not None: isclass = space.isinstance_w(w_meta, space.w_type) @@ -144,6 +164,8 @@ frame = space.createframe(code, w_func.w_func_globals, w_func) frame.setdictscope(w_namespace) w_cell = frame.run() + if bases_w is not orig_bases_w: + space.setitem(w_namespace, space.newtext("__orig_bases__"), w_orig_bases) keywords = kwds_w.keys() args = Arguments(space, args_w=[w_name, w_bases, w_namespace], diff --git a/pypy/objspace/std/test/test_typeobject.py b/pypy/objspace/std/test/test_typeobject.py --- a/pypy/objspace/std/test/test_typeobject.py +++ b/pypy/objspace/std/test/test_typeobject.py @@ -1433,6 +1433,58 @@ assert WithMetaclass[int] == "Metaclass[int]" """ + def test_mro_entries(self): + """ + class BaseA: pass + class BaseB: pass + class BaseC: pass + class BaseD: pass + + class ProxyA: + def __mro_entries__(self, orig_bases): + return (BaseA,) + class ProxyAB: + def __mro_entries__(self, orig_bases): + return (BaseA, BaseB) + class ProxyNone: + def __mro_entries__(self, orig_bases): + return () + + class TestA(ProxyA()): pass + assert TestA.__bases__ == (BaseA,) + assert len(TestA.__orig_bases__) == 1 + assert isinstance(TestA.__orig_bases__[0], ProxyA) + + class TestAB(ProxyAB()): pass + assert TestAB.__bases__ == (BaseA, BaseB) + assert len(TestAB.__orig_bases__) == 1 + assert isinstance(TestAB.__orig_bases__[0], ProxyAB) + + class TestNone(ProxyNone()): pass + assert TestNone.__bases__ == (object,) + assert len(TestNone.__orig_bases__) == 1 + assert isinstance(TestNone.__orig_bases__[0], ProxyNone) + + class TestMixed(BaseC, ProxyAB(), BaseD, ProxyNone()): pass + assert TestMixed.__bases__ == (BaseC, BaseA, BaseB, BaseD) + assert len(TestMixed.__orig_bases__) == 4 + assert isinstance(TestMixed.__orig_bases__[1], ProxyAB) and isinstance(TestMixed.__orig_bases__[3], ProxyNone) + + with raises(TypeError) as excinfo: + class TestDuplicate(BaseB, ProxyAB()): pass + assert str(excinfo.value) == "duplicate base class 'BaseB'" + + with raises(TypeError) as excinfo: + type('TestType', (BaseC, ProxyAB(), BaseD, ProxyNone()), {}) + assert str(excinfo.value) == "type() doesn't support MRO entry resolution; use types.new_class()" + + import types + TestTypesNewClass = types.new_class('TestTypesNewClass', (BaseC, ProxyAB(), BaseD, ProxyNone()), {}) + assert TestMixed.__bases__ == (BaseC, BaseA, BaseB, BaseD) + assert len(TestMixed.__orig_bases__) == 4 + assert isinstance(TestMixed.__orig_bases__[1], ProxyAB) and isinstance(TestMixed.__orig_bases__[3], ProxyNone) + """ + class AppTestWithMethodCacheCounter: spaceconfig = {"objspace.std.withmethodcachecounter": True} diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py --- a/pypy/objspace/std/typeobject.py +++ b/pypy/objspace/std/typeobject.py @@ -808,6 +808,11 @@ # above to be seen by the jit. _check_new_args(space, w_name, w_bases, w_dict) bases_w = space.fixedview(w_bases) + for w_base in bases_w: + if space.lookup(w_base, '__mro_entries__') is not None: + raise oefmt(space.w_TypeError, + "type() doesn't support MRO entry resolution; " + "use types.new_class()") w_winner = _calculate_metaclass(space, w_typetype, bases_w) if not space.is_w(w_winner, w_typetype): From pypy.commits at gmail.com Sun Oct 6 10:40:34 2019 From: pypy.commits at gmail.com (Yannick_Jadoul) Date: Sun, 06 Oct 2019 07:40:34 -0700 (PDT) Subject: [pypy-commit] pypy py3.7: Fixed annotation errors related to fixed-sized lists, and added a test checking __orig_bases__ is only set when __bases__ gets changed through the __mro_entries__ mechanism Message-ID: <5d99fce2.1c69fb81.6ca67.597d@mx.google.com> Author: Yannick Jadoul Branch: py3.7 Changeset: r97730:01b4c2af5928 Date: 2019-09-24 19:08 +0200 http://bitbucket.org/pypy/pypy/changeset/01b4c2af5928/ Log: Fixed annotation errors related to fixed-sized lists, and added a test checking __orig_bases__ is only set when __bases__ gets changed through the __mro_entries__ mechanism diff --git a/pypy/module/__builtin__/compiling.py b/pypy/module/__builtin__/compiling.py --- a/pypy/module/__builtin__/compiling.py +++ b/pypy/module/__builtin__/compiling.py @@ -97,7 +97,7 @@ frame.exec_(w_prog, w_globals, w_locals) def _update_bases(space, w_bases): - bases_w = space.listview(w_bases) + bases_w = space.fixedview(w_bases) new_bases_w = [] changed = False for w_base in bases_w: @@ -112,7 +112,7 @@ new_bases_w.append(w_base) if not changed: return bases_w - return new_bases_w + return new_bases_w[:] def build_class(space, w_func, w_name, __args__): from pypy.objspace.std.typeobject import _calculate_metaclass, W_TypeObject @@ -122,7 +122,7 @@ orig_bases_w, kwds_w = __args__.unpack() w_orig_bases = space.newtuple(orig_bases_w) bases_w = _update_bases(space, w_orig_bases) - w_bases = space.newtuple(bases_w[:]) + w_bases = space.newtuple(bases_w) w_meta = kwds_w.pop('metaclass', None) if w_meta is not None: isclass = space.isinstance_w(w_meta, space.w_type) diff --git a/pypy/objspace/std/test/test_typeobject.py b/pypy/objspace/std/test/test_typeobject.py --- a/pypy/objspace/std/test/test_typeobject.py +++ b/pypy/objspace/std/test/test_typeobject.py @@ -1483,6 +1483,10 @@ assert TestMixed.__bases__ == (BaseC, BaseA, BaseB, BaseD) assert len(TestMixed.__orig_bases__) == 4 assert isinstance(TestMixed.__orig_bases__[1], ProxyAB) and isinstance(TestMixed.__orig_bases__[3], ProxyNone) + + class TestNoOrigBases(BaseA, BaseB): pass + assert TestNoOrigBases.__bases__ == (BaseA, BaseB) + assert not hasattr(TestNoOrigBases, '__orig_bases__') """ From pypy.commits at gmail.com Sun Oct 6 10:40:36 2019 From: pypy.commits at gmail.com (Yannick_Jadoul) Date: Sun, 06 Oct 2019 07:40:36 -0700 (PDT) Subject: [pypy-commit] pypy py3.7: merge upstream py3.7 Message-ID: <5d99fce4.1c69fb81.667e4.7f98@mx.google.com> Author: Yannick Jadoul Branch: py3.7 Changeset: r97731:0c9e1e22eaad Date: 2019-10-03 01:18 +0200 http://bitbucket.org/pypy/pypy/changeset/0c9e1e22eaad/ Log: merge upstream py3.7 diff --git a/pypy/module/__builtin__/compiling.py b/pypy/module/__builtin__/compiling.py --- a/pypy/module/__builtin__/compiling.py +++ b/pypy/module/__builtin__/compiling.py @@ -96,12 +96,32 @@ frame = space.getexecutioncontext().gettopframe() frame.exec_(w_prog, w_globals, w_locals) +def _update_bases(space, w_bases): + bases_w = space.fixedview(w_bases) + new_bases_w = [] + changed = False + for w_base in bases_w: + w_meth = space.lookup(w_base, '__mro_entries__') + if w_meth is not None: + new_base_w = space.get_and_call_function(w_meth, w_base, w_bases) + if not space.isinstance_w(new_base_w, space.w_tuple): + raise oefmt(space.w_TypeError, "__mro_entries__ must return a tuple") + new_bases_w.extend(space.fixedview(new_base_w)) + changed = True + else: + new_bases_w.append(w_base) + if not changed: + return bases_w + return new_bases_w[:] + def build_class(space, w_func, w_name, __args__): from pypy.objspace.std.typeobject import _calculate_metaclass, W_TypeObject from pypy.interpreter.nestedscope import Cell if not isinstance(w_func, Function): raise oefmt(space.w_TypeError, "__build_class__: func must be a function") - bases_w, kwds_w = __args__.unpack() + orig_bases_w, kwds_w = __args__.unpack() + w_orig_bases = space.newtuple(orig_bases_w) + bases_w = _update_bases(space, w_orig_bases) w_bases = space.newtuple(bases_w) w_meta = kwds_w.pop('metaclass', None) if w_meta is not None: @@ -144,6 +164,8 @@ frame = space.createframe(code, w_func.w_func_globals, w_func) frame.setdictscope(w_namespace) w_cell = frame.run() + if bases_w is not orig_bases_w: + space.setitem(w_namespace, space.newtext("__orig_bases__"), w_orig_bases) keywords = kwds_w.keys() args = Arguments(space, args_w=[w_name, w_bases, w_namespace], diff --git a/pypy/objspace/std/test/test_typeobject.py b/pypy/objspace/std/test/test_typeobject.py --- a/pypy/objspace/std/test/test_typeobject.py +++ b/pypy/objspace/std/test/test_typeobject.py @@ -1433,6 +1433,62 @@ assert WithMetaclass[int] == "Metaclass[int]" """ + def test_mro_entries(self): + """ + class BaseA: pass + class BaseB: pass + class BaseC: pass + class BaseD: pass + + class ProxyA: + def __mro_entries__(self, orig_bases): + return (BaseA,) + class ProxyAB: + def __mro_entries__(self, orig_bases): + return (BaseA, BaseB) + class ProxyNone: + def __mro_entries__(self, orig_bases): + return () + + class TestA(ProxyA()): pass + assert TestA.__bases__ == (BaseA,) + assert len(TestA.__orig_bases__) == 1 + assert isinstance(TestA.__orig_bases__[0], ProxyA) + + class TestAB(ProxyAB()): pass + assert TestAB.__bases__ == (BaseA, BaseB) + assert len(TestAB.__orig_bases__) == 1 + assert isinstance(TestAB.__orig_bases__[0], ProxyAB) + + class TestNone(ProxyNone()): pass + assert TestNone.__bases__ == (object,) + assert len(TestNone.__orig_bases__) == 1 + assert isinstance(TestNone.__orig_bases__[0], ProxyNone) + + class TestMixed(BaseC, ProxyAB(), BaseD, ProxyNone()): pass + assert TestMixed.__bases__ == (BaseC, BaseA, BaseB, BaseD) + assert len(TestMixed.__orig_bases__) == 4 + assert isinstance(TestMixed.__orig_bases__[1], ProxyAB) and isinstance(TestMixed.__orig_bases__[3], ProxyNone) + + with raises(TypeError) as excinfo: + class TestDuplicate(BaseB, ProxyAB()): pass + assert str(excinfo.value) == "duplicate base class 'BaseB'" + + with raises(TypeError) as excinfo: + type('TestType', (BaseC, ProxyAB(), BaseD, ProxyNone()), {}) + assert str(excinfo.value) == "type() doesn't support MRO entry resolution; use types.new_class()" + + import types + TestTypesNewClass = types.new_class('TestTypesNewClass', (BaseC, ProxyAB(), BaseD, ProxyNone()), {}) + assert TestMixed.__bases__ == (BaseC, BaseA, BaseB, BaseD) + assert len(TestMixed.__orig_bases__) == 4 + assert isinstance(TestMixed.__orig_bases__[1], ProxyAB) and isinstance(TestMixed.__orig_bases__[3], ProxyNone) + + class TestNoOrigBases(BaseA, BaseB): pass + assert TestNoOrigBases.__bases__ == (BaseA, BaseB) + assert not hasattr(TestNoOrigBases, '__orig_bases__') + """ + class AppTestWithMethodCacheCounter: spaceconfig = {"objspace.std.withmethodcachecounter": True} diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py --- a/pypy/objspace/std/typeobject.py +++ b/pypy/objspace/std/typeobject.py @@ -808,6 +808,11 @@ # above to be seen by the jit. _check_new_args(space, w_name, w_bases, w_dict) bases_w = space.fixedview(w_bases) + for w_base in bases_w: + if space.lookup(w_base, '__mro_entries__') is not None: + raise oefmt(space.w_TypeError, + "type() doesn't support MRO entry resolution; " + "use types.new_class()") w_winner = _calculate_metaclass(space, w_typetype, bases_w) if not space.is_w(w_winner, w_typetype): From pypy.commits at gmail.com Sun Oct 6 11:06:34 2019 From: pypy.commits at gmail.com (rlamy) Date: Sun, 06 Oct 2019 08:06:34 -0700 (PDT) Subject: [pypy-commit] pypy py3.7: Merged in Yannick_Jadoul/pypy/py3.7-pep562 (pull request #670) Message-ID: <5d9a02fa.1c69fb81.6b8e2.023c@mx.google.com> Author: Ronan Lamy Branch: py3.7 Changeset: r97733:dff15039dbab Date: 2019-10-06 15:05 +0000 http://bitbucket.org/pypy/pypy/changeset/dff15039dbab/ Log: Merged in Yannick_Jadoul/pypy/py3.7-pep562 (pull request #670) Implementation of PEP 562, adding __getattr__ and __dir__ for modules diff --git a/pypy/interpreter/module.py b/pypy/interpreter/module.py --- a/pypy/interpreter/module.py +++ b/pypy/interpreter/module.py @@ -134,6 +134,10 @@ except OperationError as e: if not e.match(space, space.w_AttributeError): raise + w_dict = self.w_dict + w_getattr = space.finditem(w_dict, space.newtext('__getattr__')) + if w_getattr is not None: + return space.call_function(w_getattr, w_attr) w_name = space.finditem(self.w_dict, space.newtext('__name__')) if w_name is None: raise oefmt(space.w_AttributeError, @@ -147,6 +151,9 @@ if not space.isinstance_w(w_dict, space.w_dict): raise oefmt(space.w_TypeError, "%N.__dict__ is not a dictionary", self) + w_dir = space.finditem(w_dict, space.newtext('__dir__')) + if w_dir is not None: + return space.call_function(w_dir) return space.call_function(space.w_list, w_dict) # These three methods are needed to implement '__class__' assignment 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 @@ -262,3 +262,29 @@ raises(AttributeError, "del x.a") raises(TypeError, "x.__class__ = X") raises(TypeError, "sys.__class__ = XX") + + def test_getattr_dir(self): + def __getattr__(name): + if name == 'y': + return 42 + elif name == 'z': + return + raise AttributeError("No attribute '{}'".format(name)) + + def __dir__(): + return ['x', 'y', 'z', 'w'] + + import sys + m = type(sys)('foo') + m.x = 'x' + m.__getattr__ = __getattr__ + print(m.__dir__) + m.__dir__ = __dir__ + + assert m.x == 'x' + assert m.y == 42 + assert m.z == None + excinfo = raises(AttributeError, 'm.w') + assert str(excinfo.value) == "No attribute 'w'" + print(dir(m)) + assert dir(m) == sorted(['x', 'y', 'z', 'w']) From pypy.commits at gmail.com Sun Oct 6 11:06:42 2019 From: pypy.commits at gmail.com (Yannick_Jadoul) Date: Sun, 06 Oct 2019 08:06:42 -0700 (PDT) Subject: [pypy-commit] pypy py3.7-pep562: Implementation of PEP 562, adding __getattr__ and __dir__ for modules Message-ID: <5d9a0302.1c69fb81.d03c0.879c@mx.google.com> Author: Yannick Jadoul Branch: py3.7-pep562 Changeset: r97732:bb000a053f9f Date: 2019-10-04 01:34 +0200 http://bitbucket.org/pypy/pypy/changeset/bb000a053f9f/ Log: Implementation of PEP 562, adding __getattr__ and __dir__ for modules diff --git a/pypy/interpreter/module.py b/pypy/interpreter/module.py --- a/pypy/interpreter/module.py +++ b/pypy/interpreter/module.py @@ -134,6 +134,10 @@ except OperationError as e: if not e.match(space, space.w_AttributeError): raise + w_dict = self.w_dict + w_getattr = space.finditem(w_dict, space.newtext('__getattr__')) + if w_getattr is not None: + return space.call_function(w_getattr, w_attr) w_name = space.finditem(self.w_dict, space.newtext('__name__')) if w_name is None: raise oefmt(space.w_AttributeError, @@ -147,6 +151,9 @@ if not space.isinstance_w(w_dict, space.w_dict): raise oefmt(space.w_TypeError, "%N.__dict__ is not a dictionary", self) + w_dir = space.finditem(w_dict, space.newtext('__dir__')) + if w_dir is not None: + return space.call_function(w_dir) return space.call_function(space.w_list, w_dict) # These three methods are needed to implement '__class__' assignment 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 @@ -262,3 +262,29 @@ raises(AttributeError, "del x.a") raises(TypeError, "x.__class__ = X") raises(TypeError, "sys.__class__ = XX") + + def test_getattr_dir(self): + def __getattr__(name): + if name == 'y': + return 42 + elif name == 'z': + return + raise AttributeError("No attribute '{}'".format(name)) + + def __dir__(): + return ['x', 'y', 'z', 'w'] + + import sys + m = type(sys)('foo') + m.x = 'x' + m.__getattr__ = __getattr__ + print(m.__dir__) + m.__dir__ = __dir__ + + assert m.x == 'x' + assert m.y == 42 + assert m.z == None + excinfo = raises(AttributeError, 'm.w') + assert str(excinfo.value) == "No attribute 'w'" + print(dir(m)) + assert dir(m) == sorted(['x', 'y', 'z', 'w']) From pypy.commits at gmail.com Sun Oct 6 15:11:47 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 06 Oct 2019 12:11:47 -0700 (PDT) Subject: [pypy-commit] pypy _siphash24-collecting: try to align the starts differently and pass translation on aarch64 Message-ID: <5d9a3c73.1c69fb81.42918.27fd@mx.google.com> Author: Matti Picus Branch: _siphash24-collecting Changeset: r97734:1fdf07c8f10e Date: 2019-10-06 22:10 +0300 http://bitbucket.org/pypy/pypy/changeset/1fdf07c8f10e/ Log: try to align the starts differently and pass translation on aarch64 diff --git a/rpython/rlib/rsiphash.py b/rpython/rlib/rsiphash.py --- a/rpython/rlib/rsiphash.py +++ b/rpython/rlib/rsiphash.py @@ -256,8 +256,9 @@ v2 = k0 ^ magic2 v3 = k1 ^ magic3 - direct = (SZ == 1) and (misaligned_is_fine or - (rffi.cast(lltype.Signed, addr_in) & 7) == 0) + # direct = (SZ == 1) and (misaligned_is_fine or + # (rffi.cast(lltype.Signed, addr_in) & 7) == 0) + direct = (SZ == 1) and misaligned_is_fine if direct: assert SZ == 1 while size >= 8: From pypy.commits at gmail.com Mon Oct 7 14:32:06 2019 From: pypy.commits at gmail.com (rlamy) Date: Mon, 07 Oct 2019 11:32:06 -0700 (PDT) Subject: [pypy-commit] pypy py3.6-asyncgen: Remove unused argument in_generator from execute_frame() Message-ID: <5d9b84a6.1c69fb81.af29f.4705@mx.google.com> Author: Ronan Lamy Branch: py3.6-asyncgen Changeset: r97735:43827ffdbe61 Date: 2019-10-07 19:31 +0100 http://bitbucket.org/pypy/pypy/changeset/43827ffdbe61/ Log: Remove unused argument in_generator from execute_frame() diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py --- a/pypy/interpreter/generator.py +++ b/pypy/interpreter/generator.py @@ -125,7 +125,7 @@ self.KIND) self.running = True try: - w_result = frame.execute_frame(self, w_arg_or_err) + w_result = frame.execute_frame(w_arg_or_err) except OperationError as e: # errors finish a frame try: diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py --- a/pypy/interpreter/pyframe.py +++ b/pypy/interpreter/pyframe.py @@ -351,9 +351,9 @@ else: return r_uint(0) - def execute_frame(self, in_generator=None, w_arg_or_err=None): + def execute_frame(self, w_arg_or_err=None): """Execute this frame. Main entry point to the interpreter. - 'in_generator' is non-None iff we are starting or resuming + 'w_arg_or_err' is non-None iff we are starting or resuming a generator or coroutine frame; in that case, w_arg_or_err is the input argument -or- an SApplicationException instance. """ @@ -375,7 +375,7 @@ # the YIELD_VALUE/YIELD_FROM instruction. try: try: - if in_generator is None: + if w_arg_or_err is None: assert self.last_instr == -1 next_instr = r_uint(0) else: diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py --- a/pypy/interpreter/pyopcode.py +++ b/pypy/interpreter/pyopcode.py @@ -1151,8 +1151,8 @@ def YIELD_FROM(self, oparg, next_instr): # Unlike CPython, we handle this not by repeating the same # bytecode over and over until the inner iterator is exhausted. - # Instead, we directly set the generator's w_yielded_from. - # This asks generator.resume_execute_frame() to exhaust that + # Instead, we set w_yielding_from. + # This asks resume_execute_frame() to exhaust that # sub-iterable first before continuing on the next bytecode. w_inputvalue = self.popvalue() # that's always w_None, actually w_gen = self.popvalue() From pypy.commits at gmail.com Mon Oct 7 15:53:37 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 07 Oct 2019 12:53:37 -0700 (PDT) Subject: [pypy-commit] pypy default: add jit help to documentation Message-ID: <5d9b97c1.1c69fb81.8c9a.6c8b@mx.google.com> Author: Matti Picus Branch: Changeset: r97736:42337a484364 Date: 2019-10-07 22:51 +0300 http://bitbucket.org/pypy/pypy/changeset/42337a484364/ Log: add jit help to documentation diff --git a/pypy/doc/commandline_ref.rst b/pypy/doc/commandline_ref.rst --- a/pypy/doc/commandline_ref.rst +++ b/pypy/doc/commandline_ref.rst @@ -9,3 +9,4 @@ man/pypy.1.rst man/pypy3.1.rst + jit_help.rst diff --git a/pypy/doc/jit_help.rst b/pypy/doc/jit_help.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/jit_help.rst @@ -0,0 +1,78 @@ +======== +JIT help +======== + +.. note this is from ``pypy --jit help`` + +Advanced JIT options +==================== + +`` --jit`` [*options*] where *options* is a comma-separated list of +``OPTION=VALUE``: + + decay=N + amount to regularly decay counters by (0=none, 1000=max) (default 40) + + disable_unrolling=N + after how many operations we should not unroll (default 200) + + enable_opts=N + INTERNAL USE ONLY (MAY NOT WORK OR LEAD TO CRASHES): optimizations to + enable, or all = + intbounds:rewrite:virtualize:string:pure:earlyforce:heap:unroll (default + all) + + function_threshold=N + number of times a function must run for it to become traced from start + (default 1619) + + inlining=N + inline python functions or not (1/0) (default 1) + + loop_longevity=N + a parameter controlling how long loops will be kept before being freed, + an estimate (default 1000) + + max_retrace_guards=N + number of extra guards a retrace can cause (default 15) + + max_unroll_loops=N + number of extra unrollings a loop can cause (default 0) + + max_unroll_recursion=N + how many levels deep to unroll a recursive function (default 7) + + retrace_limit=N + how many times we can try retracing before giving up (default 0) + + threshold=N + number of times a loop has to run for it to become hot (default 1039) + + trace_eagerness=N + number of times a guard has to fail before we start compiling a bridge + (default 200) + + trace_limit=N + number of recorded operations before we abort tracing with ABORT_TOO_LONG + (default 6000) + + vec=N + turn on the vectorization optimization (vecopt). Supports x86 (SSE 4.1), + powerpc (SVX), s390x SIMD (default 0) + + vec_all=N + try to vectorize trace loops that occur outside of the numpypy library + (default 0) + + vec_cost=N + threshold for which traces to bail. Unpacking increases the counter, + vector operation decrease the cost (default 0) + + off + turn off the JIT + help + print this page + +The :ref:`pypyjit` module can be used to control the JIT from inside +pypy + From pypy.commits at gmail.com Mon Oct 7 15:53:39 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 07 Oct 2019 12:53:39 -0700 (PDT) Subject: [pypy-commit] pypy default: clean up "make html" warnings, remove a few blank whatsnew files Message-ID: <5d9b97c3.1c69fb81.3eb93.2f9d@mx.google.com> Author: Matti Picus Branch: Changeset: r97737:96a1c0a30e47 Date: 2019-10-07 22:52 +0300 http://bitbucket.org/pypy/pypy/changeset/96a1c0a30e47/ Log: clean up "make html" warnings, remove a few blank whatsnew files diff --git a/pypy/doc/index-of-whatsnew.rst b/pypy/doc/index-of-whatsnew.rst --- a/pypy/doc/index-of-whatsnew.rst +++ b/pypy/doc/index-of-whatsnew.rst @@ -42,7 +42,8 @@ ------------------------------- .. toctree:: - whatsnew-pypy3-head.rst + whatsnew-pypy3-head.rst + whatsnew-pypy3-7.1.0.rst CPython 3.5 compatible versions ------------------------------- @@ -50,6 +51,8 @@ .. toctree:: whatsnew-pypy3-7.0.0.rst + whatsnew-pypy3-6.0.0.rst + whatsnew-pypy3-5.10.0.rst whatsnew-pypy3-5.9.0.rst whatsnew-pypy3-5.8.0.rst whatsnew-pypy3-5.7.0.rst @@ -62,10 +65,4 @@ whatsnew-pypy3-5.5.0.rst whatsnew-pypy3-5.1.1-alpha1.rst -CPython 3.2 compatible versions -------------------------------- -.. toctree:: - - whatsnew-pypy3-2.4.0.rst - whatsnew-pypy3-2.3.1.rst diff --git a/pypy/doc/release-v7.2.0.rst b/pypy/doc/release-v7.2.0.rst --- a/pypy/doc/release-v7.2.0.rst +++ b/pypy/doc/release-v7.2.0.rst @@ -307,6 +307,7 @@ .. _33786 : https://bugs.python.org/issue33786 .. _32270 : https://bugs.python.org/issue32270 .. _28691 : https://bugs.python.org/issue28691 +.. _33729 : https://bugs.python.org/issue33729 .. _opencv2: https://github.com/skvark/opencv-python/ .. _`issue 2617`: https://bitbucket.com/pypy/pypy/issues/2617 diff --git a/pypy/doc/whatsnew-pypy2-5.7.0.rst b/pypy/doc/whatsnew-pypy2-5.7.0.rst --- a/pypy/doc/whatsnew-pypy2-5.7.0.rst +++ b/pypy/doc/whatsnew-pypy2-5.7.0.rst @@ -14,6 +14,7 @@ changed in exactly the same way because of this fix. + .. branch: rpython-error-to-systemerror Any uncaught RPython exception (from a PyPy bug) is turned into an diff --git a/pypy/doc/whatsnew-pypy3-2.3.1.rst b/pypy/doc/whatsnew-pypy3-2.3.1.rst deleted file mode 100644 --- a/pypy/doc/whatsnew-pypy3-2.3.1.rst +++ /dev/null @@ -1,6 +0,0 @@ -========================= -What's new in PyPy3 2.3.1 -========================= - -.. this is a revision shortly after pypy3-release-2.3.x -.. startrev: 0137d8e6657d diff --git a/pypy/doc/whatsnew-pypy3-2.4.0.rst b/pypy/doc/whatsnew-pypy3-2.4.0.rst deleted file mode 100644 --- a/pypy/doc/whatsnew-pypy3-2.4.0.rst +++ /dev/null @@ -1,6 +0,0 @@ -========================= -What's new in PyPy3 2.4.0 -========================= - -.. this is a revision shortly after pypy3-release-2.4.x -.. startrev: 12b940544622 From pypy.commits at gmail.com Mon Oct 7 17:42:45 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 07 Oct 2019 14:42:45 -0700 (PDT) Subject: [pypy-commit] pypy _siphash24-collecting: simplify test but it still passes (it should fail...) Message-ID: <5d9bb155.1c69fb81.f054b.a264@mx.google.com> Author: Matti Picus Branch: _siphash24-collecting Changeset: r97739:158a54d11ca5 Date: 2019-10-08 00:41 +0300 http://bitbucket.org/pypy/pypy/changeset/158a54d11ca5/ Log: simplify test but it still passes (it should fail...) diff --git a/rpython/memory/gctransform/test/test_framework.py b/rpython/memory/gctransform/test/test_framework.py --- a/rpython/memory/gctransform/test/test_framework.py +++ b/rpython/memory/gctransform/test/test_framework.py @@ -151,101 +151,31 @@ def test_cast_no_collect(): # mimic some of the behaviour of _siphash24 from rpython.rlib import rgc - from rpython.rlib.rarithmetic import r_uint64, r_uint32 + from rpython.rlib.rarithmetic import r_uint64 from rpython.translator.c.genc import CStandaloneBuilder @always_inline - def _rotate(x, b): - return (x << b) | (x >> (64 - b)) - - @always_inline - def _half_round(a, b, c, d, s, t): - a += b - c += d - b = _rotate(b, s) ^ a - d = _rotate(d, t) ^ c - a = _rotate(a, 32) - return a, b, c, d - - @always_inline def _double_round(v0, v1, v2, v3): - v0,v1,v2,v3 = _half_round(v0,v1,v2,v3,13,16) - v2,v1,v0,v3 = _half_round(v2,v1,v0,v3,17,21) - v0,v1,v2,v3 = _half_round(v0,v1,v2,v3,13,16) - v2,v1,v0,v3 = _half_round(v2,v1,v0,v3,17,21) return v0, v1, v2, v3 @rgc.no_collect - def _siphash24(addr_in, size, SZ=1): - index = 0 + def _siphash24(addr_in): v0 = r_uint64(0x736f6d6570736575) v1 = r_uint64(0x646f72616e646f6d) v2 = r_uint64(0x6c7967656e657261) v3 = r_uint64(0x7465646279746573) - b = r_uint64(size) << 56 - while size >= 8: - mi = ( - r_uint64(llop.raw_load(rffi.UCHAR, addr_in, index)) | - r_uint64(llop.raw_load(rffi.UCHAR, addr_in, index + 1*SZ)) << 8 | - r_uint64(llop.raw_load(rffi.UCHAR, addr_in, index + 2*SZ)) << 16 | - r_uint64(llop.raw_load(rffi.UCHAR, addr_in, index + 3*SZ)) << 24 | - r_uint64(llop.raw_load(rffi.UCHAR, addr_in, index + 4*SZ)) << 32 | - r_uint64(llop.raw_load(rffi.UCHAR, addr_in, index + 5*SZ)) << 40 | - r_uint64(llop.raw_load(rffi.UCHAR, addr_in, index + 6*SZ)) << 48 | - r_uint64(llop.raw_load(rffi.UCHAR, addr_in, index + 7*SZ)) << 56 - ) - size -= 8 - index += 8*SZ - v3 ^= mi - v0, v1, v2, v3 = _double_round(v0, v1, v2, v3) - v0 ^= mi - - t = r_uint64(0) - if size == 7: - t = r_uint64(llop.raw_load(rffi.UCHAR, addr_in, index + 6*SZ)) << 48 - size = 6 - if size == 6: - t |= r_uint64(llop.raw_load(rffi.UCHAR, addr_in, index + 5*SZ)) << 40 - size = 5 - if size == 5: - t |= r_uint64(llop.raw_load(rffi.UCHAR, addr_in, index + 4*SZ)) << 32 - size = 4 - if size == 4: - t |= r_uint64(llop.raw_load(rffi.UCHAR, addr_in, index + 3*SZ))<< 24 - size = 3 - if size == 3: - t |= r_uint64(llop.raw_load(rffi.UCHAR, addr_in, index + 2*SZ)) << 16 - size = 2 - if size == 2: - t |= r_uint64(llop.raw_load(rffi.UCHAR, addr_in, index + 1*SZ)) << 8 - size = 1 - if size == 1: - t |= r_uint64(llop.raw_load(rffi.UCHAR, addr_in, index)) - size = 0 - assert size == 0 - - b |= t - - v3 ^= b + mi = r_uint64(0) + v3 ^= mi v0, v1, v2, v3 = _double_round(v0, v1, v2, v3) - v0 ^= b - v2 ^= 0xff - v0, v1, v2, v3 = _double_round(v0, v1, v2, v3) - v0, v1, v2, v3 = _double_round(v0, v1, v2, v3) + v0 ^= mi return (v0 ^ v1) ^ (v2 ^ v3) - def siphash24(s): - """'s' is a normal string. Returns its siphash-2-4 as a r_uint64. - Don't forget to cast the result to a regular integer if needed, - e.g. with rarithmetic.intmask(). - """ - with rffi.scoped_nonmovingbuffer(s) as p: - return _siphash24(llmemory.cast_ptr_to_adr(p), len(s)) + def entrypoint(argv): - return siphash24('abc') + return _siphash24('abc') t = rtype(entrypoint, [s_list_of_strings]) t.config.translation.gc = "minimark" From pypy.commits at gmail.com Wed Oct 9 03:38:41 2019 From: pypy.commits at gmail.com (stevie_92) Date: Wed, 09 Oct 2019 00:38:41 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-gc-cycle: Implemented configurable limit for incremental rrc collection Message-ID: <5d9d8e81.1c69fb81.dd898.d03f@mx.google.com> Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r97741:518eb87d054c Date: 2019-10-09 09:37 +0200 http://bitbucket.org/pypy/pypy/changeset/518eb87d054c/ Log: Implemented configurable limit for incremental rrc collection Implemented working set to improve overall performance 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 @@ -2415,7 +2415,6 @@ # finalizers/weak references are rare and short which means that # they do not need a separate state and do not need to be # made incremental. - # For now, the same applies to rawrefcount'ed objects. if rrc_finished: ll_assert(not (self.probably_young_objects_with_finalizers .non_empty()), diff --git a/rpython/memory/gc/rrc/base.py b/rpython/memory/gc/rrc/base.py --- a/rpython/memory/gc/rrc/base.py +++ b/rpython/memory/gc/rrc/base.py @@ -1,6 +1,7 @@ from rpython.rtyper.lltypesystem import lltype, llmemory, llgroup, rffi from rpython.rtyper.lltypesystem.lloperation import llop from rpython.rlib.debug import ll_assert, debug_print, debug_start, debug_stop +from rpython.memory.gc import env def choose_rrc_gc_from_config(config): if config.translation.rrcgc: @@ -118,6 +119,11 @@ self.tuple_maybe_untrack = tuple_maybe_untrack self.state = self.STATE_DEFAULT self.cycle_enabled = True + inc_limit = env.read_uint_from_env('PYPY_RRC_GC_INCREMENT_STEP') + if inc_limit > 0: + self.inc_limit = inc_limit + else: + self.inc_limit = 1000 def create_link_pypy(self, gcobj, pyobject): obj = llmemory.cast_ptr_to_adr(gcobj) @@ -566,8 +572,14 @@ pygchdr = self.pyobj_as_gc(pyobj) if pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR): if pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED: - pygchdr.c_gc_refs += self.refcnt_add << \ - self.RAWREFCOUNT_REFS_SHIFT + if (self.state != self.STATE_GARBAGE_MARKING and + pygchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT == 0 + and self.refcnt_add > 0): + addr = llmemory.cast_ptr_to_adr(pygchdr) + self.pyobj_to_trace.append(addr) + else: + pygchdr.c_gc_refs += (self.refcnt_add << + self.RAWREFCOUNT_REFS_SHIFT) elif pyobj.c_ob_pypy_link != 0: pyobj.c_ob_refcnt += self.refcnt_add if self.refcnt_add > 0: diff --git a/rpython/memory/gc/rrc/incmark.py b/rpython/memory/gc/rrc/incmark.py --- a/rpython/memory/gc/rrc/incmark.py +++ b/rpython/memory/gc/rrc/incmark.py @@ -16,37 +16,46 @@ if self.state == self.STATE_DEFAULT: # untrack all tuples with only non-gc rrc objects and # promote all other tuples to the pyobj_list - self._untrack_tuples() - # TODO: execute incrementally? (before snapshot!, own phase) + self._untrack_tuples() # execute incrementally? # now take a snapshot self._take_snapshot() self._debug_print_snap(print_label="after-snapshot") - # collect all rawrefcounted roots - self._collect_roots() - # TODO: execute incrementally (own phase, save index) - - self._debug_print_snap(print_label="roots-marked") - self._debug_check_consistency(print_label="roots-marked") - - self._gc_list_init(self.pyobj_old_list) self.state = self.STATE_MARKING + self.marking_state = 0 return False if self.state == self.STATE_MARKING: - # mark all objects reachable from rawrefcounted roots - all_rrc_marked = self._mark_rawrefcount() - # TODO: execute incrementally - - if (all_rrc_marked and not self.gc.objects_to_trace.non_empty() and - not self.gc.more_objects_to_trace.non_empty()): - # all objects have been marked, dead objects will stay dead - self._debug_print_snap(print_label="before-fin") - self._debug_check_consistency(print_label="before-fin") - self.state = self.STATE_GARBAGE_MARKING + if self.marking_state == 0: + # collect all rawrefcounted roots + self._collect_roots() # execute incrementally (save index)? + self._debug_print_snap(print_label="roots-marked") + self._debug_check_consistency(print_label="roots-marked") + self._gc_list_init(self.pyobj_old_list) + self.marking_state = 1 + return False + elif self.marking_state == 1: + # initialize working set from roots, then pause + self.pyobj_to_trace = self.gc.AddressStack() + for i in range(0, self.total_objs): + obj = self.snapshot_objs[i] + self._mark_rawrefcount_obj(obj) + self.p_list_old.foreach(self._mark_rawrefcount_linked, None) + self.o_list_old.foreach(self._mark_rawrefcount_linked, None) + self.marking_state = 2 + return False else: - return False + # mark all objects reachable from rawrefcounted roots + all_rrc_marked = self._mark_rawrefcount() + if (all_rrc_marked and not self.gc.objects_to_trace.non_empty() and + not self.gc.more_objects_to_trace.non_empty()): + # all objects have been marked, dead objects will stay dead + self._debug_print_snap(print_label="before-fin") + self._debug_check_consistency(print_label="before-fin") + self.state = self.STATE_GARBAGE_MARKING + else: + return False # we are finished with marking, now finish things up ll_assert(self.state == self.STATE_GARBAGE_MARKING, "invalid state") @@ -91,7 +100,8 @@ # sync p_list_old (except gc-objects) # simply iterate the snapshot for objects in p_list, as linked objects - # might not be freed, except by the gc + # might not be freed, except by the gc; p_list is always at the + # beginning of the snapshot, so break if we reached a different pyobj free_p_list = self.gc.AddressStack() for i in range(0, self.total_objs): snapobj = self.snapshot_objs[i] @@ -102,6 +112,10 @@ if (pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR) and pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED): break # only look for non-gc + addr = llmemory.cast_int_to_adr(snapobj.pypy_link) + if (self.gc.header(addr).tid & + (self.GCFLAG_VISITED | self.GCFLAG_NO_HEAP_PTRS)): + continue # keep proxy if obj is marked if snapobj.refcnt == 0: # check consistency consistent = pyobj.c_ob_refcnt == snapobj.refcnt_original @@ -269,17 +283,23 @@ # objects are found, increment the refcount of all referenced objects # of those newly found objects reached_limit = False - found_alive = True simple_limit = 0 + first = True # rescan proxies, in case only non-rc have been marked # - while found_alive and not reached_limit: # TODO: working set to improve performance? - found_alive = False - for i in range(0, self.total_objs): - obj = self.snapshot_objs[i] - found_alive |= self._mark_rawrefcount_obj(obj) - simple_limit += 1 - if simple_limit > 3: # TODO: implement sane limit - reached_limit + while first or (self.pyobj_to_trace.non_empty() and not reached_limit): + while self.pyobj_to_trace.non_empty() and not reached_limit: + addr = self.pyobj_to_trace.pop() + snapobj = llmemory.cast_adr_to_ptr(addr, + self.PYOBJ_SNAPSHOT_OBJ_PTR) + snapobj.refcnt += 1 + self._mark_rawrefcount_obj(snapobj) + simple_limit += 1 + if simple_limit > self.inc_limit: # TODO: add test + reached_limit = True + + self.p_list_old.foreach(self._mark_rawrefcount_linked, None) + self.o_list_old.foreach(self._mark_rawrefcount_linked, None) + first = False return not reached_limit # are there any objects left? def _mark_rawrefcount_obj(self, snapobj): @@ -301,17 +321,38 @@ obj_ref = llmemory.cast_adr_to_ptr(addr, self.PYOBJ_SNAPSHOT_OBJ_PTR) if obj_ref != lltype.nullptr(self.PYOBJ_SNAPSHOT_OBJ): - obj_ref.refcnt += 1 + if obj_ref.refcnt == 0: + addr = llmemory.cast_ptr_to_adr(obj_ref) + self.pyobj_to_trace.append(addr) + else: + obj_ref.refcnt += 1 # mark recursively, if it is a pypyobj if snapobj.pypy_link <> 0: intobj = snapobj.pypy_link obj = llmemory.cast_int_to_adr(intobj) self.gc.objects_to_trace.append(obj) - self.gc.visit_all_objects() # TODO: remove to improve pause times + self.gc.visit_all_objects() # TODO: move to outer loop, implement sane limit (ex. half of normal limit), retrace proxies # mark as processed snapobj.status = 0 return alive + def _mark_rawrefcount_linked(self, pyobject, ignore): + # we only have to take gc-objs into consideration, rc-proxies only + # keep their non-rc objs alive (see _major_free) + pyobj = self._pyobj(pyobject) + addr = llmemory.cast_int_to_adr(pyobj.c_ob_pypy_link) + if self.gc.header(addr).tid & (self.GCFLAG_VISITED | + self.GCFLAG_NO_HEAP_PTRS): + pygchdr = self.pyobj_as_gc(pyobj) + if (pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR) and + pygchdr.c_gc_refs > 0 and + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED): + index = pygchdr.c_gc_refs - 1 + snapobj = self.snapshot_objs[index] + if snapobj.refcnt == 0: + addr = llmemory.cast_ptr_to_adr(snapobj) + self.pyobj_to_trace.append(addr) + def _take_snapshot(self): total_refcnt = 0 total_objs = 0 diff --git a/rpython/memory/gc/rrc/mark.py b/rpython/memory/gc/rrc/mark.py --- a/rpython/memory/gc/rrc/mark.py +++ b/rpython/memory/gc/rrc/mark.py @@ -166,25 +166,36 @@ self._gc_list_init(self.pyobj_old_list) else: self._gc_list_move(self.pyobj_list, self.pyobj_old_list) + pyobj_old = self.pyobj_list + + # initialize working set + self.pyobj_to_trace = self.gc.AddressStack() + gchdr = self.pyobj_old_list.c_gc_next + while gchdr <> self.pyobj_old_list: + next_old = gchdr.c_gc_next + self._mark_rawrefcount_obj(gchdr, pyobj_old) + gchdr = next_old + gchdr = self.pyobj_isolate_old_list.c_gc_next + while gchdr <> self.pyobj_isolate_old_list: + next_old = gchdr.c_gc_next + self._mark_rawrefcount_obj(gchdr, pyobj_old) + gchdr = next_old + self.p_list_old.foreach(self._mark_rawrefcount_linked, None) + self.o_list_old.foreach(self._mark_rawrefcount_linked, None) + # as long as new objects with cyclic a refcount > 0 or alive border # objects are found, increment the refcount of all referenced objects # of those newly found objects - found_alive = True - pyobj_old = self.pyobj_list - # - while found_alive: # TODO: working set to improve performance? - found_alive = False - gchdr = self.pyobj_old_list.c_gc_next - while gchdr <> self.pyobj_old_list: - next_old = gchdr.c_gc_next - found_alive |= self._mark_rawrefcount_obj(gchdr, pyobj_old) - gchdr = next_old - gchdr = self.pyobj_isolate_old_list.c_gc_next - while gchdr <> self.pyobj_isolate_old_list: - next_old = gchdr.c_gc_next - found_alive |= self._mark_rawrefcount_obj(gchdr, pyobj_old) - gchdr = next_old - # + while self.pyobj_to_trace.non_empty(): + while self.pyobj_to_trace.non_empty(): + addr = self.pyobj_to_trace.pop() + gchdr = llmemory.cast_adr_to_ptr(addr, self.PYOBJ_GC_HDR_PTR) + gchdr.c_gc_refs += 1 << self.RAWREFCOUNT_REFS_SHIFT + self._mark_rawrefcount_obj(gchdr, pyobj_old) + self.gc.visit_all_objects() + self.p_list_old.foreach(self._mark_rawrefcount_linked, None) + self.o_list_old.foreach(self._mark_rawrefcount_linked, None) + # now all rawrefcounted objects, which are alive, have a cyclic # refcount > 0 or are marked @@ -212,5 +223,15 @@ # mark recursively, if it is a pypyobj if pyobj.c_ob_pypy_link <> 0: self.gc.objects_to_trace.append(obj) - self.gc.visit_all_objects() return alive + + def _mark_rawrefcount_linked(self, pyobject, ignore): + pyobj = self._pyobj(pyobject) + obj = self.refcnt_dict.get(pyobject) + if self.gc.header(obj).tid & (self.GCFLAG_VISITED | + self.GCFLAG_NO_HEAP_PTRS): + gchdr = self.pyobj_as_gc(pyobj) + if gchdr <> lltype.nullptr(self.PYOBJ_GC_HDR): + if gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT == 0: + addr = llmemory.cast_ptr_to_adr(gchdr) + self.pyobj_to_trace.append(addr) diff --git a/rpython/memory/gc/test/test_rawrefcount.py b/rpython/memory/gc/test/test_rawrefcount.py --- a/rpython/memory/gc/test/test_rawrefcount.py +++ b/rpython/memory/gc/test/test_rawrefcount.py @@ -104,6 +104,7 @@ rawrefcount_pyobj_as_gc, rawrefcount_finalizer_type, rawrefcount_tuple_maybe_untrack) + self.gc.rrc_gc.inc_limit = 2 # low limit to test incremental collection def _collect(self, major, expected_trigger=0): if major: From pypy.commits at gmail.com Wed Oct 9 05:06:49 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 09 Oct 2019 02:06:49 -0700 (PDT) Subject: [pypy-commit] pypy _siphash24-collecting: be closer to what the translation does, which actually makes the test pass Message-ID: <5d9da329.1c69fb81.197af.749a@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: _siphash24-collecting Changeset: r97742:3a1a3aada546 Date: 2019-10-09 11:06 +0200 http://bitbucket.org/pypy/pypy/changeset/3a1a3aada546/ Log: be closer to what the translation does, which actually makes the test pass (hence it didn't reproduce the actual problem) diff --git a/rpython/memory/gctransform/test/test_framework.py b/rpython/memory/gctransform/test/test_framework.py --- a/rpython/memory/gctransform/test/test_framework.py +++ b/rpython/memory/gctransform/test/test_framework.py @@ -174,6 +174,7 @@ return _siphash24('abc') t = rtype(entrypoint, [s_list_of_strings]) + backend_optimizations(t, clever_malloc_removal=False, storesink=True) t.config.translation.gc = "minimark" cbuild = CStandaloneBuilder(t, entrypoint, t.config, gcpolicy=FrameworkGcPolicy2) From pypy.commits at gmail.com Wed Oct 9 07:22:00 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 09 Oct 2019 04:22:00 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: move fast path from interp_codecs to unicodehelper, since several places call that directly Message-ID: <5d9dc2d8.1c69fb81.23e2e.6d9b@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r97743:40f56f2802c9 Date: 2019-10-09 12:21 +0200 http://bitbucket.org/pypy/pypy/changeset/40f56f2802c9/ Log: move fast path from interp_codecs to unicodehelper, since several places call that directly diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -375,6 +375,14 @@ return res_utf8, len(res), size def str_decode_utf8(s, errors, final, errorhandler, allow_surrogates=False): + try: + # fast version first + return s, rutf8.check_utf8(s, allow_surrogates=allow_surrogates), len(s) + except rutf8.CheckError: + return _str_decode_utf8_slowpath( + s, errors, final, errorhandler, allow_surrogates=allow_surrogates) + +def _str_decode_utf8_slowpath(s, errors, final, errorhandler, allow_surrogates): """ Same as checking for the valid utf8, but we know the utf8 is not valid so we're trying to either raise or pack stuff with error handler. The key difference is that this is call_may_force diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py --- a/pypy/module/_codecs/interp_codecs.py +++ b/pypy/module/_codecs/interp_codecs.py @@ -737,17 +737,10 @@ errors = 'strict' final = space.is_true(w_final) state = space.fromcache(CodecState) - # call the fast version for checking - try: - lgt = rutf8.check_utf8(string, allow_surrogates=False) - except rutf8.CheckError: - res, lgt, pos = unicodehelper.str_decode_utf8(string, - errors, final, state.decode_error_handler) - return space.newtuple([space.newutf8(res, lgt), - space.newint(pos)]) - else: - return space.newtuple([space.newutf8(string, lgt), - space.newint(len(string))]) + res, lgt, pos = unicodehelper.str_decode_utf8(string, + errors, final, state.decode_error_handler) + return space.newtuple([space.newutf8(res, lgt), + space.newint(pos)]) @unwrap_spec(data='bufferstr', errors='text_or_none', byteorder=int, w_final=WrappedDefault(False)) From pypy.commits at gmail.com Wed Oct 9 09:48:21 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 09 Oct 2019 06:48:21 -0700 (PDT) Subject: [pypy-commit] pypy default: support utf8_len_w in the fake object space Message-ID: <5d9de525.1c69fb81.b1c9b.7190@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r97744:a511d86377d6 Date: 2019-10-09 15:35 +0200 http://bitbucket.org/pypy/pypy/changeset/a511d86377d6/ Log: support utf8_len_w in the fake object space 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 @@ -149,6 +149,9 @@ is_root(w_obj) return NonConstant(False) + def utf8_len_w(self, space): + return NonConstant((NonConstant("utf8len_foobar"), NonConstant(14))) + @not_rpython def unwrap(self, w_obj): raise NotImplementedError diff --git a/pypy/objspace/fake/test/test_objspace.py b/pypy/objspace/fake/test/test_objspace.py --- a/pypy/objspace/fake/test/test_objspace.py +++ b/pypy/objspace/fake/test/test_objspace.py @@ -1,4 +1,5 @@ -import py +import pytest +from rpython.rlib.nonconst import NonConstant from pypy.objspace.fake.objspace import FakeObjSpace, W_Root from pypy.interpreter.argument import Arguments from pypy.interpreter.typedef import TypeDef @@ -63,8 +64,8 @@ def test_is_true(self): space = self.space space.translates(lambda: space.is_true(W_Root())) - py.test.raises(AssertionError, - space.translates, lambda: space.is_true(42)) + with pytest.raises(AssertionError): + space.translates(lambda: space.is_true(42)) def test_unpackiterable(self): space = self.space @@ -79,3 +80,23 @@ space = self.space space.translates(lambda: (space.get(W_Root(), W_Root()), space.get(W_Root(), W_Root(), W_Root()))) + + def test_bug_utf8_len_w(self): + space = self.space + + class A(object): + pass + + def f(): + s = NonConstant('a') + w_s = space.newutf8(s, 1) + t, l = space.utf8_len_w(w_s) + a = A() + if l == 1: + a.x = 1 + else: + raise Exception + return a.x + space.translates(f) + + From pypy.commits at gmail.com Wed Oct 9 09:59:40 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 09 Oct 2019 06:59:40 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: speed up utf8-handling of csv module Message-ID: <5d9de7cc.1c69fb81.54617.c374@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r97745:1b9016af40bd Date: 2019-10-09 13:39 +0200 http://bitbucket.org/pypy/pypy/changeset/1b9016af40bd/ Log: speed up utf8-handling of csv module diff --git a/pypy/module/_csv/interp_reader.py b/pypy/module/_csv/interp_reader.py --- a/pypy/module/_csv/interp_reader.py +++ b/pypy/module/_csv/interp_reader.py @@ -1,5 +1,5 @@ from rpython.rlib.rstring import UnicodeBuilder -from rpython.rlib.rutf8 import Utf8StringIterator +from rpython.rlib.rutf8 import Utf8StringIterator, Utf8StringBuilder from rpython.rlib import objectmodel from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError @@ -38,16 +38,15 @@ assert field_builder is not None if field_builder.getlength() >= field_limit.limit: raise self.error(u"field larger than field limit") - field_builder.append(c) + field_builder.append_code(c) def save_field(self, field_builder): space = self.space field = field_builder.build() + w_obj = space.newutf8(field, field_builder.getlength()) if self.numeric_field: self.numeric_field = False - w_obj = space.call_function(space.w_float, space.newtext(field)) - else: - w_obj = space.newtext(field) + w_obj = space.call_function(space.w_float, w_obj) self.fields_w.append(w_obj) def next_w(self): @@ -79,13 +78,11 @@ u"(did you open the file in text mode?") line = space.utf8_w(w_line) for c in Utf8StringIterator(line): - # XXX rewrite this to use c (as int) not unichr(c) - c = unichr(c) - if c == '\0': + if c == 0: raise self.error(u"line contains NULL byte") if state == START_RECORD: - if c == b'\n' or c == b'\r': + if c == ord(u'\n') or c == ord(u'\r'): state = EAT_CRNL continue # normal character - handle as START_FIELD @@ -93,23 +90,23 @@ # fall-through to the next case if state == START_FIELD: - field_builder = UnicodeBuilder(64) + field_builder = Utf8StringBuilder(64) # expecting field - if c == u'\n' or c == u'\r': + if c == ord(u'\n') or c == ord(u'\r'): # save empty field self.save_field(field_builder) state = EAT_CRNL - elif (c == dialect.quotechar and + elif (c == ord(dialect.quotechar) and dialect.quoting != QUOTE_NONE): # start quoted field state = IN_QUOTED_FIELD - elif c == dialect.escapechar: + elif c == ord(dialect.escapechar): # possible escaped character state = ESCAPED_CHAR - elif c == u' ' and dialect.skipinitialspace: + elif c == ord(u' ') and dialect.skipinitialspace: # ignore space at start of field pass - elif c == dialect.delimiter: + elif c == ord(dialect.delimiter): # save empty field self.save_field(field_builder) else: @@ -120,7 +117,7 @@ state = IN_FIELD elif state == ESCAPED_CHAR: - if c in '\n\r': + if c == ord(u'\n') or c == ord(u'\r'): self.add_char(field_builder, c) state = AFTER_ESCAPED_CRNL else: @@ -129,14 +126,14 @@ elif state == IN_FIELD or state == AFTER_ESCAPED_CRNL: # in unquoted field - if c == u'\n' or c == u'\r': + if c == ord(u'\n') or c == ord(u'\r'): # end of line self.save_field(field_builder) state = EAT_CRNL - elif c == dialect.escapechar: + elif c == ord(dialect.escapechar): # possible escaped character state = ESCAPED_CHAR - elif c == dialect.delimiter: + elif c == ord(dialect.delimiter): # save field - wait for new field self.save_field(field_builder) state = START_FIELD @@ -146,10 +143,10 @@ elif state == IN_QUOTED_FIELD: # in quoted field - if c == dialect.escapechar: + if c == ord(dialect.escapechar): # Possible escape character state = ESCAPE_IN_QUOTED_FIELD - elif (c == dialect.quotechar and + elif (c == ord(dialect.quotechar) and dialect.quoting != QUOTE_NONE): if dialect.doublequote: # doublequote; " represented by "" @@ -168,15 +165,15 @@ elif state == QUOTE_IN_QUOTED_FIELD: # doublequote - seen a quote in an quoted field if (dialect.quoting != QUOTE_NONE and - c == dialect.quotechar): + c == ord(dialect.quotechar)): # save "" as " self.add_char(field_builder, c) state = IN_QUOTED_FIELD - elif c == dialect.delimiter: + elif c == ord(dialect.delimiter): # save field - wait for new field self.save_field(field_builder) state = START_FIELD - elif c == u'\n' or c == u'\r': + elif c == ord(u'\n') or c == ord(u'\r'): # end of line self.save_field(field_builder) state = EAT_CRNL @@ -189,7 +186,7 @@ dialect.delimiter, dialect.quotechar)) elif state == EAT_CRNL: - if not (c == u'\n' or c == u'\r'): + if not (c == ord(u'\n') or c == ord(u'\r')): raise self.error(u"new-line character seen in unquoted " u"field - do you need to open the file " u"in universal-newline mode?") @@ -198,16 +195,16 @@ self.save_field(field_builder) break elif state == ESCAPED_CHAR: - self.add_char(field_builder, u'\n') + self.add_char(field_builder, ord(u'\n')) state = IN_FIELD elif state == IN_QUOTED_FIELD: pass elif state == ESCAPE_IN_QUOTED_FIELD: - self.add_char(field_builder, u'\n') + self.add_char(field_builder, ord(u'\n')) state = IN_QUOTED_FIELD elif state == START_FIELD: # save empty field - field_builder = UnicodeBuilder(1) + field_builder = Utf8StringBuilder() self.save_field(field_builder) break elif state == AFTER_ESCAPED_CRNL: From pypy.commits at gmail.com Wed Oct 9 09:59:42 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 09 Oct 2019 06:59:42 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: merge default Message-ID: <5d9de7ce.1c69fb81.e0333.004c@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r97746:60306f724eeb Date: 2019-10-09 15:49 +0200 http://bitbucket.org/pypy/pypy/changeset/60306f724eeb/ Log: merge default diff --git a/pypy/doc/commandline_ref.rst b/pypy/doc/commandline_ref.rst --- a/pypy/doc/commandline_ref.rst +++ b/pypy/doc/commandline_ref.rst @@ -9,3 +9,4 @@ man/pypy.1.rst man/pypy3.1.rst + jit_help.rst diff --git a/pypy/doc/index-of-whatsnew.rst b/pypy/doc/index-of-whatsnew.rst --- a/pypy/doc/index-of-whatsnew.rst +++ b/pypy/doc/index-of-whatsnew.rst @@ -42,7 +42,8 @@ ------------------------------- .. toctree:: - whatsnew-pypy3-head.rst + whatsnew-pypy3-head.rst + whatsnew-pypy3-7.1.0.rst CPython 3.5 compatible versions ------------------------------- @@ -50,6 +51,8 @@ .. toctree:: whatsnew-pypy3-7.0.0.rst + whatsnew-pypy3-6.0.0.rst + whatsnew-pypy3-5.10.0.rst whatsnew-pypy3-5.9.0.rst whatsnew-pypy3-5.8.0.rst whatsnew-pypy3-5.7.0.rst @@ -62,10 +65,4 @@ whatsnew-pypy3-5.5.0.rst whatsnew-pypy3-5.1.1-alpha1.rst -CPython 3.2 compatible versions -------------------------------- -.. toctree:: - - whatsnew-pypy3-2.4.0.rst - whatsnew-pypy3-2.3.1.rst diff --git a/pypy/doc/jit_help.rst b/pypy/doc/jit_help.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/jit_help.rst @@ -0,0 +1,78 @@ +======== +JIT help +======== + +.. note this is from ``pypy --jit help`` + +Advanced JIT options +==================== + +`` --jit`` [*options*] where *options* is a comma-separated list of +``OPTION=VALUE``: + + decay=N + amount to regularly decay counters by (0=none, 1000=max) (default 40) + + disable_unrolling=N + after how many operations we should not unroll (default 200) + + enable_opts=N + INTERNAL USE ONLY (MAY NOT WORK OR LEAD TO CRASHES): optimizations to + enable, or all = + intbounds:rewrite:virtualize:string:pure:earlyforce:heap:unroll (default + all) + + function_threshold=N + number of times a function must run for it to become traced from start + (default 1619) + + inlining=N + inline python functions or not (1/0) (default 1) + + loop_longevity=N + a parameter controlling how long loops will be kept before being freed, + an estimate (default 1000) + + max_retrace_guards=N + number of extra guards a retrace can cause (default 15) + + max_unroll_loops=N + number of extra unrollings a loop can cause (default 0) + + max_unroll_recursion=N + how many levels deep to unroll a recursive function (default 7) + + retrace_limit=N + how many times we can try retracing before giving up (default 0) + + threshold=N + number of times a loop has to run for it to become hot (default 1039) + + trace_eagerness=N + number of times a guard has to fail before we start compiling a bridge + (default 200) + + trace_limit=N + number of recorded operations before we abort tracing with ABORT_TOO_LONG + (default 6000) + + vec=N + turn on the vectorization optimization (vecopt). Supports x86 (SSE 4.1), + powerpc (SVX), s390x SIMD (default 0) + + vec_all=N + try to vectorize trace loops that occur outside of the numpypy library + (default 0) + + vec_cost=N + threshold for which traces to bail. Unpacking increases the counter, + vector operation decrease the cost (default 0) + + off + turn off the JIT + help + print this page + +The :ref:`pypyjit` module can be used to control the JIT from inside +pypy + diff --git a/pypy/doc/release-v7.2.0.rst b/pypy/doc/release-v7.2.0.rst --- a/pypy/doc/release-v7.2.0.rst +++ b/pypy/doc/release-v7.2.0.rst @@ -307,6 +307,7 @@ .. _33786 : https://bugs.python.org/issue33786 .. _32270 : https://bugs.python.org/issue32270 .. _28691 : https://bugs.python.org/issue28691 +.. _33729 : https://bugs.python.org/issue33729 .. _opencv2: https://github.com/skvark/opencv-python/ .. _`issue 2617`: https://bitbucket.com/pypy/pypy/issues/2617 diff --git a/pypy/doc/whatsnew-pypy3-2.3.1.rst b/pypy/doc/whatsnew-pypy3-2.3.1.rst deleted file mode 100644 --- a/pypy/doc/whatsnew-pypy3-2.3.1.rst +++ /dev/null @@ -1,6 +0,0 @@ -========================= -What's new in PyPy3 2.3.1 -========================= - -.. this is a revision shortly after pypy3-release-2.3.x -.. startrev: 0137d8e6657d diff --git a/pypy/doc/whatsnew-pypy3-2.4.0.rst b/pypy/doc/whatsnew-pypy3-2.4.0.rst deleted file mode 100644 --- a/pypy/doc/whatsnew-pypy3-2.4.0.rst +++ /dev/null @@ -1,6 +0,0 @@ -========================= -What's new in PyPy3 2.4.0 -========================= - -.. this is a revision shortly after pypy3-release-2.4.x -.. startrev: 12b940544622 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 @@ -164,6 +164,9 @@ is_root(w_obj) return NonConstant(False) + def utf8_len_w(self, space): + return NonConstant((NonConstant("utf8len_foobar"), NonConstant(14))) + @not_rpython def unwrap(self, w_obj): raise NotImplementedError diff --git a/pypy/objspace/fake/test/test_objspace.py b/pypy/objspace/fake/test/test_objspace.py --- a/pypy/objspace/fake/test/test_objspace.py +++ b/pypy/objspace/fake/test/test_objspace.py @@ -1,4 +1,5 @@ -import py +import pytest +from rpython.rlib.nonconst import NonConstant from pypy.objspace.fake.objspace import FakeObjSpace, W_Root from pypy.interpreter.argument import Arguments from pypy.interpreter.typedef import TypeDef @@ -63,8 +64,8 @@ def test_is_true(self): space = self.space space.translates(lambda: space.is_true(W_Root())) - py.test.raises(AssertionError, - space.translates, lambda: space.is_true(42)) + with pytest.raises(AssertionError): + space.translates(lambda: space.is_true(42)) def test_unpackiterable(self): space = self.space @@ -79,3 +80,23 @@ space = self.space space.translates(lambda: (space.get(W_Root(), W_Root()), space.get(W_Root(), W_Root(), W_Root()))) + + def test_bug_utf8_len_w(self): + space = self.space + + class A(object): + pass + + def f(): + s = NonConstant('a') + w_s = space.newutf8(s, 1) + t, l = space.utf8_len_w(w_s) + a = A() + if l == 1: + a.x = 1 + else: + raise Exception + return a.x + space.translates(f) + + From pypy.commits at gmail.com Wed Oct 9 09:59:44 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 09 Oct 2019 06:59:44 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: remove this hack, now that the fake objspace supports utf8_len_w Message-ID: <5d9de7d0.1c69fb81.1f155.bad2@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r97747:ac3b88caa03e Date: 2019-10-09 15:57 +0200 http://bitbucket.org/pypy/pypy/changeset/ac3b88caa03e/ Log: remove this hack, now that the fake objspace supports utf8_len_w diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -1787,8 +1787,7 @@ def convert_arg_to_w_unicode(self, w_obj, strict=None): # XXX why convert_to_w_unicode does something slightly different? from pypy.objspace.std.unicodeobject import W_UnicodeObject - # for z_translation tests - if hasattr(self, 'is_fake_objspace'): return self.newtext("foobar") + assert not hasattr(self, 'is_fake_objspace') return W_UnicodeObject.convert_arg_to_w_unicode(self, w_obj, strict) def utf8_len_w(self, w_obj): From pypy.commits at gmail.com Wed Oct 9 09:59:45 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 09 Oct 2019 06:59:45 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: small improvement: use newlist with a sizehint that is the length of the previous line Message-ID: <5d9de7d1.1c69fb81.1d990.83c7@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r97748:8b92f9a32d07 Date: 2019-10-09 15:58 +0200 http://bitbucket.org/pypy/pypy/changeset/8b92f9a32d07/ Log: small improvement: use newlist with a sizehint that is the length of the previous line diff --git a/pypy/module/_csv/interp_reader.py b/pypy/module/_csv/interp_reader.py --- a/pypy/module/_csv/interp_reader.py +++ b/pypy/module/_csv/interp_reader.py @@ -1,4 +1,3 @@ -from rpython.rlib.rstring import UnicodeBuilder from rpython.rlib.rutf8 import Utf8StringIterator, Utf8StringBuilder from rpython.rlib import objectmodel from pypy.interpreter.baseobjspace import W_Root @@ -22,6 +21,7 @@ self.dialect = dialect self.w_iter = w_iter self.line_num = 0 + self.sizehint = 1 # just used for first line def iter_w(self): return self @@ -52,7 +52,7 @@ def next_w(self): space = self.space dialect = self.dialect - self.fields_w = [] + self.fields_w = objectmodel.newlist_hint(self.sizehint) self.numeric_field = False field_builder = None # valid iff state not in [START_RECORD, EAT_CRNL] state = START_RECORD @@ -213,6 +213,8 @@ break # w_result = space.newlist(self.fields_w) + # assume all lines have the same number of fields + self.sizehint = len(self.fields_w) self.fields_w = None return w_result From pypy.commits at gmail.com Wed Oct 9 09:59:47 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 09 Oct 2019 06:59:47 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: switch to Utf8StringBuilder for the csv writer as well Message-ID: <5d9de7d3.1c69fb81.cbfc5.ca7d@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r97749:6d2f8470165b Date: 2019-10-09 15:58 +0200 http://bitbucket.org/pypy/pypy/changeset/6d2f8470165b/ Log: switch to Utf8StringBuilder for the csv writer as well diff --git a/pypy/module/_csv/interp_csv.py b/pypy/module/_csv/interp_csv.py --- a/pypy/module/_csv/interp_csv.py +++ b/pypy/module/_csv/interp_csv.py @@ -1,3 +1,4 @@ +from rpython.rlib import rutf8 from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.typedef import TypeDef, interp_attrproperty @@ -47,24 +48,26 @@ if w_src is None: return default try: - return space.realunicode_w(w_src) + return space.text_w(w_src) except OperationError as e: if e.match(space, space.w_TypeError): raise oefmt(space.w_TypeError, '"%s" must be a string', attrname) raise -def _get_char(space, w_src, default, name): +def _get_codepoint(space, w_src, default, name): if w_src is None: return default if space.is_w(w_src, space.w_None): - return u'\0' + return 0 if not space.isinstance_w(w_src, space.w_unicode): raise oefmt(space.w_TypeError, '"%s" must be string, not %T', name, w_src) - src = space.realunicode_w(w_src) - if len(src) == 1: - return src[0] + src, length = space.utf8_len_w(w_src) + if length == 1: + res = rutf8.codepoint_at_pos(src, 0) + assert res >= 0 + return res if len(src) == 0: - return u'\0' + return 0 raise oefmt(space.w_TypeError, '"%s" must be a 1-character string', name) def _build_dialect(space, w_dialect, w_delimiter, w_doublequote, @@ -104,11 +107,11 @@ w_strict = _fetch(space, w_dialect, 'strict') dialect = W_Dialect() - dialect.delimiter = _get_char(space, w_delimiter, u',', 'delimiter') + dialect.delimiter = _get_codepoint(space, w_delimiter, ord(u','), 'delimiter') dialect.doublequote = _get_bool(space, w_doublequote, True) - dialect.escapechar = _get_char(space, w_escapechar, u'\0', 'escapechar') - dialect.lineterminator = _get_str(space, w_lineterminator, u'\r\n', 'lineterminator') - dialect.quotechar = _get_char(space, w_quotechar, u'"', 'quotechar') + dialect.escapechar = _get_codepoint(space, w_escapechar, ord(u'\0'), 'escapechar') + dialect.lineterminator = _get_str(space, w_lineterminator, '\r\n', 'lineterminator') + dialect.quotechar = _get_codepoint(space, w_quotechar, ord(u'"'), 'quotechar') tmp_quoting = _get_int(space, w_quoting, QUOTE_MINIMAL, 'quoting') dialect.skipinitialspace = _get_bool(space, w_skipinitialspace, False) dialect.strict = _get_bool(space, w_strict, False) @@ -117,13 +120,13 @@ if not (0 <= tmp_quoting < 4): raise oefmt(space.w_TypeError, 'bad "quoting" value') - if dialect.delimiter == u'\0': + if dialect.delimiter == 0: raise oefmt(space.w_TypeError, '"delimiter" must be a 1-character string') if space.is_w(w_quotechar, space.w_None) and w_quoting is None: tmp_quoting = QUOTE_NONE - if tmp_quoting != QUOTE_NONE and dialect.quotechar == u'\0': + if tmp_quoting != QUOTE_NONE and dialect.quotechar == 0: raise oefmt(space.w_TypeError, "quotechar must be set if quoting enabled") dialect.quoting = tmp_quoting @@ -158,14 +161,20 @@ def _get_escapechar(space, dialect): - if dialect.escapechar == u'\0': + if dialect.escapechar == 0: return space.w_None - return space.newtext(dialect.escapechar) + s = rutf8.unichr_as_utf8(dialect.escapechar) + return space.newutf8(s, 1) def _get_quotechar(space, dialect): - if dialect.quotechar == u'\0': + if dialect.quotechar == 0: return space.w_None - return space.newtext(dialect.quotechar) + s = rutf8.unichr_as_utf8(dialect.quotechar) + return space.newutf8(s, 1) + +def _get_delimiter(space, dialect): + s = rutf8.unichr_as_utf8(dialect.delimiter) + return space.newutf8(s, 1) W_Dialect.typedef = TypeDef( @@ -173,8 +182,7 @@ __new__ = interp2app(W_Dialect___new__), __reduce_ex__ = interp2app(W_Dialect.reduce_ex_w), - delimiter = interp_attrproperty('delimiter', W_Dialect, - wrapfn='newtext'), + delimiter = GetSetProperty(_get_delimiter, cls=W_Dialect), doublequote = interp_attrproperty('doublequote', W_Dialect, wrapfn='newbool'), escapechar = GetSetProperty(_get_escapechar, cls=W_Dialect), diff --git a/pypy/module/_csv/interp_reader.py b/pypy/module/_csv/interp_reader.py --- a/pypy/module/_csv/interp_reader.py +++ b/pypy/module/_csv/interp_reader.py @@ -96,17 +96,17 @@ # save empty field self.save_field(field_builder) state = EAT_CRNL - elif (c == ord(dialect.quotechar) and + elif (c == dialect.quotechar and dialect.quoting != QUOTE_NONE): # start quoted field state = IN_QUOTED_FIELD - elif c == ord(dialect.escapechar): + elif c == dialect.escapechar: # possible escaped character state = ESCAPED_CHAR elif c == ord(u' ') and dialect.skipinitialspace: # ignore space at start of field pass - elif c == ord(dialect.delimiter): + elif c == dialect.delimiter: # save empty field self.save_field(field_builder) else: @@ -130,10 +130,10 @@ # end of line self.save_field(field_builder) state = EAT_CRNL - elif c == ord(dialect.escapechar): + elif c == dialect.escapechar: # possible escaped character state = ESCAPED_CHAR - elif c == ord(dialect.delimiter): + elif c == dialect.delimiter: # save field - wait for new field self.save_field(field_builder) state = START_FIELD @@ -143,10 +143,10 @@ elif state == IN_QUOTED_FIELD: # in quoted field - if c == ord(dialect.escapechar): + if c == dialect.escapechar: # Possible escape character state = ESCAPE_IN_QUOTED_FIELD - elif (c == ord(dialect.quotechar) and + elif (c == dialect.quotechar and dialect.quoting != QUOTE_NONE): if dialect.doublequote: # doublequote; " represented by "" @@ -165,11 +165,11 @@ elif state == QUOTE_IN_QUOTED_FIELD: # doublequote - seen a quote in an quoted field if (dialect.quoting != QUOTE_NONE and - c == ord(dialect.quotechar)): + c == dialect.quotechar): # save "" as " self.add_char(field_builder, c) state = IN_QUOTED_FIELD - elif c == ord(dialect.delimiter): + elif c == dialect.delimiter: # save field - wait for new field self.save_field(field_builder) state = START_FIELD @@ -183,7 +183,7 @@ else: # illegal raise self.error(u"'%s' expected after '%s'" % ( - dialect.delimiter, dialect.quotechar)) + unichr(dialect.delimiter), unichr(dialect.quotechar))) elif state == EAT_CRNL: if not (c == ord(u'\n') or c == ord(u'\r')): diff --git a/pypy/module/_csv/interp_writer.py b/pypy/module/_csv/interp_writer.py --- a/pypy/module/_csv/interp_writer.py +++ b/pypy/module/_csv/interp_writer.py @@ -1,4 +1,4 @@ -from rpython.rlib.rstring import UnicodeBuilder +from rpython.rlib.rutf8 import Utf8StringIterator, Utf8StringBuilder from rpython.rlib import objectmodel from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError @@ -15,11 +15,13 @@ self.dialect = dialect self.w_filewrite = space.getattr(w_fileobj, space.newtext('write')) # precompute this - special = dialect.delimiter + dialect.lineterminator - if dialect.escapechar != '\0': - special += dialect.escapechar - if dialect.quotechar != '\0': - special += dialect.quotechar + special = [dialect.delimiter] + for c in Utf8StringIterator(dialect.lineterminator): + special.append(c) + if dialect.escapechar != 0: + special.append(dialect.escapechar) + if dialect.quotechar != 0: + special.append(dialect.quotechar) self.special_characters = special @objectmodel.dont_inline @@ -35,16 +37,17 @@ space = self.space fields_w = space.listview(w_fields) dialect = self.dialect - rec = UnicodeBuilder(80) + rec = Utf8StringBuilder(80) # for field_index in range(len(fields_w)): w_field = fields_w[field_index] if space.is_w(w_field, space.w_None): - field = u"" + field = "" + length = 0 elif space.isinstance_w(w_field, space.w_float): - field = space.realunicode_w(space.repr(w_field)) + field, length = space.utf8_len_w(space.repr(w_field)) else: - field = space.realunicode_w(space.str(w_field)) + field, length = space.utf8_len_w(space.str(w_field)) # if dialect.quoting == QUOTE_NONNUMERIC: try: @@ -57,9 +60,9 @@ elif dialect.quoting == QUOTE_ALL: quoted = True elif dialect.quoting == QUOTE_MINIMAL: - # Find out if we really quoting + # Find out if we really need quoting. special_characters = self.special_characters - for c in field: + for c in Utf8StringIterator(field): if c in special_characters: if c != dialect.quotechar or dialect.doublequote: quoted = True @@ -78,15 +81,15 @@ # If this is not the first field we need a field separator if field_index > 0: - rec.append(dialect.delimiter) + rec.append_code(dialect.delimiter) # Handle preceding quote if quoted: - rec.append(dialect.quotechar) + rec.append_code(dialect.quotechar) # Copy field data special_characters = self.special_characters - for c in field: + for c in Utf8StringIterator(field): if c in special_characters: if dialect.quoting == QUOTE_NONE: want_escape = True @@ -94,28 +97,28 @@ want_escape = False if c == dialect.quotechar: if dialect.doublequote: - rec.append(dialect.quotechar) + rec.append_code(dialect.quotechar) else: want_escape = True if want_escape: - if dialect.escapechar == u'\0': + if dialect.escapechar == 0: raise self.error("need to escape, " "but no escapechar set") - rec.append(dialect.escapechar) + rec.append_code(dialect.escapechar) else: assert quoted # Copy field character into record buffer - rec.append(c) + rec.append_code(c) # Handle final quote if quoted: - rec.append(dialect.quotechar) + rec.append_code(dialect.quotechar) # Add line terminator rec.append(dialect.lineterminator) line = rec.build() - return space.call_function(self.w_filewrite, space.newtext(line)) + return space.call_function(self.w_filewrite, space.newutf8(line, rec.getlength())) def writerows(self, w_seqseq): """Construct and write a series of sequences to a csv file. From pypy.commits at gmail.com Wed Oct 9 11:01:48 2019 From: pypy.commits at gmail.com (arigo) Date: Wed, 09 Oct 2019 08:01:48 -0700 (PDT) Subject: [pypy-commit] pypy.org extradoc: Update link to the MS page for vcruntime140.dll Message-ID: <5d9df65c.1c69fb81.4d22c.2657@mx.google.com> Author: Armin Rigo Branch: extradoc Changeset: r953:b97894caf276 Date: 2019-10-09 16:58 +0200 http://bitbucket.org/pypy/pypy.org/changeset/b97894caf276/ Log: Update link to the MS page for vcruntime140.dll diff --git a/download.html b/download.html --- a/download.html +++ b/download.html @@ -115,8 +115,8 @@
  • Linux x86-64 binary (64bit, built on Ubuntu 14.04) (see [1] below)
  • Mac OS X binary (64bit)
  • FreeBSD x86 and x86_64: see FreshPorts
  • -
  • Windows binary (32bit) (you might need the VS 2008 runtime library -installer vcredist_x86.exe.)
  • +
  • Windows binary (32bit) (you might need the VC runtime library +installer vcredist.x86.exe.)
  • PowerPC PPC64 Linux binary (64bit big-endian, Fedora 20) (see [1] below)
  • PowerPC PPC64le Linux binary (64bit little-endian, Fedora 21) (see [1] below)
  • s390x Linux binary (built on Redhat Linux 7.2) (see [1] below)
  • @@ -131,7 +131,8 @@
  • Linux x86-64 binary (64bit, built on Ubuntu 14.04) (see [1] below)
  • Linux x86 binary (32bit, built on Ubuntu 14.04) (see [1] below)
  • Mac OS X binary (64bit) (High Sierra >= 10.13, not for Sierra and below)
  • -
  • Windows binary (32bit)
  • +
  • Windows binary (32bit) (you might need the VC runtime library +installer vcredist.x86.exe.)
  • PowerPC PPC64 Linux binary (64bit big-endian, Fedora 20) (see [1] below)
  • PowerPC PPC64le Linux binary (64bit little-endian, Fedora 21) (see [1] below)
  • s390x Linux binary (built on Redhat Linux 7.2) (see [1] below)
  • @@ -146,7 +147,8 @@
  • Linux x86 binary (32bit, built on Ubuntu 14.04) (see [1] below)
  • Linux x86-64 binary (64bit, built on Ubuntu 14.04) (see [1] below)
  • Mac OS X binary (64bit) (High Sierra >= 10.13, not for Sierra and below)
  • -
  • Windows binary (32bit) BETA
  • +
  • Windows binary (32bit) BETA (you might need the VC runtime library +installer vcredist.x86.exe.)
  • PowerPC PPC64 Linux binary (64bit big-endian, Fedora 20) (see [1] below)
  • PowerPC PPC64le Linux binary (64bit little-endian, Fedora 21) (see [1] below)
  • s390x Linux binary (built on Redhat Linux 7.2) (see [1] below)
  • diff --git a/source/download.txt b/source/download.txt --- a/source/download.txt +++ b/source/download.txt @@ -88,8 +88,8 @@ * `Linux x86-64 binary (64bit, built on Ubuntu 14.04)`__ (see ``[1]`` below) * `Mac OS X binary (64bit)`__ * FreeBSD x86 and x86_64: see FreshPorts_ -* `Windows binary (32bit)`__ (you might need the VS 2008 runtime library - installer `vcredist_x86.exe`_.) +* `Windows binary (32bit)`__ (you might need the VC runtime library + installer `vcredist.x86.exe`_.) * `PowerPC PPC64 Linux binary (64bit big-endian, Fedora 20)`__ (see ``[1]`` below) * `PowerPC PPC64le Linux binary (64bit little-endian, Fedora 21)`__ (see ``[1]`` below) * `s390x Linux binary (built on Redhat Linux 7.2)`__ (see ``[1]`` below) @@ -106,7 +106,7 @@ .. __: https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.1.1-s390x.tar.bz2 .. __: https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.1.1-src.tar.bz2 .. __: https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.1.1-src.zip -.. _`vcredist_x86.exe`: http://www.microsoft.com/en-us/download/details.aspx?id=5582 +.. _`vcredist.x86.exe`: https://www.microsoft.com/en-us/download/details.aspx?id=52685 .. __: https://bitbucket.org/pypy/pypy/downloads .. _mirror: http://buildbot.pypy.org/mirror/ .. _FreshPorts: http://www.freshports.org/lang/pypy @@ -120,7 +120,8 @@ * `Linux x86-64 binary (64bit, built on Ubuntu 14.04)`__ (see ``[1]`` below) * `Linux x86 binary (32bit, built on Ubuntu 14.04)`__ (see ``[1]`` below) * `Mac OS X binary (64bit)`__ (High Sierra >= 10.13, not for Sierra and below) -* `Windows binary (32bit)`__ +* `Windows binary (32bit)`__ (you might need the VC runtime library + installer `vcredist.x86.exe`_.) * `PowerPC PPC64 Linux binary (64bit big-endian, Fedora 20)`__ (see ``[1]`` below) * `PowerPC PPC64le Linux binary (64bit little-endian, Fedora 21)`__ (see ``[1]`` below) * `s390x Linux binary (built on Redhat Linux 7.2)`__ (see ``[1]`` below) @@ -148,7 +149,8 @@ * `Linux x86 binary (32bit, built on Ubuntu 14.04)`__ (see ``[1]`` below) * `Linux x86-64 binary (64bit, built on Ubuntu 14.04)`__ (see ``[1]`` below) * `Mac OS X binary (64bit)`__ (High Sierra >= 10.13, not for Sierra and below) -* `Windows binary (32bit)`__ **BETA** +* `Windows binary (32bit)`__ **BETA** (you might need the VC runtime library + installer `vcredist.x86.exe`_.) * `PowerPC PPC64 Linux binary (64bit big-endian, Fedora 20)`__ (see ``[1]`` below) * `PowerPC PPC64le Linux binary (64bit little-endian, Fedora 21)`__ (see ``[1]`` below) * `s390x Linux binary (built on Redhat Linux 7.2)`__ (see ``[1]`` below) From pypy.commits at gmail.com Wed Oct 9 15:12:11 2019 From: pypy.commits at gmail.com (rlamy) Date: Wed, 09 Oct 2019 12:12:11 -0700 (PDT) Subject: [pypy-commit] pypy release-pypy3.6-v7.x: Add _PyDict_GetItemWithError (part of the public API in py3) Message-ID: <5d9e310b.1c69fb81.c4ad1.093a@mx.google.com> Author: Ronan Lamy Branch: release-pypy3.6-v7.x Changeset: r97750:c162c307e6dd Date: 2019-10-04 18:02 +0100 http://bitbucket.org/pypy/pypy/changeset/c162c307e6dd/ Log: Add _PyDict_GetItemWithError (part of the public API in py3) (grafted from d663ce56919c62a2bf5e242ee79b8bc3c640662a) diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -80,6 +80,13 @@ # XXX this is wrong with IntMutableCell. Hope it works... return w_dict.getitem(w_key) + at cpython_api([PyObject, PyObject], PyObject, result_borrowed=True) +def _PyDict_GetItemWithError(space, w_dict, w_key): + # Like PyDict_GetItem(), but doesn't swallow the error + if not isinstance(w_dict, W_DictMultiObject): + PyErr_BadInternalCall(space) + return w_dict.getitem(w_key) + @cpython_api([PyObject, PyObject, PyObject], rffi.INT_real, error=-1) def PyDict_SetItem(space, w_dict, w_key, w_obj): if not isinstance(w_dict, W_DictMultiObject): 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 @@ -175,6 +175,26 @@ ]) assert module.dict_proxy({'a': 1, 'b': 2}) == 2 + def test_getitemwitherror(self): + module = self.import_extension('foo', [ + ("dict_getitem", "METH_VARARGS", + """ + PyObject *d, *key, *result; + if (!PyArg_ParseTuple(args, "OO", &d, &key)) { + return NULL; + } + result = _PyDict_GetItemWithError(d, key); + if (result == NULL && !PyErr_Occurred()) + Py_RETURN_NONE; + Py_XINCREF(result); + return result; + """)]) + d = {'foo': 'bar'} + assert module.dict_getitem(d, 'foo') == 'bar' + assert module.dict_getitem(d, 'missing') is None + with raises(TypeError): + module.dict_getitem(d, []) + def test_setdefault(self): module = self.import_extension('foo', [ ("setdefault", "METH_VARARGS", From pypy.commits at gmail.com Wed Oct 9 15:12:13 2019 From: pypy.commits at gmail.com (rlamy) Date: Wed, 09 Oct 2019 12:12:13 -0700 (PDT) Subject: [pypy-commit] pypy release-pypy3.6-v7.x: Rename to PyDict_GetItemWithError (no leading underscore) Message-ID: <5d9e310d.1c69fb81.ba216.2279@mx.google.com> Author: Ronan Lamy Branch: release-pypy3.6-v7.x Changeset: r97751:5da45ced70e5 Date: 2019-10-04 18:29 +0100 http://bitbucket.org/pypy/pypy/changeset/5da45ced70e5/ Log: Rename to PyDict_GetItemWithError (no leading underscore) (grafted from 6e344fc026c3997723683b2ae7ed953fdf84c387) diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -81,8 +81,11 @@ return w_dict.getitem(w_key) @cpython_api([PyObject, PyObject], PyObject, result_borrowed=True) -def _PyDict_GetItemWithError(space, w_dict, w_key): - # Like PyDict_GetItem(), but doesn't swallow the error +def PyDict_GetItemWithError(space, w_dict, w_key): + """Variant of PyDict_GetItem() that does not suppress + exceptions. Return NULL with an exception set if an exception + occurred. Return NULL without an exception set if the key + wasn't present.""" if not isinstance(w_dict, W_DictMultiObject): PyErr_BadInternalCall(space) return w_dict.getitem(w_key) diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py --- a/pypy/module/cpyext/stubs.py +++ b/pypy/module/cpyext/stubs.py @@ -243,14 +243,6 @@ def PyWrapper_New(space, w_d, w_self): raise NotImplementedError - at cpython_api([PyObject, PyObject], PyObject) -def PyDict_GetItemWithError(space, p, key): - """Variant of PyDict_GetItem() that does not suppress - exceptions. Return NULL with an exception set if an exception - occurred. Return NULL without an exception set if the key - wasn't present.""" - raise NotImplementedError - @cpython_api([PyObject, PyObject, rffi.INT_real], rffi.INT_real, error=-1) def PyDict_MergeFromSeq2(space, a, seq2, override): """Update or merge into dictionary a, from the key-value pairs in seq2. 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 @@ -183,7 +183,7 @@ if (!PyArg_ParseTuple(args, "OO", &d, &key)) { return NULL; } - result = _PyDict_GetItemWithError(d, key); + result = PyDict_GetItemWithError(d, key); if (result == NULL && !PyErr_Occurred()) Py_RETURN_NONE; Py_XINCREF(result); From pypy.commits at gmail.com Wed Oct 9 15:12:15 2019 From: pypy.commits at gmail.com (rlamy) Date: Wed, 09 Oct 2019 12:12:15 -0700 (PDT) Subject: [pypy-commit] pypy release-pypy2.7-v7.x: Add _PyDict_GetItemWithError (part of the public API in py3) Message-ID: <5d9e310f.1c69fb81.ae168.9368@mx.google.com> Author: Ronan Lamy Branch: release-pypy2.7-v7.x Changeset: r97752:4a68d8d3d2fc Date: 2019-10-04 18:02 +0100 http://bitbucket.org/pypy/pypy/changeset/4a68d8d3d2fc/ Log: Add _PyDict_GetItemWithError (part of the public API in py3) (grafted from d663ce56919c62a2bf5e242ee79b8bc3c640662a) diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -80,6 +80,13 @@ # XXX this is wrong with IntMutableCell. Hope it works... return w_dict.getitem(w_key) + at cpython_api([PyObject, PyObject], PyObject, result_borrowed=True) +def _PyDict_GetItemWithError(space, w_dict, w_key): + # Like PyDict_GetItem(), but doesn't swallow the error + if not isinstance(w_dict, W_DictMultiObject): + PyErr_BadInternalCall(space) + return w_dict.getitem(w_key) + @cpython_api([PyObject, PyObject, PyObject], rffi.INT_real, error=-1) def PyDict_SetItem(space, w_dict, w_key, w_obj): if not isinstance(w_dict, W_DictMultiObject): 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 @@ -173,6 +173,26 @@ ]) assert module.dict_proxy({'a': 1, 'b': 2}) == 2 + def test_getitemwitherror(self): + module = self.import_extension('foo', [ + ("dict_getitem", "METH_VARARGS", + """ + PyObject *d, *key, *result; + if (!PyArg_ParseTuple(args, "OO", &d, &key)) { + return NULL; + } + result = _PyDict_GetItemWithError(d, key); + if (result == NULL && !PyErr_Occurred()) + Py_RETURN_NONE; + Py_XINCREF(result); + return result; + """)]) + d = {'foo': 'bar'} + assert module.dict_getitem(d, 'foo') == 'bar' + assert module.dict_getitem(d, 'missing') is None + with raises(TypeError): + module.dict_getitem(d, []) + def test_update(self): module = self.import_extension('foo', [ ("update", "METH_VARARGS", From pypy.commits at gmail.com Wed Oct 9 15:12:17 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 09 Oct 2019 12:12:17 -0700 (PDT) Subject: [pypy-commit] pypy default: update release note Message-ID: <5d9e3111.1c69fb81.ceec.040f@mx.google.com> Author: Matti Picus Branch: Changeset: r97753:46cfab6a4a1e Date: 2019-10-09 22:11 +0300 http://bitbucket.org/pypy/pypy/changeset/46cfab6a4a1e/ Log: update release note diff --git a/pypy/doc/release-v7.2.0.rst b/pypy/doc/release-v7.2.0.rst --- a/pypy/doc/release-v7.2.0.rst +++ b/pypy/doc/release-v7.2.0.rst @@ -216,6 +216,7 @@ * Add more constants to `sysconfig``. Set ``MACOSX_DEPLOYMENT_TARGET`` for darwin (`issue 2994`_) * fix ``CBuffer.buffer_attach`` +* Add ``_PyDict_GetItemWithError`` (``PyDict_GetItemWithError`` on Python3) Python 3.6 only --------------- From pypy.commits at gmail.com Thu Oct 10 09:44:28 2019 From: pypy.commits at gmail.com (cfbolz) Date: Thu, 10 Oct 2019 06:44:28 -0700 (PDT) Subject: [pypy-commit] pypy default: debug-print quasi-immutable invalidations Message-ID: <5d9f35bc.1c69fb81.ad419.7932@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r97754:5dd2d3cca75b Date: 2019-10-10 15:43 +0200 http://bitbucket.org/pypy/pypy/changeset/5dd2d3cca75b/ Log: debug-print quasi-immutable invalidations (been wanting that since a while) diff --git a/rpython/jit/metainterp/quasiimmut.py b/rpython/jit/metainterp/quasiimmut.py --- a/rpython/jit/metainterp/quasiimmut.py +++ b/rpython/jit/metainterp/quasiimmut.py @@ -5,6 +5,7 @@ from rpython.jit.metainterp.history import ( AbstractDescr, ConstPtr, ConstInt, ConstFloat) from rpython.rlib.objectmodel import we_are_translated +from rpython.rlib.debug import ll_assert, debug_print, debug_start, debug_stop def get_mutate_field_name(fieldname): @@ -31,7 +32,7 @@ qmut_ptr = getattr(p, mutatefieldname) setattr(p, mutatefieldname, lltype.nullptr(rclass.OBJECT)) qmut = cast_base_ptr_to_instance(QuasiImmut, qmut_ptr) - qmut.invalidate() + qmut.invalidate(mutatefieldname) _invalidate_now._dont_inline_ = True # def invalidation(p): @@ -45,7 +46,7 @@ if qmut_ref: cpu.bh_setfield_gc_r(p, ConstPtr.value, mutatefielddescr) qmut = cast_gcref_to_instance(QuasiImmut, qmut_ref) - qmut.invalidate() + qmut.invalidate(mutatefielddescr.fieldname) class QuasiImmut(object): @@ -78,7 +79,8 @@ # already invalidated; see below self.compress_limit = (len(self.looptokens_wrefs) + 15) * 2 - def invalidate(self): + def invalidate(self, fieldname=None): + debug_start("jit-invalidate-quasi-immutable") # When this is called, all the loops that we record become # invalid: all GUARD_NOT_INVALIDATED in these loops (and # in attached bridges) must now fail. @@ -87,9 +89,11 @@ return wrefs = self.looptokens_wrefs self.looptokens_wrefs = [] + invalidated = 0 for wref in wrefs: looptoken = wref() if looptoken is not None: + invalidated += 1 looptoken.invalidated = True self.cpu.invalidate_loop(looptoken) # NB. we must call cpu.invalidate_loop() even if @@ -100,6 +104,8 @@ if not we_are_translated(): self.cpu.stats.invalidated_token_numbers.add( looptoken.number) + debug_print("fieldname", fieldname or "", "invalidated", invalidated) + debug_stop("jit-invalidate-quasi-immutable") class QuasiImmutDescr(AbstractDescr): From pypy.commits at gmail.com Thu Oct 10 12:10:32 2019 From: pypy.commits at gmail.com (mattip) Date: Thu, 10 Oct 2019 09:10:32 -0700 (PDT) Subject: [pypy-commit] pypy default: Added tags for v7.2.0rc2 Message-ID: <5d9f57f8.1c69fb81.15246.ac70@mx.google.com> Author: Matti Picus Branch: Changeset: r97755:e1741b330c9d Date: 2019-10-10 18:53 +0300 http://bitbucket.org/pypy/pypy/changeset/e1741b330c9d/ Log: Added tags for v7.2.0rc2 diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -53,3 +53,5 @@ 85dae4fd5c234b482feff834c73e089872194541 release-pypy2.7-v7.2.0rc0 7ffb92269488f37c707ce66076f50ffd8613f8e2 release-pypy3.6-v7.2.0rc0 4d6761df14ffd6f38450f183ac1fad32c946c21b release-pypy3.6-v7.2.0rc1 +5da45ced70e515f94686be0df47c59abd1348ebc release-pypy3.6-v7.2.0rc2 +4a68d8d3d2fc1faec2e83bcb4d28559099092574 release-pypy2.7-v7.2.0rc2 From pypy.commits at gmail.com Thu Oct 10 14:12:46 2019 From: pypy.commits at gmail.com (stevie_92) Date: Thu, 10 Oct 2019 11:12:46 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-gc-cycle: Check state during cpyext rrc gc processing (incorrect state after garbage handling) Message-ID: <5d9f749e.1c69fb81.f6731.207d@mx.google.com> Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r97756:30c564b06dd8 Date: 2019-10-10 20:12 +0200 http://bitbucket.org/pypy/pypy/changeset/30c564b06dd8/ Log: Check state during cpyext rrc gc processing (incorrect state after garbage handling) Handle non-gc objects in o_list correctly Fixed handling of gc_refs in rrc incmark Fixed issue with pypy_link of freed objects in p_list in rrc incmark diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py --- a/pypy/module/cpyext/state.py +++ b/pypy/module/cpyext/state.py @@ -321,61 +321,63 @@ finalize, from_ref, cts) from pypy.module.cpyext.api import generic_cpy_call - start = time.time() - while True: - py_obj = rawrefcount.next_dead(PyObject) - if not py_obj: - break - decref(space, py_obj) + if rawrefcount.check_state(): + start = time.time() - while True: - py_obj = rawrefcount.next_cyclic_isolate(PyObject) - if not py_obj: - break - finalize(space, py_obj) + while True: + py_obj = rawrefcount.next_dead(PyObject) + if not py_obj: + break + decref(space, py_obj) - while True: - py_obj = rawrefcount.cyclic_garbage_head(PyObject) - if not py_obj: - break - pyobj = rffi.cast(PyObject, py_obj) - adr_int = llmemory.cast_adr_to_int(llmemory.cast_ptr_to_adr(pyobj)) - pto = pyobj.c_ob_type - if pto.c_tp_clear: - incref(space, py_obj) - #if pto and pto.c_tp_name: - # tp_name = pto.c_tp_name - # name = rffi.charp2str(cts.cast('char*', tp_name)) - # debug_print("tp_clear", pyobj, ": type", pto, - # ": name", name) - generic_cpy_call(space, pto.c_tp_clear, pyobj) - decref(space, py_obj) - head = rawrefcount.cyclic_garbage_head(PyObject) - if adr_int == llmemory.cast_adr_to_int(llmemory.cast_ptr_to_adr(head)): - rawrefcount.cyclic_garbage_remove() + while True: + py_obj = rawrefcount.next_cyclic_isolate(PyObject) + if not py_obj: + break + finalize(space, py_obj) - rawrefcount.begin_garbage() - w_list = space.newlist([]) - while True: - w_obj = rawrefcount.next_garbage_pypy(W_Root) - if not py_obj: - break - w_list.append(w_obj) - while True: - w_pyobj = rawrefcount.next_garbage_pyobj(PyObject) - if not w_pyobj: - break - w_obj = from_ref(space, w_pyobj) - w_list.append(w_obj) - space.setattr(space.builtin_modules['gc'], space.newtext('garbage'), - w_list) - rawrefcount.end_garbage() + while True: + py_obj = rawrefcount.cyclic_garbage_head(PyObject) + if not py_obj: + break + pyobj = rffi.cast(PyObject, py_obj) + adr_int = llmemory.cast_adr_to_int(llmemory.cast_ptr_to_adr(pyobj)) + pto = pyobj.c_ob_type + if pto.c_tp_clear: + incref(space, py_obj) + #if pto and pto.c_tp_name: + # tp_name = pto.c_tp_name + # name = rffi.charp2str(cts.cast('char*', tp_name)) + # debug_print("tp_clear", pyobj, ": type", pto, + # ": name", name) + generic_cpy_call(space, pto.c_tp_clear, pyobj) + decref(space, py_obj) + head = rawrefcount.cyclic_garbage_head(PyObject) + if adr_int == llmemory.cast_adr_to_int(llmemory.cast_ptr_to_adr(head)): + rawrefcount.cyclic_garbage_remove() - duration = time.time() - start - module = space.builtin_modules['gc'] - durations = space.getattr(module, space.newtext('cpyext_durations')) - durations.append(space.newfloat(duration)) + rawrefcount.begin_garbage() + w_list = space.newlist([]) + while True: + w_obj = rawrefcount.next_garbage_pypy(W_Root) + if not py_obj: + break + w_list.append(w_obj) + while True: + w_pyobj = rawrefcount.next_garbage_pyobj(PyObject) + if not w_pyobj: + break + w_obj = from_ref(space, w_pyobj) + w_list.append(w_obj) + space.setattr(space.builtin_modules['gc'], space.newtext('garbage'), + w_list) + rawrefcount.end_garbage() + + duration = time.time() - start + module = space.builtin_modules['gc'] + durations = space.getattr(module, space.newtext('cpyext_durations')) + durations.append(space.newfloat(duration)) class PyObjDeallocAction(executioncontext.AsyncAction): """An action that invokes _Py_Dealloc() on the dying PyObjects. 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 @@ -71,7 +71,7 @@ from rpython.memory.support import mangle_hash from rpython.rlib.rarithmetic import ovfcheck, LONG_BIT, intmask, r_uint from rpython.rlib.rarithmetic import LONG_BIT_SHIFT -from rpython.rlib.debug import ll_assert, debug_print, debug_start, debug_stop +from rpython.rlib.debug import ll_assert, debug_print, debug_start, debug_stop, debug_flush from rpython.rlib.objectmodel import specialize from rpython.rlib import rgc from rpython.memory.gc.minimarkpage import out_of_memory @@ -1292,6 +1292,7 @@ def debug_check_consistency(self): if self.DEBUG: + debug_flush() # TODO: remove? ll_assert(not self.young_rawmalloced_objects, "young raw-malloced objects in a major collection") ll_assert(not self.young_objects_with_weakrefs.non_empty(), @@ -2025,10 +2026,11 @@ # # Check that the flags are correct: we must not have # GCFLAG_TRACK_YOUNG_PTRS so far. - ll_assert(self.header(obj).tid & GCFLAG_TRACK_YOUNG_PTRS == 0, - "old_objects_pointing_to_young contains obj with " - "GCFLAG_TRACK_YOUNG_PTRS") - # + # TODO: fix + #ll_assert(self.header(obj).tid & GCFLAG_TRACK_YOUNG_PTRS == 0, + # "old_objects_pointing_to_young contains obj with " + # "GCFLAG_TRACK_YOUNG_PTRS") + # Add the flag GCFLAG_TRACK_YOUNG_PTRS. All live objects should # have this flag set after a nursery collection. self.header(obj).tid |= GCFLAG_TRACK_YOUNG_PTRS @@ -2407,7 +2409,11 @@ # - (non-inc) mark expects all objects to be marked # - both do not rescan nonstack-roots if self.rrc_enabled: + debug_print("starting rrc state:", self.rrc_gc.state) + debug_print("starting marking_state:", self.rrc_gc.marking_state) rrc_finished = self.rrc_gc.major_collection_trace_step() + debug_print("ending rrc state:", self.rrc_gc.state) + debug_print("ending marking_state:", self.rrc_gc.marking_state) else: rrc_finished = True @@ -3138,6 +3144,10 @@ ll_assert(self.rrc_enabled, "rawrefcount.init not called") self.rrc_gc.mark_deallocating(gcobj, pyobject) + def rawrefcount_check_state(self): + ll_assert(self.rrc_enabled, "rawrefcount.init not called") + return self.rrc_gc.state == RawRefCountBaseGC.STATE_DEFAULT + def rawrefcount_next_dead(self): ll_assert(self.rrc_enabled, "rawrefcount.init not called") return self.rrc_gc.next_dead() @@ -3156,10 +3166,12 @@ def rawrefcount_begin_garbage(self): ll_assert(self.rrc_enabled, "rawrefcount.init not called") + debug_print("rrc state garbage") self.rrc_gc.state = RawRefCountBaseGC.STATE_GARBAGE def rawrefcount_end_garbage(self): ll_assert(self.rrc_enabled, "rawrefcount.init not called") + debug_print("rrc state default") self.rrc_gc.state = RawRefCountBaseGC.STATE_DEFAULT def rawrefcount_next_garbage_pypy(self): diff --git a/rpython/memory/gc/rrc/base.py b/rpython/memory/gc/rrc/base.py --- a/rpython/memory/gc/rrc/base.py +++ b/rpython/memory/gc/rrc/base.py @@ -81,6 +81,7 @@ RAWREFCOUNT_REFS_SHIFT = 1 RAWREFCOUNT_REFS_MASK_FINALIZED = 1 RAWREFCOUNT_REFS_UNTRACKED = -2 << RAWREFCOUNT_REFS_SHIFT + RAWREFCOUNT_REFS_REACHABLE = -3 << RAWREFCOUNT_REFS_SHIFT def _pyobj(self, pyobjaddr): return llmemory.cast_adr_to_ptr(pyobjaddr, self.PYOBJ_HDR_PTR) @@ -102,7 +103,7 @@ self.p_dict_nurs = self.gc.AddressDict() # nursery keys only self.dealloc_trigger_callback = dealloc_trigger_callback self.dealloc_pending = self.gc.AddressStack() - self.refcnt_dict = self.gc.AddressDict() + self.pypy_link_dict = self.gc.AddressDict() self.tp_traverse = tp_traverse self.pyobj_list = self._pygchdr(pyobj_list) self.tuple_list = self._pygchdr(tuple_list) @@ -118,6 +119,7 @@ self.finalizer_type = finalizer_type self.tuple_maybe_untrack = tuple_maybe_untrack self.state = self.STATE_DEFAULT + self.marking_state = 0 self.cycle_enabled = True inc_limit = env.read_uint_from_env('PYPY_RRC_GC_INCREMENT_STEP') if inc_limit > 0: @@ -377,7 +379,7 @@ debug_print("pyobj stays alive", pyobj, "rc", rc, "cyclic_rc", cyclic_rc) if use_dict: - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) else: intobj = pyobj.c_ob_pypy_link obj = llmemory.cast_int_to_adr(intobj) @@ -404,7 +406,7 @@ # force the corresponding object to be alive debug_print("pyobj stays alive", pyobj, "rc", rc) if use_dict: - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) else: intobj = pyobj.c_ob_pypy_link obj = llmemory.cast_int_to_adr(intobj) @@ -489,7 +491,7 @@ if pyobj.c_ob_pypy_link <> 0: if use_dict: pyobject = llmemory.cast_ptr_to_adr(pyobj) - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) else: intobj = pyobj.c_ob_pypy_link obj = llmemory.cast_int_to_adr(intobj) @@ -524,7 +526,7 @@ if pyobj.c_ob_pypy_link <> 0: if use_dict: pyobject = llmemory.cast_ptr_to_adr(pyobj) - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) else: intobj = pyobj.c_ob_pypy_link obj = llmemory.cast_int_to_adr(intobj) @@ -584,7 +586,7 @@ pyobj.c_ob_refcnt += self.refcnt_add if self.refcnt_add > 0: pyobject = llmemory.cast_ptr_to_adr(pyobj) - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) self.gc.objects_to_trace.append(obj) self.gc.visit_all_objects() @@ -602,6 +604,13 @@ else: self.tp_traverse(pyobj, self._visit_action, None) + def _pyobj_gc_refcnt_set(self, pygchdr, refcnt): + pygchdr.c_gc_refs &= self.RAWREFCOUNT_REFS_MASK_FINALIZED + pygchdr.c_gc_refs |= refcnt << self.RAWREFCOUNT_REFS_SHIFT + + def _pyobj_gc_refcnt_get(self, pygchdr): + return pygchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT + # --- Helpers --- def _gc_list_new(self): diff --git a/rpython/memory/gc/rrc/incmark.py b/rpython/memory/gc/rrc/incmark.py --- a/rpython/memory/gc/rrc/incmark.py +++ b/rpython/memory/gc/rrc/incmark.py @@ -110,7 +110,8 @@ pyobj = llmemory.cast_adr_to_ptr(snapobj.pyobj, self.PYOBJ_HDR_PTR) pygchdr = self.pyobj_as_gc(pyobj) if (pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR) and - pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED): + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED and + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_REACHABLE): break # only look for non-gc addr = llmemory.cast_int_to_adr(snapobj.pypy_link) if (self.gc.header(addr).tid & @@ -135,14 +136,16 @@ pygchdr = self.pyobj_list.c_gc_next while pygchdr <> self.pyobj_list and consistent: next_old = pygchdr.c_gc_next - if pygchdr.c_gc_refs > 0: # object is in snapshot + if pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_REACHABLE and \ + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED: # object is in snapshot consistent = self._check_consistency_gc(pygchdr, self.pyobj_old_list) if not consistent: break else: # new object, keep alive - pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT + self._pyobj_gc_refcnt_set(pygchdr, 1) + #pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT pyobj = self.gc_as_pyobj(pygchdr) if pyobj.c_ob_pypy_link != 0: addr = llmemory.cast_int_to_adr(pyobj.c_ob_pypy_link) @@ -175,12 +178,14 @@ # continue previous loop, keep objects alive pygchdr = pygchdr_continue_gc while pygchdr <> self.pyobj_list: - pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT + self._pyobj_gc_refcnt_set(pygchdr, 1) + #pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT pygchdr = pygchdr.c_gc_next pygchdr = self.pyobj_old_list.c_gc_next # resurrect "dead" objects while pygchdr <> self.pyobj_old_list: - pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT + self._pyobj_gc_refcnt_set(pygchdr, 1) + #pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT pygchdr = pygchdr.c_gc_next # merge lists if not self._gc_list_is_empty(self.pyobj_old_list): @@ -197,11 +202,13 @@ # continue previous loop, keep objects alive pygchdr = pygchdr_continue_isolate while pygchdr <> self.pyobj_isolate_old_list: - pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT + self._pyobj_gc_refcnt_set(pygchdr, 1) + #pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT pygchdr = pygchdr.c_gc_next # resurrect "dead" objects while pygchdr <> self.pyobj_isolate_dead_list: - pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT + self._pyobj_gc_refcnt_set(pygchdr, 1) + #pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT pygchdr = pygchdr.c_gc_next # merge lists if not self._gc_list_is_empty(self.pyobj_isolate_old_list): @@ -212,8 +219,11 @@ self.pyobj_list) def _check_consistency_gc(self, pygchdr, pylist_dead_target): - snapobj = self.snapshot_objs[pygchdr.c_gc_refs - 1] - pygchdr.c_gc_refs = snapobj.refcnt + c_gc_refs = self._pyobj_gc_refcnt_get(pygchdr) + snapobj = self.snapshot_objs[c_gc_refs - 1] + #snapobj = self.snapshot_objs[pygchdr.c_gc_refs - 1] + self._pyobj_gc_refcnt_set(pygchdr, snapobj.refcnt) + #pygchdr.c_gc_refs = snapobj.refcnt if snapobj.refcnt == 0: # object considered dead # check consistency (dead subgraphs can never change): pyobj = self.gc_as_pyobj(pygchdr) @@ -241,6 +251,7 @@ "refcnt", snapobj.refcnt, "refcnt original", snapobj.refcnt_original, "link", snapobj.pypy_link) + debug_stop("snap " + print_label) def _check_consistency_p_list_old(self, pyobject, foo): pyobj = llmemory.cast_adr_to_ptr(pyobject, self.PYOBJ_HDR_PTR) @@ -262,6 +273,7 @@ elif refcnt >= REFCNT_FROM_PYPY: refcnt -= REFCNT_FROM_PYPY pyobj.c_ob_refcnt = refcnt + pyobj.c_ob_pypy_link = 0 def _collect_roots(self): # Subtract all internal refcounts from the cyclic refcount @@ -345,9 +357,11 @@ self.GCFLAG_NO_HEAP_PTRS): pygchdr = self.pyobj_as_gc(pyobj) if (pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR) and - pygchdr.c_gc_refs > 0 and - pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED): - index = pygchdr.c_gc_refs - 1 + #c_gc_refs > 0 and + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED and + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_REACHABLE): + c_gc_refs = self._pyobj_gc_refcnt_get(pygchdr) + index = c_gc_refs - 1 snapobj = self.snapshot_objs[index] if snapobj.refcnt == 0: addr = llmemory.cast_ptr_to_adr(snapobj) @@ -388,10 +402,15 @@ self.total_objs = total_objs self.objs_index = 0 self.refs_index = 0 + debug_print("take snapshot, count:", self.total_objs) # take snapshot of p_list_old self.p_list_old.foreach(self._take_snapshot_pyobject, None) + # set pypy_link for o_list_old to zero, in case they are encountered + # during tp_traverse (so they are excluded from the snapshot) + self.o_list_old.foreach(self._take_snapshot_o_clearlink, None) + # take snapshot of gc objs pygchdr = self.pyobj_list.c_gc_next while pygchdr <> self.pyobj_list: @@ -405,22 +424,32 @@ pygchdr = pygchdr.c_gc_next # fix references + debug_print("fix references, count:", self.refs_index) for i in range(0, self.refs_index): addr = self.snapshot_refs[i] pyobj = llmemory.cast_adr_to_ptr(addr, self.PYOBJ_HDR_PTR) pygchdr = self.pyobj_as_gc(pyobj) - if (pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR) and - pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED): - if pygchdr.c_gc_refs > 0: - obj = self.snapshot_objs[pygchdr.c_gc_refs - 1] + if pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR): + if (#c_gc_refs > 0 and + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED and + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_REACHABLE): + c_gc_refs = self._pyobj_gc_refcnt_get(pygchdr) + obj = self.snapshot_objs[c_gc_refs - 1] + debug_print("fix reference", i, "from", obj, "gc", + pygchdr.c_gc_refs) else: obj = lltype.nullptr(self.PYOBJ_SNAPSHOT_OBJ) else: obj = self.snapshot_objs[pyobj.c_ob_pypy_link - 1] + debug_print("fix reference", i, "from", obj, "non-gc", + pyobj.c_ob_pypy_link - 1) self.snapshot_refs[i] = llmemory.cast_ptr_to_adr(obj) - # fix links of p_list_old back + # fix links of p_list_old and o_list_old back self.p_list_old.foreach(self._take_snapshot_fixlink, None) + self.pypy_link_dict.foreach(self._take_snapshot_o_fixlink, None) + self.pypy_link_dict.delete() + self.pypy_link_dict = self.gc.AddressDict() def _take_snapshot_count_gc(self, pygchdr): from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY @@ -448,7 +477,9 @@ if self.gc.header(addr).tid & (self.GCFLAG_VISITED | self.GCFLAG_NO_HEAP_PTRS): refcnt += 1 - pygchdr.c_gc_refs = self.objs_index + 1 + debug_print("take snapshot", self.objs_index, "gc", pyobj) + self._pyobj_gc_refcnt_set(pygchdr, self.objs_index + 1) + #pygchdr.c_gc_refs = self.objs_index + 1 obj = self.snapshot_objs[self.objs_index] obj.pyobj = llmemory.cast_ptr_to_adr(pyobj) obj.status = 1 @@ -469,8 +500,7 @@ pyobj = self._pyobj(pyobject) pygchdr = self.pyobj_as_gc(pyobj) # only include non-gc - if (pygchdr == lltype.nullptr(self.PYOBJ_GC_HDR) or - pygchdr.c_gc_refs == self.RAWREFCOUNT_REFS_UNTRACKED): + if pygchdr == lltype.nullptr(self.PYOBJ_GC_HDR): self.p_list_count += 1 refcnt = pyobj.c_ob_refcnt if refcnt >= REFCNT_FROM_PYPY_LIGHT: @@ -486,8 +516,7 @@ pyobj = self._pyobj(pyobject) pygchdr = self.pyobj_as_gc(pyobj) # only include non-gc - if (pygchdr == lltype.nullptr(self.PYOBJ_GC_HDR) or - pygchdr.c_gc_refs == self.RAWREFCOUNT_REFS_UNTRACKED): + if pygchdr == lltype.nullptr(self.PYOBJ_GC_HDR): refcnt = pyobj.c_ob_refcnt if refcnt >= REFCNT_FROM_PYPY_LIGHT: refcnt -= REFCNT_FROM_PYPY_LIGHT @@ -498,6 +527,7 @@ if self.gc.header(addr).tid & (self.GCFLAG_VISITED | self.GCFLAG_NO_HEAP_PTRS): refcnt += 1 + debug_print("take snapshot", self.objs_index, "non-gc", pyobj) obj = self.snapshot_objs[self.objs_index] obj.pyobj = llmemory.cast_ptr_to_adr(pyobj) obj.status = 1 @@ -513,12 +543,25 @@ pyobj = self._pyobj(pyobject) pygchdr = self.pyobj_as_gc(pyobj) # only include non-gc - if (pygchdr == lltype.nullptr(self.PYOBJ_GC_HDR) or - pygchdr.c_gc_refs == self.RAWREFCOUNT_REFS_UNTRACKED): + if pygchdr == lltype.nullptr(self.PYOBJ_GC_HDR): obj_index = pyobj.c_ob_pypy_link - 1 obj = self.snapshot_objs[obj_index] pyobj.c_ob_pypy_link = obj.pypy_link + def _take_snapshot_o_clearlink(self, pyobject, foo): + pyobj = self._pyobj(pyobject) + pygchdr = self.pyobj_as_gc(pyobj) + # only for non-gc + if pygchdr == lltype.nullptr(self.PYOBJ_GC_HDR): + link = llmemory.cast_int_to_adr(pyobj.c_ob_pypy_link) + self.pypy_link_dict.setitem(pyobject, link) + pyobj.c_ob_pypy_link = 0 + + def _take_snapshot_o_fixlink(self, pyobject, link, foo): + pyobj = self._pyobj(pyobject) + link_int = llmemory.cast_adr_to_int(link, "symbolic") + pyobj.c_ob_pypy_link = link_int + def _take_snapshot_visit(pyobj, self_ptr): from rpython.rtyper.annlowlevel import cast_adr_to_nongc_instance # @@ -531,9 +574,12 @@ pygchdr = self.pyobj_as_gc(pyobj) curr = self.snapshot_curr index = curr.refs_index + curr.refs_len - if ((pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR) and + if ((pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR) and pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED) or - pyobj.c_ob_pypy_link != 0): + (pygchdr == lltype.nullptr(self.PYOBJ_GC_HDR) and + pyobj.c_ob_pypy_link != 0)): + debug_print("take ref", index, "curr refs_index", curr.refs_index, + "curr refs_len", curr.refs_len, "whatever", pyobj) self.snapshot_refs[index] = llmemory.cast_ptr_to_adr(pyobj) curr.refs_len += 1 @@ -561,7 +607,8 @@ def _check_snapshot_visit_action(self, pyobj, ignore): pygchdr = self.pyobj_as_gc(pyobj) if pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR) and \ - pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED: + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED and \ + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_REACHABLE: # check consistency with snapshot curr = self.snapshot_curr curr_index = self.snapshot_curr_index diff --git a/rpython/memory/gc/rrc/mark.py b/rpython/memory/gc/rrc/mark.py --- a/rpython/memory/gc/rrc/mark.py +++ b/rpython/memory/gc/rrc/mark.py @@ -56,9 +56,9 @@ self._debug_check_consistency(print_label="end-mark") # fix refcnt back - self.refcnt_dict.foreach(self._fix_refcnt_back, None) - self.refcnt_dict.delete() - self.refcnt_dict = self.gc.AddressDict() + self.pypy_link_dict.foreach(self._fix_refcnt_back, None) + self.pypy_link_dict.delete() + self.pypy_link_dict = self.gc.AddressDict() self.use_refcntdict = False else: self.p_list_old.foreach(self._major_trace, (False, False)) @@ -69,7 +69,7 @@ def to_obj(self, pyobject): if self.use_refcntdict: - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) else: obj = llmemory.cast_int_to_adr( self._pyobj(pyobject).c_ob_pypy_link) @@ -127,19 +127,15 @@ self._traverse(pyobj, -1) pygchdr = pygchdr.c_gc_next - def _pyobj_gc_refcnt_set(self, pygchdr, refcnt): - pygchdr.c_gc_refs &= self.RAWREFCOUNT_REFS_MASK_FINALIZED - pygchdr.c_gc_refs |= refcnt << self.RAWREFCOUNT_REFS_SHIFT - def _obj_save_refcnt(self, pyobject, ignore): pyobj = self._pyobj(pyobject) link = llmemory.cast_int_to_adr(pyobj.c_ob_pypy_link) - self.refcnt_dict.setitem(pyobject, link) + self.pypy_link_dict.setitem(pyobject, link) pyobj.c_ob_pypy_link = pyobj.c_ob_refcnt def _obj_fix_refcnt(self, pyobject, ignore): pyobj = self._pyobj(pyobject) - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) gchdr = self.pyobj_as_gc(pyobj) if gchdr <> lltype.nullptr(self.PYOBJ_GC_HDR): rc = gchdr.c_gc_refs @@ -205,7 +201,7 @@ obj = llmemory.NULL if pyobj.c_ob_pypy_link <> 0: pyobject = llmemory.cast_ptr_to_adr(pyobj) - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) if not alive and self.gc.header(obj).tid & ( self.GCFLAG_VISITED | self.GCFLAG_NO_HEAP_PTRS): # add fake refcount, to mark it as live @@ -227,7 +223,7 @@ def _mark_rawrefcount_linked(self, pyobject, ignore): pyobj = self._pyobj(pyobject) - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) if self.gc.header(obj).tid & (self.GCFLAG_VISITED | self.GCFLAG_NO_HEAP_PTRS): gchdr = self.pyobj_as_gc(pyobj) diff --git a/rpython/memory/gc/test/dot/keep_cpython_inc_1.dot b/rpython/memory/gc/test/dot/keep_cpython_inc_4.dot copy from rpython/memory/gc/test/dot/keep_cpython_inc_1.dot copy to rpython/memory/gc/test/dot/keep_cpython_inc_4.dot --- a/rpython/memory/gc/test/dot/keep_cpython_inc_1.dot +++ b/rpython/memory/gc/test/dot/keep_cpython_inc_4.dot @@ -1,18 +1,9 @@ digraph G { - "a" [type=B, alive=y, ext_refcnt=1]; - "b" [type=P, alive=n]; - "c" [type=B, alive=y]; - "d" [type=P, alive=y]; - "e" [type=B, alive=y]; - "f" [type=C, alive=y]; - "g" [type=C, alive=y]; - "h" [type=C, alive=y, ext_refcnt=1]; - "a" -> "b" [removed=after_snap]; - "b"-> "c"; - "c" -> "d"; - "c" -> "f"; - "f" -> "c"; - "d" -> "e"; - "e" -> "g"; - "h" -> "f" [added=after_snap]; + "a" [type=C, alive=n]; + "b" [type=C, alive=n]; + "c" [type=C, alive=n]; + "d" [type=B, alive=y, added=after_snap, rooted=y]; + "a" -> "b"; + "b" -> "c"; + "c" -> "a"; } diff --git a/rpython/memory/gc/test/test_rawrefcount.py b/rpython/memory/gc/test/test_rawrefcount.py --- a/rpython/memory/gc/test/test_rawrefcount.py +++ b/rpython/memory/gc/test/test_rawrefcount.py @@ -17,6 +17,7 @@ RAWREFCOUNT_FINALIZER_LEGACY = RawRefCountBaseGC.RAWREFCOUNT_FINALIZER_LEGACY RAWREFCOUNT_FINALIZER_NONE = RawRefCountBaseGC.RAWREFCOUNT_FINALIZER_NONE RAWREFCOUNT_REFS_UNTRACKED = RawRefCountBaseGC.RAWREFCOUNT_REFS_UNTRACKED +RAWREFCOUNT_REFS_REACHABLE = RawRefCountBaseGC.RAWREFCOUNT_REFS_REACHABLE S = lltype.GcForwardReference() S.become(lltype.GcStruct('S', @@ -249,7 +250,7 @@ immortal=True) self.gcobjs.append(r1gc) if tracked: - r1gc.c_gc_refs = 0 + r1gc.c_gc_refs = RAWREFCOUNT_REFS_REACHABLE if tuple: r1gc.c_gc_next = self.tuple_list r1gc.c_gc_prev = self.tuple_list.c_gc_prev 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 @@ -514,6 +514,9 @@ self.rawrefcount_to_obj_ptr = getfn( GCClass.rawrefcount_to_obj, [s_gc, SomeAddress()], s_gcref, inline = True) + self.rawrefcount_check_state_ptr = getfn( + GCClass.rawrefcount_check_state, [s_gc], annmodel.SomeBool(), + inline = True) self.rawrefcount_next_dead_ptr = getfn( GCClass.rawrefcount_next_dead, [s_gc], SomeAddress(), inline = True) @@ -1424,6 +1427,11 @@ [self.rawrefcount_to_obj_ptr, self.c_const_gc, v_pyobject], resultvar=hop.spaceop.result) + def gct_gc_rawrefcount_check_state(self, hop): + hop.genop("direct_call", + [self.rawrefcount_check_state_ptr, self.c_const_gc], + resultvar=hop.spaceop.result) + def gct_gc_rawrefcount_next_dead(self, hop): assert hop.spaceop.result.concretetype == llmemory.Address hop.genop("direct_call", diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py --- a/rpython/rlib/rawrefcount.py +++ b/rpython/rlib/rawrefcount.py @@ -117,6 +117,10 @@ return p @not_rpython +def check_state(): + return True + + at not_rpython def next_dead(OB_PTR_TYPE): """When the GC runs, it finds some pyobjs to be dead but cannot immediately dispose of them (it doesn't know how to call @@ -387,6 +391,19 @@ resulttype = llmemory.GCREF) return _spec_p(hop, v_p) + +class Entry(ExtRegistryEntry): + _about_ = check_state + + def compute_result_annotation(self): + from rpython.annotator import model as annmodel + return annmodel.SomeBool() + + def specialize_call(self, hop): + hop.exception_cannot_occur() + return hop.genop('gc_rawrefcount_check_state', [], + resulttype = hop.r_result) + class Entry(ExtRegistryEntry): _about_ = (next_dead, cyclic_garbage_head, next_cyclic_isolate, next_garbage_pyobj) diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py --- a/rpython/rtyper/llinterp.py +++ b/rpython/rtyper/llinterp.py @@ -984,6 +984,9 @@ def op_gc_rawrefcount_mark_deallocating(self, *args): raise NotImplementedError("gc_rawrefcount_mark_deallocating") + def op_gc_rawrefcount_check_state(self, *args): + raise NotImplementedError("gc_rawrefcount_check_state") + def op_gc_rawrefcount_next_dead(self, *args): raise NotImplementedError("gc_rawrefcount_next_dead") 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 @@ -528,6 +528,7 @@ 'gc_rawrefcount_mark_deallocating': LLOp(), 'gc_rawrefcount_from_obj': LLOp(sideeffects=False), 'gc_rawrefcount_to_obj': LLOp(sideeffects=False), + 'gc_rawrefcount_check_state': LLOp(sideeffects=False), 'gc_rawrefcount_next_dead': LLOp(), 'gc_rawrefcount_next_cyclic_isolate': LLOp(), 'gc_rawrefcount_cyclic_garbage_head': LLOp(sideeffects=False), From pypy.commits at gmail.com Thu Oct 10 15:34:58 2019 From: pypy.commits at gmail.com (rlamy) Date: Thu, 10 Oct 2019 12:34:58 -0700 (PDT) Subject: [pypy-commit] pypy default: Stop updating hypothesis: 4.40 is incompatible with our ancient pytest Message-ID: <5d9f87e2.1c69fb81.45ee8.25c7@mx.google.com> Author: Ronan Lamy Branch: Changeset: r97757:9b953c97479f Date: 2019-10-10 20:34 +0100 http://bitbucket.org/pypy/pypy/changeset/9b953c97479f/ Log: Stop updating hypothesis: 4.40 is incompatible with our ancient pytest diff --git a/requirements.txt b/requirements.txt --- a/requirements.txt +++ b/requirements.txt @@ -6,5 +6,5 @@ vmprof>=0.4.10; 'x86' in platform.machine #skip arm, s390x # hypothesis is used for test generation on untranslated tests -hypothesis +hypothesis<4.40 enum34>=1.1.2 From pypy.commits at gmail.com Fri Oct 11 04:45:16 2019 From: pypy.commits at gmail.com (cfbolz) Date: Fri, 11 Oct 2019 01:45:16 -0700 (PDT) Subject: [pypy-commit] pypy default: fix translation, tweak output Message-ID: <5da0411c.1c69fb81.a4ca9.ba66@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r97761:c866b476cfee Date: 2019-10-11 10:43 +0200 http://bitbucket.org/pypy/pypy/changeset/c866b476cfee/ Log: fix translation, tweak output diff --git a/rpython/jit/metainterp/quasiimmut.py b/rpython/jit/metainterp/quasiimmut.py --- a/rpython/jit/metainterp/quasiimmut.py +++ b/rpython/jit/metainterp/quasiimmut.py @@ -27,12 +27,14 @@ return qmut def make_invalidation_function(STRUCT, mutatefieldname): - # + # fake a repr + descr_repr = "FieldDescr(%s, '%s')" % (STRUCT.TO, mutatefieldname) + def _invalidate_now(p): qmut_ptr = getattr(p, mutatefieldname) setattr(p, mutatefieldname, lltype.nullptr(rclass.OBJECT)) qmut = cast_base_ptr_to_instance(QuasiImmut, qmut_ptr) - qmut.invalidate(mutatefieldname) + qmut.invalidate(descr_repr) _invalidate_now._dont_inline_ = True # def invalidation(p): @@ -46,7 +48,7 @@ if qmut_ref: cpu.bh_setfield_gc_r(p, ConstPtr.value, mutatefielddescr) qmut = cast_gcref_to_instance(QuasiImmut, qmut_ref) - qmut.invalidate(mutatefielddescr.fieldname) + qmut.invalidate(mutatefielddescr.repr_of_descr()) class QuasiImmut(object): @@ -79,7 +81,7 @@ # already invalidated; see below self.compress_limit = (len(self.looptokens_wrefs) + 15) * 2 - def invalidate(self, fieldname=None): + def invalidate(self, descr_repr=None): debug_start("jit-invalidate-quasi-immutable") # When this is called, all the loops that we record become # invalid: all GUARD_NOT_INVALIDATED in these loops (and @@ -104,7 +106,7 @@ if not we_are_translated(): self.cpu.stats.invalidated_token_numbers.add( looptoken.number) - debug_print("fieldname", fieldname or "", "invalidated", invalidated) + debug_print("fieldname", descr_repr or "", "invalidated", invalidated) debug_stop("jit-invalidate-quasi-immutable") From pypy.commits at gmail.com Fri Oct 11 06:41:10 2019 From: pypy.commits at gmail.com (mattip) Date: Fri, 11 Oct 2019 03:41:10 -0700 (PDT) Subject: [pypy-commit] pypy default: refactor repackage script to generate exe Message-ID: <5da05c46.1c69fb81.f0ef.5c33@mx.google.com> Author: Matti Picus Branch: Changeset: r97762:241777245f33 Date: 2019-10-11 13:40 +0300 http://bitbucket.org/pypy/pypy/changeset/241777245f33/ Log: refactor repackage script to generate exe diff --git a/pypy/tool/release/repackage.sh b/pypy/tool/release/repackage.sh --- a/pypy/tool/release/repackage.sh +++ b/pypy/tool/release/repackage.sh @@ -1,11 +1,15 @@ # Edit these appropriately before running this script pmaj=2 # python main version: 2 or 3 pmin=7 # python minor version -exe=pypy # pypy3 or pypy maj=7 min=2 -rev=0rc0 +rev=0rc2 +case $pmaj in + "2") exe=pypy;; + "3") exe=pypy3;; + *) echo invalid pmaj=$pmaj; exit 1;; +esac branchname=release-pypy$pmaj.$pmin-v$maj.x # ==OR== release-v$maj.x # ==OR== release-v$maj.$min.x tagname=release-pypy$pmaj.$pmin-v$maj.$min.$rev # ==OR== release-$maj.$min From pypy.commits at gmail.com Fri Oct 11 07:30:39 2019 From: pypy.commits at gmail.com (mattip) Date: Fri, 11 Oct 2019 04:30:39 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: merge default into py3.6 Message-ID: <5da067df.1c69fb81.c7a91.c08c@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r97763:f963c5cf8e2e Date: 2019-10-11 14:29 +0300 http://bitbucket.org/pypy/pypy/changeset/f963c5cf8e2e/ Log: merge default into py3.6 diff --git a/pypy/tool/release/repackage.sh b/pypy/tool/release/repackage.sh --- a/pypy/tool/release/repackage.sh +++ b/pypy/tool/release/repackage.sh @@ -1,15 +1,18 @@ # Edit these appropriately before running this script pmaj=2 # python main version: 2 or 3 pmin=7 # python minor version -exe=pypy3 # pypy3 or pypy maj=7 min=2 -rev=0 +rev=0rc2 +case $pmaj in + "2") exe=pypy;; + "3") exe=pypy3;; + *) echo invalid pmaj=$pmaj; exit 1;; +esac branchname=release-pypy$pmaj.$pmin-v$maj.x # ==OR== release-v$maj.x # ==OR== release-v$maj.$min.x -tagname=release-candidate-pypy$pmaj.$pmin-v$maj.$min.$rev # ==OR== release-$maj.$min -# tagname=release-pypy$pmaj.$pmin-v$maj.$min.$rev # ==OR== release-$maj.$min +tagname=release-pypy$pmaj.$pmin-v$maj.$min.$rev # ==OR== release-$maj.$min echo checking hg log -r $branchname hg log -r $branchname || exit 1 diff --git a/rpython/jit/metainterp/quasiimmut.py b/rpython/jit/metainterp/quasiimmut.py --- a/rpython/jit/metainterp/quasiimmut.py +++ b/rpython/jit/metainterp/quasiimmut.py @@ -27,12 +27,14 @@ return qmut def make_invalidation_function(STRUCT, mutatefieldname): - # + # fake a repr + descr_repr = "FieldDescr(%s, '%s')" % (STRUCT.TO, mutatefieldname) + def _invalidate_now(p): qmut_ptr = getattr(p, mutatefieldname) setattr(p, mutatefieldname, lltype.nullptr(rclass.OBJECT)) qmut = cast_base_ptr_to_instance(QuasiImmut, qmut_ptr) - qmut.invalidate(mutatefieldname) + qmut.invalidate(descr_repr) _invalidate_now._dont_inline_ = True # def invalidation(p): @@ -46,7 +48,7 @@ if qmut_ref: cpu.bh_setfield_gc_r(p, ConstPtr.value, mutatefielddescr) qmut = cast_gcref_to_instance(QuasiImmut, qmut_ref) - qmut.invalidate(mutatefielddescr.fieldname) + qmut.invalidate(mutatefielddescr.repr_of_descr()) class QuasiImmut(object): @@ -79,7 +81,7 @@ # already invalidated; see below self.compress_limit = (len(self.looptokens_wrefs) + 15) * 2 - def invalidate(self, fieldname=None): + def invalidate(self, descr_repr=None): debug_start("jit-invalidate-quasi-immutable") # When this is called, all the loops that we record become # invalid: all GUARD_NOT_INVALIDATED in these loops (and @@ -104,7 +106,7 @@ if not we_are_translated(): self.cpu.stats.invalidated_token_numbers.add( looptoken.number) - debug_print("fieldname", fieldname or "", "invalidated", invalidated) + debug_print("fieldname", descr_repr or "", "invalidated", invalidated) debug_stop("jit-invalidate-quasi-immutable") From pypy.commits at gmail.com Sat Oct 12 06:19:08 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 12 Oct 2019 03:19:08 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: #3088: don't grow the lzma.decompress() buffer past max_length Message-ID: <5da1a89c.1c69fb81.2086a.1c25@mx.google.com> Author: Armin Rigo Branch: py3.6 Changeset: r97764:466d2e12ff4b Date: 2019-10-12 12:18 +0200 http://bitbucket.org/pypy/pypy/changeset/466d2e12ff4b/ Log: #3088: don't grow the lzma.decompress() buffer past max_length diff --git a/lib_pypy/_lzma.py b/lib_pypy/_lzma.py --- a/lib_pypy/_lzma.py +++ b/lib_pypy/_lzma.py @@ -634,6 +634,8 @@ break # ran out of space in the output buffer, let's grow it bufsiz += (bufsiz >> 3) + 6 + if max_length > 0 and bufsiz > max_length: + bufsiz = max_length next_out = m.realloc(orig_out, bufsiz) if next_out == ffi.NULL: # realloc unsuccessful From pypy.commits at gmail.com Sat Oct 12 06:38:49 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 12 Oct 2019 03:38:49 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: #3090: lzma sometimes fails to decompress a file Message-ID: <5da1ad39.1c69fb81.f3095.73ca@mx.google.com> Author: Armin Rigo Branch: py3.6 Changeset: r97765:3a35a0ae4463 Date: 2019-10-12 12:38 +0200 http://bitbucket.org/pypy/pypy/changeset/3a35a0ae4463/ Log: #3090: lzma sometimes fails to decompress a file Fixed by back-porting CPython commit a3c53a1b45b05bcb69660eac5a271443b37ecc42 diff --git a/lib_pypy/_lzma.py b/lib_pypy/_lzma.py --- a/lib_pypy/_lzma.py +++ b/lib_pypy/_lzma.py @@ -583,8 +583,16 @@ self.clear_input_buffer() elif lzs.avail_in == 0: # completed successfully! - self.needs_input = True lzs.next_in = ffi.NULL + if lzs.avail_out == 0: + # (avail_in==0 && avail_out==0) + # Maybe lzs's internal state still have a few bytes can + # be output, try to output them next time. + self.needs_input = False + assert max_length >= 0 # if < 0, lzs.avail_out always > 0 + else: + # Input buffer exhausted, output buffer has space. + self.needs_input = True self.clear_input_buffer() else: self.needs_input = False @@ -599,9 +607,6 @@ lzs.next_in = buf lzs.avail_in = buf_len - if buf_len == 0: - return b"" - bufsiz = self._bufsiz if not (max_length < 0 or max_length > io.DEFAULT_BUFFER_SIZE): bufsiz = max_length @@ -616,7 +621,8 @@ try: while True: - ret = catch_lzma_error(m.lzma_code, lzs, m.LZMA_RUN) + ret = catch_lzma_error(m.lzma_code, lzs, m.LZMA_RUN, + ignore_buf_error=(lzs.avail_in == 0 and lzs.avail_out > 0)) data_size = int(ffi.cast('uintptr_t', lzs.next_out)) - int(ffi.cast('uintptr_t', orig_out)) # data_size is the amount lzma_code has already outputted @@ -626,10 +632,11 @@ if ret == m.LZMA_STREAM_END: self.eof = True break - elif lzs.avail_in == 0: - # it ate everything - break elif lzs.avail_out == 0: + # Need to check lzs->avail_out before lzs->avail_in. + # Maybe lzs's internal state still have a few bytes + # can be output, grow the output buffer and continue + # if max_lengh < 0. if data_size == max_length: break # ran out of space in the output buffer, let's grow it @@ -647,6 +654,9 @@ lzs.next_out = orig_out + data_size lzs.avail_out = bufsiz - data_size + elif lzs.avail_in == 0: + # it ate everything + break result = ffi.buffer(orig_out, data_size)[:] finally: From pypy.commits at gmail.com Sat Oct 12 18:10:23 2019 From: pypy.commits at gmail.com (cfbolz) Date: Sat, 12 Oct 2019 15:10:23 -0700 (PDT) Subject: [pypy-commit] pypy fix-descrmismatch-crash: fix first level of problems: args.firstarg() is always None in this context, Message-ID: <5da24f4f.1c69fb81.ca3c7.f9de@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: fix-descrmismatch-crash Changeset: r97766:da9a65e1debd Date: 2019-10-13 00:09 +0200 http://bitbucket.org/pypy/pypy/changeset/da9a65e1debd/ Log: fix first level of problems: args.firstarg() is always None in this context, use w_obj instead diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py --- a/pypy/interpreter/gateway.py +++ b/pypy/interpreter/gateway.py @@ -709,6 +709,7 @@ self.func__args__ = func elif unwrap_spec == [self_type, ObjSpace, Arguments]: self.__class__ = BuiltinCodePassThroughArguments1 + self.descr_reqcls = self_type miniglobals = {'func': func, 'self_type': self_type} d = {} source = """if 1: @@ -808,7 +809,7 @@ try: w_result = self.func__args__(space, w_obj, args) except DescrMismatch: - return args.firstarg().descr_call_mismatch(space, + return w_obj.descr_call_mismatch(space, self.descrmismatch_op, self.descr_reqcls, args.prepend(w_obj)) diff --git a/pypy/interpreter/test/test_gateway.py b/pypy/interpreter/test/test_gateway.py --- a/pypy/interpreter/test/test_gateway.py +++ b/pypy/interpreter/test/test_gateway.py @@ -1005,3 +1005,13 @@ d.update(**{clash: 33}) dict.update(d, **{clash: 33}) + + + +class AppTestFastPathCrash(object): + def test_fast_path_crash(self): + with raises(TypeError) as excinfo: + # does not crash in BuiltinCodePassThroughArguments0.funcrun + dict.__init__.im_func(0) + print(str(excinfo.value)) + assert str(excinfo.value) == "'dict' object expected, got 'int' instead" From pypy.commits at gmail.com Sun Oct 13 04:59:50 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 13 Oct 2019 01:59:50 -0700 (PDT) Subject: [pypy-commit] pypy fix-descrmismatch-crash: improve error message Message-ID: <5da2e786.1c69fb81.4a165.fd00@mx.google.com> Author: Matti Picus Branch: fix-descrmismatch-crash Changeset: r97767:5aa0e1eef0dd Date: 2019-10-13 11:58 +0300 http://bitbucket.org/pypy/pypy/changeset/5aa0e1eef0dd/ Log: improve error message diff --git a/pypy/interpreter/test/test_gateway.py b/pypy/interpreter/test/test_gateway.py --- a/pypy/interpreter/test/test_gateway.py +++ b/pypy/interpreter/test/test_gateway.py @@ -1009,9 +1009,18 @@ class AppTestFastPathCrash(object): + def setup_class(cls): + cls.w_runappdirect = cls.space.wrap(cls.runappdirect) + def test_fast_path_crash(self): - with raises(TypeError) as excinfo: - # does not crash in BuiltinCodePassThroughArguments0.funcrun - dict.__init__.im_func(0) - print(str(excinfo.value)) - assert str(excinfo.value) == "'dict' object expected, got 'int' instead" + # issue bb-3091 crash in BuiltinCodePassThroughArguments0.funcrun + for obj in (dict, set, frozenset): + with raises(TypeError) as excinfo: + if self.runappdirect: + msg_fmt = "'%s' object but received a '%s'" + obj.__init__(0) + else: + msg_fmt = "'%s' object expected, got '%s'" + obj.__init__.im_func(0) + msg = msg_fmt %(obj.__name__, 'int') + assert msg in str(excinfo.value) diff --git a/pypy/objspace/std/setobject.py b/pypy/objspace/std/setobject.py --- a/pypy/objspace/std/setobject.py +++ b/pypy/objspace/std/setobject.py @@ -496,6 +496,12 @@ class W_SetObject(W_BaseSetObject): + + #overridden here so the error is reported correctly + def __init__(self, space, w_iterable=None): + """Initialize the set by taking ownership of 'setdata'.""" + W_BaseSetObject.__init__(self, space, w_iterable) + def _newobj(self, space, w_iterable): """Make a new set by taking ownership of 'w_iterable'.""" if type(self) is W_SetObject: @@ -516,7 +522,7 @@ Build an unordered collection.""", __new__ = gateway.interp2app(W_SetObject.descr_new), - __init__ = gateway.interp2app(W_BaseSetObject.descr_init), + __init__ = gateway.interp2app(W_SetObject.descr_init), __repr__ = gateway.interp2app(W_BaseSetObject.descr_repr), __hash__ = None, __cmp__ = gateway.interp2app(W_BaseSetObject.descr_cmp), @@ -572,6 +578,11 @@ class W_FrozensetObject(W_BaseSetObject): hash = 0 + #overridden here so the error is reported correctly + def __init__(self, space, w_iterable=None): + """Initialize the frozenset by taking ownership of 'setdata'.""" + W_BaseSetObject.__init__(self, space, w_iterable) + def _cleanup_(self): # in case there are frozenset objects existing during # translation, make sure we don't translate a cached hash @@ -639,6 +650,7 @@ Build an immutable unordered collection.""", __new__ = gateway.interp2app(W_FrozensetObject.descr_new2), + __init__ = gateway.interp2app(W_FrozensetObject.descr_init), __repr__ = gateway.interp2app(W_BaseSetObject.descr_repr), __hash__ = gateway.interp2app(W_FrozensetObject.descr_hash), __cmp__ = gateway.interp2app(W_BaseSetObject.descr_cmp), From pypy.commits at gmail.com Mon Oct 14 01:59:31 2019 From: pypy.commits at gmail.com (arigo) Date: Sun, 13 Oct 2019 22:59:31 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: issue #3092 Message-ID: <5da40ec3.1c69fb81.26597.6b56@mx.google.com> Author: Armin Rigo Branch: py3.6 Changeset: r97768:234d0560480e Date: 2019-10-14 07:58 +0200 http://bitbucket.org/pypy/pypy/changeset/234d0560480e/ Log: issue #3092 PyObject_GenericGetDict, PyObject_GenericSetDict diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -308,3 +308,5 @@ return 0 return 1 +#def PyObject_GenericGetDict(space, w_obj, context): +# unlike CPython, you'll find this one in object.py together with ..SetDict diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py --- a/pypy/module/cpyext/object.py +++ b/pypy/module/cpyext/object.py @@ -444,3 +444,16 @@ del d[w_obj] except KeyError: pass + + at cpython_api([PyObject, rffi.VOIDP], PyObject) +def PyObject_GenericGetDict(space, w_obj, context): + from pypy.interpreter.typedef import descr_get_dict + return descr_get_dict(space, w_obj) + + at cpython_api([PyObject, PyObject, rffi.VOIDP], rffi.INT_real, error=-1) +def PyObject_GenericSetDict(space, w_obj, w_value, context): + from pypy.interpreter.typedef import descr_set_dict + if w_value is None: + raise oefmt(space.w_TypeError, "cannot delete __dict__") + descr_set_dict(space, w_obj, w_value) + return 0 diff --git a/pypy/module/cpyext/test/test_object.py b/pypy/module/cpyext/test/test_object.py --- a/pypy/module/cpyext/test/test_object.py +++ b/pypy/module/cpyext/test/test_object.py @@ -451,6 +451,31 @@ assert n == 1 module.leave(obj2) + def test_GenericGetSetDict(self): + module = self.import_extension('test_GenericGetSetDict', [ + ('test1', 'METH_VARARGS', + """ + PyObject *obj = PyTuple_GET_ITEM(args, 0); + PyObject *newdict = PyTuple_GET_ITEM(args, 1); + + PyObject *olddict = PyObject_GenericGetDict(obj, NULL); + if (olddict == NULL) + return NULL; + int res = PyObject_GenericSetDict(obj, newdict, NULL); + if (res != 0) + return NULL; + return olddict; + """)]) + class A: + pass + a = A() + a.x = 42 + nd = {'y': 43} + d = module.test1(a, nd) + assert d == {'x': 42} + assert a.y == 43 + assert a.__dict__ is nd + class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase): """ From pypy.commits at gmail.com Mon Oct 14 03:19:02 2019 From: pypy.commits at gmail.com (arigo) Date: Mon, 14 Oct 2019 00:19:02 -0700 (PDT) Subject: [pypy-commit] cffi default: Add a warning when we use in cdef() a global variable without also specifying a storage class (extern or static) Message-ID: <5da42166.1c69fb81.a4845.d740@mx.google.com> Author: Armin Rigo Branch: Changeset: r3294:32b6ed4cb029 Date: 2019-10-14 09:16 +0200 http://bitbucket.org/cffi/cffi/changeset/32b6ed4cb029/ Log: Add a warning when we use in cdef() a global variable without also specifying a storage class (extern or static) diff --git a/cffi/cparser.py b/cffi/cparser.py --- a/cffi/cparser.py +++ b/cffi/cparser.py @@ -156,6 +156,13 @@ "confuse pre-parsing.") break +def _warn_for_non_extern_non_static_global_variable(decl): + if not decl.storage: + import warnings + warnings.warn("Declaration of global variable '%s' in cdef() should " + "be marked 'extern' for consistency (or possibly " + "'static' in API mode)" % (decl.name,)) + def _preprocess(csource): # Remove comments. NOTE: this only work because the cdef() section # should not contain any string literal! @@ -506,6 +513,7 @@ if (quals & model.Q_CONST) and not tp.is_array_type: self._declare('constant ' + decl.name, tp, quals=quals) else: + _warn_for_non_extern_non_static_global_variable(decl) self._declare('variable ' + decl.name, tp, quals=quals) def parse_type(self, cdecl): diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py --- a/testing/cffi0/test_function.py +++ b/testing/cffi0/test_function.py @@ -113,7 +113,7 @@ ffi = FFI(backend=self.Backend()) ffi.cdef(""" int fputs(const char *, void *); - void *stderr; + extern void *stderr; """) needs_dlopen_none() ffi.C = ffi.dlopen(None) @@ -130,7 +130,7 @@ ffi = FFI(backend=self.Backend()) ffi.cdef(""" int fputs(char *, void *); - void *stderr; + extern void *stderr; """) needs_dlopen_none() ffi.C = ffi.dlopen(None) @@ -147,7 +147,7 @@ ffi = FFI(backend=self.Backend()) ffi.cdef(""" int fprintf(void *, const char *format, ...); - void *stderr; + extern void *stderr; """) needs_dlopen_none() ffi.C = ffi.dlopen(None) @@ -209,7 +209,7 @@ py.test.skip("probably no symbol 'stderr' in the lib") ffi.cdef(""" int fputs(const char *, void *); - void *stderr; + extern void *stderr; """) needs_dlopen_none() ffi.C = ffi.dlopen(None) @@ -256,7 +256,7 @@ py.test.skip("probably no symbol 'stdout' in the lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" - void *stdout; + extern void *stdout; """) needs_dlopen_none() C = ffi.dlopen(None) @@ -496,7 +496,7 @@ ffi.cdef(""" typedef enum { MYE1, MYE2 } myenum_t; double myfunc(double); - double myvar; + extern double myvar; const double myconst; #define MYFOO 42 """) @@ -507,7 +507,7 @@ if self.Backend is CTypesBackend: py.test.skip("not with the ctypes backend") ffi = FFI(backend=self.Backend()) - ffi.cdef("int foobar(void); int foobaz;") + ffi.cdef("int foobar(void); extern int foobaz;") lib = ffi.dlopen(lib_m) ffi.dlclose(lib) e = py.test.raises(ValueError, getattr, lib, 'foobar') diff --git a/testing/cffi0/test_ownlib.py b/testing/cffi0/test_ownlib.py --- a/testing/cffi0/test_ownlib.py +++ b/testing/cffi0/test_ownlib.py @@ -201,7 +201,7 @@ py.test.skip("fix the auto-generation of the tiny test lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" - int my_array[7]; + extern int my_array[7]; """) ownlib = ffi.dlopen(self.module) for i in range(7): @@ -223,7 +223,7 @@ py.test.skip("not supported by the ctypes backend") ffi = FFI(backend=self.Backend()) ffi.cdef(""" - int my_array[]; + extern int my_array[]; """) ownlib = ffi.dlopen(self.module) for i in range(7): @@ -291,7 +291,7 @@ long bottom; } RECT; - long left, top, right, bottom; + extern long left, top, right, bottom; RECT ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr, RECT *er, POINT fp, RECT gr); @@ -321,7 +321,7 @@ if self.Backend is CTypesBackend: py.test.skip("not implemented with the ctypes backend") ffi = FFI(backend=self.Backend()) - ffi.cdef("long left; int test_getting_errno(void);") + ffi.cdef("extern long left; int test_getting_errno(void);") lib = ffi.dlopen(self.module) lib.left = 123456 p = ffi.addressof(lib, "left") diff --git a/testing/cffi0/test_parsing.py b/testing/cffi0/test_parsing.py --- a/testing/cffi0/test_parsing.py +++ b/testing/cffi0/test_parsing.py @@ -324,6 +324,7 @@ assert value == sys.maxsize * 2 - 40 def test__is_constant_globalvar(): + import warnings for input, expected_output in [ ("int a;", False), ("const int a;", True), @@ -341,10 +342,13 @@ ("const int a[5][6];", False), ]: ffi = FFI() - ffi.cdef(input) + with warnings.catch_warnings(record=True) as log: + warnings.simplefilter("always") + ffi.cdef(input) declarations = ffi._parser._declarations assert ('constant a' in declarations) == expected_output assert ('variable a' in declarations) == (not expected_output) + assert len(log) == (1 - expected_output) def test_restrict(): from cffi import model @@ -354,7 +358,7 @@ ("int *a;", False), ]: ffi = FFI() - ffi.cdef(input) + ffi.cdef("extern " + input) tp, quals = ffi._parser._declarations['variable a'] assert bool(quals & model.Q_RESTRICT) == expected_output diff --git a/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py --- a/testing/cffi0/test_verify.py +++ b/testing/cffi0/test_verify.py @@ -286,7 +286,7 @@ def test_var_signed_integer_types(): ffi = FFI() lst = all_signed_integer_types(ffi) - csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_')) + csource = "\n".join(["static %s somevar_%s;" % (tp, tp.replace(' ', '_')) for tp in lst]) ffi.cdef(csource) lib = ffi.verify(csource) @@ -305,7 +305,7 @@ def test_var_unsigned_integer_types(): ffi = FFI() lst = all_unsigned_integer_types(ffi) - csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_')) + csource = "\n".join(["static %s somevar_%s;" % (tp, tp.replace(' ', '_')) for tp in lst]) ffi.cdef(csource) lib = ffi.verify(csource) @@ -817,8 +817,8 @@ def test_access_variable(): ffi = FFI() - ffi.cdef("int foo(void);\n" - "int somenumber;") + ffi.cdef("static int foo(void);\n" + "static int somenumber;") lib = ffi.verify(""" static int somenumber = 2; static int foo(void) { @@ -835,7 +835,7 @@ def test_access_address_of_variable(): # access the address of 'somenumber': need a trick ffi = FFI() - ffi.cdef("int somenumber; static int *const somenumberptr;") + ffi.cdef("static int somenumber; static int *const somenumberptr;") lib = ffi.verify(""" static int somenumber = 2; #define somenumberptr (&somenumber) @@ -848,7 +848,7 @@ def test_access_array_variable(length=5): ffi = FFI() ffi.cdef("int foo(int);\n" - "int somenumber[%s];" % (length,)) + "static int somenumber[%s];" % (length,)) lib = ffi.verify(""" static int somenumber[] = {2, 2, 3, 4, 5}; static int foo(int i) { @@ -880,7 +880,7 @@ ffi = FFI() ffi.cdef("struct foo { int x; ...; };\n" "int foo(int);\n" - "struct foo stuff;") + "static struct foo stuff;") lib = ffi.verify(""" struct foo { int x, y, z; }; static struct foo stuff = {2, 5, 8}; @@ -904,9 +904,9 @@ def test_access_callback(): ffi = FFI() - ffi.cdef("int (*cb)(int);\n" - "int foo(int);\n" - "void reset_cb(void);") + ffi.cdef("static int (*cb)(int);\n" + "static int foo(int);\n" + "static void reset_cb(void);") lib = ffi.verify(""" static int g(int x) { return x * 7; } static int (*cb)(int); @@ -922,9 +922,9 @@ def test_access_callback_function_typedef(): ffi = FFI() ffi.cdef("typedef int mycallback_t(int);\n" - "mycallback_t *cb;\n" - "int foo(int);\n" - "void reset_cb(void);") + "static mycallback_t *cb;\n" + "static int foo(int);\n" + "static void reset_cb(void);") lib = ffi.verify(""" static int g(int x) { return x * 7; } static int (*cb)(int); @@ -1074,7 +1074,7 @@ def test_autofilled_struct_as_argument_dynamic(): ffi = FFI() ffi.cdef("struct foo_s { long a; ...; };\n" - "int (*foo)(struct foo_s);") + "static int (*foo)(struct foo_s);") lib = ffi.verify(""" struct foo_s { double b; @@ -1083,7 +1083,7 @@ int foo1(struct foo_s s) { return (int)s.a - (int)s.b; } - int (*foo)(struct foo_s s) = &foo1; + static int (*foo)(struct foo_s s) = &foo1; """) e = py.test.raises(NotImplementedError, lib.foo, "?") msg = ("ctype 'struct foo_s' not supported as argument. It is a struct " @@ -1453,7 +1453,7 @@ py.test.skip("_Bool not in MSVC") ffi = FFI() ffi.cdef("struct foo_s { _Bool x; };" - "_Bool foo(_Bool); _Bool (*foop)(_Bool);") + "_Bool foo(_Bool); static _Bool (*foop)(_Bool);") lib = ffi.verify(""" struct foo_s { _Bool x; }; int foo(int arg) { @@ -1462,7 +1462,7 @@ _Bool _foofunc(_Bool x) { return !x; } - _Bool (*foop)(_Bool) = _foofunc; + static _Bool (*foop)(_Bool) = _foofunc; """) p = ffi.new("struct foo_s *") p.x = 1 @@ -1653,7 +1653,7 @@ def test_FILE_stored_explicitly(): ffi = FFI() - ffi.cdef("int myprintf11(const char *, int); FILE *myfile;") + ffi.cdef("int myprintf11(const char *, int); extern FILE *myfile;") lib = ffi.verify(""" #include FILE *myfile; @@ -1679,19 +1679,19 @@ def test_global_array_with_missing_length(): ffi = FFI() - ffi.cdef("int fooarray[];") + ffi.cdef("extern int fooarray[];") lib = ffi.verify("int fooarray[50];") assert repr(lib.fooarray).startswith("" def test_bug_const_char_ptr_array_2(): from cffi import FFI # ignore warnings ffi = FFI() - ffi.cdef("""const int a[];""") + ffi.cdef("""extern const int a[];""") lib = ffi.verify("""const int a[5];""") assert repr(ffi.typeof(lib.a)) == "" def _test_various_calls(force_libffi): cdef_source = """ - int xvalue; - long long ivalue, rvalue; - float fvalue; - double dvalue; - long double Dvalue; + extern int xvalue; + extern long long ivalue, rvalue; + extern float fvalue; + extern double dvalue; + extern long double Dvalue; signed char tf_bb(signed char x, signed char c); unsigned char tf_bB(signed char x, unsigned char c); short tf_bh(signed char x, short c); @@ -2147,7 +2147,7 @@ # exported symbols as well. So we must not export a simple name # like 'foo'! ffi1 = FFI() - ffi1.cdef("int foo_verify_dlopen_flags;") + ffi1.cdef("extern int foo_verify_dlopen_flags;") lib1 = ffi1.verify("int foo_verify_dlopen_flags;", flags=ffi1.RTLD_GLOBAL | ffi1.RTLD_LAZY) @@ -2161,7 +2161,7 @@ def get_second_lib(): # Hack, using modulename makes the test fail ffi2 = FFI() - ffi2.cdef("int foo_verify_dlopen_flags;") + ffi2.cdef("extern int foo_verify_dlopen_flags;") lib2 = ffi2.verify("int foo_verify_dlopen_flags;", flags=ffi2.RTLD_GLOBAL | ffi2.RTLD_LAZY) return lib2 diff --git a/testing/cffi1/test_dlopen.py b/testing/cffi1/test_dlopen.py --- a/testing/cffi1/test_dlopen.py +++ b/testing/cffi1/test_dlopen.py @@ -6,7 +6,7 @@ def test_simple(): ffi = FFI() - ffi.cdef("int close(int); static const int BB = 42; int somevar;") + ffi.cdef("int close(int); static const int BB = 42; extern int somevar;") target = udir.join('test_simple.py') make_py_source(ffi, 'test_simple', str(target)) assert target.read() == r"""# auto-generated file @@ -196,7 +196,7 @@ def test_global_var(): ffi = FFI() - ffi.cdef("int myglob;") + ffi.cdef("extern int myglob;") target = udir.join('test_global_var.py') make_py_source(ffi, 'test_global_var', str(target)) assert target.read() == r"""# auto-generated file diff --git a/testing/cffi1/test_new_ffi_1.py b/testing/cffi1/test_new_ffi_1.py --- a/testing/cffi1/test_new_ffi_1.py +++ b/testing/cffi1/test_new_ffi_1.py @@ -1779,7 +1779,7 @@ def test_import_from_lib(self): ffi2 = cffi.FFI() - ffi2.cdef("int myfunc(int); int myvar;\n#define MYFOO ...\n") + ffi2.cdef("int myfunc(int); extern int myvar;\n#define MYFOO ...\n") outputfilename = recompile(ffi2, "_test_import_from_lib", "int myfunc(int x) { return x + 1; }\n" "int myvar = -5;\n" diff --git a/testing/cffi1/test_re_python.py b/testing/cffi1/test_re_python.py --- a/testing/cffi1/test_re_python.py +++ b/testing/cffi1/test_re_python.py @@ -63,11 +63,11 @@ #define BIGNEG -420000000000L int add42(int); int add43(int, ...); - int globalvar42; + extern int globalvar42; const int globalconst42; const char *const globalconsthello; int no_such_function(int); - int no_such_globalvar; + extern int no_such_globalvar; struct foo_s; typedef struct bar_s { int x; signed char a[]; } bar_t; enum foo_e { AA, BB, CC }; diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py --- a/testing/cffi1/test_recompiler.py +++ b/testing/cffi1/test_recompiler.py @@ -83,7 +83,7 @@ "(FUNCTION 1)(PRIMITIVE 7)(FUNCTION_END 1)(POINTER 0)") def test_type_table_array(): - check_type_table("int a[100];", + check_type_table("extern int a[100];", "(PRIMITIVE 7)(ARRAY 0)(None 100)") def test_type_table_typedef(): @@ -158,7 +158,7 @@ def test_global_var_array(): ffi = FFI() - ffi.cdef("int a[100];") + ffi.cdef("extern int a[100];") lib = verify(ffi, 'test_global_var_array', 'int a[100] = { 9999 };') lib.a[42] = 123456 assert lib.a[42] == 123456 @@ -182,7 +182,7 @@ def test_global_var_int(): ffi = FFI() - ffi.cdef("int a, b, c;") + ffi.cdef("extern int a, b, c;") lib = verify(ffi, 'test_global_var_int', 'int a = 999, b, c;') assert lib.a == 999 lib.a -= 1001 @@ -283,7 +283,7 @@ def test_dir(): ffi = FFI() - ffi.cdef("int ff(int); int aa; static const int my_constant;") + ffi.cdef("int ff(int); extern int aa; static const int my_constant;") lib = verify(ffi, 'test_dir', """ #define my_constant (-45) int aa; @@ -405,7 +405,7 @@ def test_dotdotdot_global_array(): ffi = FFI() - ffi.cdef("int aa[...]; int bb[...];") + ffi.cdef("extern int aa[...]; extern int bb[...];") lib = verify(ffi, 'test_dotdotdot_global_array', "int aa[41]; int bb[12];") assert ffi.sizeof(lib.aa) == 41 * 4 @@ -560,37 +560,37 @@ def test_bad_size_of_global_1(): ffi = FFI() - ffi.cdef("short glob;") + ffi.cdef("extern short glob;") py.test.raises(VerificationError, verify, ffi, "test_bad_size_of_global_1", "long glob;") def test_bad_size_of_global_2(): ffi = FFI() - ffi.cdef("int glob[10];") + ffi.cdef("extern int glob[10];") py.test.raises(VerificationError, verify, ffi, "test_bad_size_of_global_2", "int glob[9];") def test_unspecified_size_of_global_1(): ffi = FFI() - ffi.cdef("int glob[];") + ffi.cdef("extern int glob[];") lib = verify(ffi, "test_unspecified_size_of_global_1", "int glob[10];") assert ffi.typeof(lib.glob) == ffi.typeof("int *") def test_unspecified_size_of_global_2(): ffi = FFI() - ffi.cdef("int glob[][5];") + ffi.cdef("extern int glob[][5];") lib = verify(ffi, "test_unspecified_size_of_global_2", "int glob[10][5];") assert ffi.typeof(lib.glob) == ffi.typeof("int(*)[5]") def test_unspecified_size_of_global_3(): ffi = FFI() - ffi.cdef("int glob[][...];") + ffi.cdef("extern int glob[][...];") lib = verify(ffi, "test_unspecified_size_of_global_3", "int glob[10][5];") assert ffi.typeof(lib.glob) == ffi.typeof("int(*)[5]") def test_unspecified_size_of_global_4(): ffi = FFI() - ffi.cdef("int glob[...][...];") + ffi.cdef("extern int glob[...][...];") lib = verify(ffi, "test_unspecified_size_of_global_4", "int glob[10][5];") assert ffi.typeof(lib.glob) == ffi.typeof("int[10][5]") @@ -813,7 +813,7 @@ def test_address_of_global_var(): ffi = FFI() ffi.cdef(""" - long bottom, bottoms[2]; + extern long bottom, bottoms[2]; long FetchRectBottom(void); long FetchRectBottoms1(void); #define FOOBAR 42 @@ -968,7 +968,7 @@ ffi = FFI() ffi.cdef(""" typedef ... opaque_t; - opaque_t globvar; + extern opaque_t globvar; """) lib = verify(ffi, 'test_variable_of_unknown_size', """ typedef char opaque_t[6]; @@ -1013,7 +1013,7 @@ def test_call_with_incomplete_structs(): ffi = FFI() ffi.cdef("typedef struct {...;} foo_t; " - "foo_t myglob; " + "extern foo_t myglob; " "foo_t increment(foo_t s); " "double getx(foo_t s);") lib = verify(ffi, 'test_call_with_incomplete_structs', """ @@ -1057,7 +1057,7 @@ def test_global_var_array_2(): ffi = FFI() - ffi.cdef("int a[...][...];") + ffi.cdef("extern int a[...][...];") lib = verify(ffi, 'test_global_var_array_2', 'int a[10][8];') lib.a[9][7] = 123456 assert lib.a[9][7] == 123456 @@ -1070,7 +1070,7 @@ def test_global_var_array_3(): ffi = FFI() - ffi.cdef("int a[][...];") + ffi.cdef("extern int a[][...];") lib = verify(ffi, 'test_global_var_array_3', 'int a[10][8];') lib.a[9][7] = 123456 assert lib.a[9][7] == 123456 @@ -1081,7 +1081,7 @@ def test_global_var_array_4(): ffi = FFI() - ffi.cdef("int a[10][...];") + ffi.cdef("extern int a[10][...];") lib = verify(ffi, 'test_global_var_array_4', 'int a[10][8];') lib.a[9][7] = 123456 assert lib.a[9][7] == 123456 @@ -1204,7 +1204,7 @@ def test_import_from_lib(): ffi = FFI() - ffi.cdef("int mybar(int); int myvar;\n#define MYFOO ...") + ffi.cdef("int mybar(int); static int myvar;\n#define MYFOO ...") lib = verify(ffi, 'test_import_from_lib', "#define MYFOO 42\n" "static int mybar(int x) { return x + 1; }\n" @@ -1220,7 +1220,7 @@ def test_macro_var_callback(): ffi = FFI() - ffi.cdef("int my_value; int *(*get_my_value)(void);") + ffi.cdef("extern int my_value; extern int *(*get_my_value)(void);") lib = verify(ffi, 'test_macro_var_callback', "int *(*get_my_value)(void);\n" "#define my_value (*get_my_value())") @@ -1335,7 +1335,7 @@ def test_const_function_type_args(): ffi = FFI() - ffi.cdef("""int (*foobar)(const int a, const int *b, const int c[]);""") + ffi.cdef("""extern int(*foobar)(const int a,const int*b,const int c[]);""") lib = verify(ffi, 'test_const_function_type_args', """ int (*foobar)(const int a, const int *b, const int c[]); """) @@ -1625,7 +1625,7 @@ def test_extern_python_bogus_name(): ffi = FFI() - ffi.cdef("int abc;") + ffi.cdef("extern int abc;") lib = verify(ffi, 'test_extern_python_bogus_name', "int abc;") def fn(): pass @@ -1786,8 +1786,8 @@ ffi.cdef(""" extern "Python" int __stdcall foo(int); extern "Python" int WINAPI bar(int); - int (__stdcall * mycb1)(int); - int indirect_call(int); + static int (__stdcall * mycb1)(int); + static int indirect_call(int); """) lib = verify(ffi, 'test_extern_python_stdcall', """ #ifndef _MSC_VER @@ -1855,7 +1855,7 @@ def test_introspect_global_var(): ffi = FFI() - ffi.cdef("float g1;") + ffi.cdef("extern float g1;") lib = verify(ffi, 'test_introspect_global_var', """ float g1; """) @@ -1866,7 +1866,7 @@ def test_introspect_global_var_array(): ffi = FFI() - ffi.cdef("float g1[100];") + ffi.cdef("extern float g1[100];") lib = verify(ffi, 'test_introspect_global_var_array', """ float g1[100]; """) @@ -2089,7 +2089,7 @@ ffi = FFI() ffi.cdef(""" typedef int foo_t[...], bar_t[...]; - int gv[...]; + extern int gv[...]; typedef int mat_t[...][...]; typedef int vmat_t[][...]; """) diff --git a/testing/cffi1/test_verify1.py b/testing/cffi1/test_verify1.py --- a/testing/cffi1/test_verify1.py +++ b/testing/cffi1/test_verify1.py @@ -267,7 +267,7 @@ def test_var_signed_integer_types(): ffi = FFI() lst = all_signed_integer_types(ffi) - csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_')) + csource = "\n".join(["static %s somevar_%s;" % (tp, tp.replace(' ', '_')) for tp in lst]) ffi.cdef(csource) lib = ffi.verify(csource) @@ -286,7 +286,7 @@ def test_var_unsigned_integer_types(): ffi = FFI() lst = all_unsigned_integer_types(ffi) - csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_')) + csource = "\n".join(["static %s somevar_%s;" % (tp, tp.replace(' ', '_')) for tp in lst]) ffi.cdef(csource) lib = ffi.verify(csource) @@ -790,8 +790,8 @@ def test_access_variable(): ffi = FFI() - ffi.cdef("int foo(void);\n" - "int somenumber;") + ffi.cdef("static int foo(void);\n" + "static int somenumber;") lib = ffi.verify(""" static int somenumber = 2; static int foo(void) { @@ -808,7 +808,7 @@ def test_access_address_of_variable(): # access the address of 'somenumber': need a trick ffi = FFI() - ffi.cdef("int somenumber; static int *const somenumberptr;") + ffi.cdef("static int somenumber; static int *const somenumberptr;") lib = ffi.verify(""" static int somenumber = 2; #define somenumberptr (&somenumber) @@ -820,8 +820,8 @@ def test_access_array_variable(length=5): ffi = FFI() - ffi.cdef("int foo(int);\n" - "int somenumber[%s];" % (length,)) + ffi.cdef("static int foo(int);\n" + "static int somenumber[%s];" % (length,)) lib = ffi.verify(""" static int somenumber[] = {2, 2, 3, 4, 5}; static int foo(int i) { @@ -852,8 +852,8 @@ def test_access_struct_variable(): ffi = FFI() ffi.cdef("struct foo { int x; ...; };\n" - "int foo(int);\n" - "struct foo stuff;") + "static int foo(int);\n" + "static struct foo stuff;") lib = ffi.verify(""" struct foo { int x, y, z; }; static struct foo stuff = {2, 5, 8}; @@ -877,9 +877,9 @@ def test_access_callback(): ffi = FFI() - ffi.cdef("int (*cb)(int);\n" - "int foo(int);\n" - "void reset_cb(void);") + ffi.cdef("static int (*cb)(int);\n" + "static int foo(int);\n" + "static void reset_cb(void);") lib = ffi.verify(""" static int g(int x) { return x * 7; } static int (*cb)(int); @@ -895,9 +895,9 @@ def test_access_callback_function_typedef(): ffi = FFI() ffi.cdef("typedef int mycallback_t(int);\n" - "mycallback_t *cb;\n" - "int foo(int);\n" - "void reset_cb(void);") + "static mycallback_t *cb;\n" + "static int foo(int);\n" + "static void reset_cb(void);") lib = ffi.verify(""" static int g(int x) { return x * 7; } static int (*cb)(int); @@ -1038,7 +1038,7 @@ def test_autofilled_struct_as_argument_dynamic(): ffi = FFI() ffi.cdef("struct foo_s { long a; ...; };\n" - "int (*foo)(struct foo_s);") + "static int (*foo)(struct foo_s);") lib = ffi.verify(""" struct foo_s { double b; @@ -1047,7 +1047,7 @@ int foo1(struct foo_s s) { return (int)s.a - (int)s.b; } - int (*foo)(struct foo_s s) = &foo1; + static int (*foo)(struct foo_s s) = &foo1; """) e = py.test.raises(NotImplementedError, lib.foo, "?") msg = ("ctype 'struct foo_s' not supported as argument. It is a struct " @@ -1423,7 +1423,7 @@ py.test.skip("_Bool not in MSVC") ffi = FFI() ffi.cdef("struct foo_s { _Bool x; };" - "_Bool foo(_Bool); _Bool (*foop)(_Bool);") + "_Bool foo(_Bool); static _Bool (*foop)(_Bool);") lib = ffi.verify(""" struct foo_s { _Bool x; }; int foo(int arg) { @@ -1432,7 +1432,7 @@ _Bool _foofunc(_Bool x) { return !x; } - _Bool (*foop)(_Bool) = _foofunc; + static _Bool (*foop)(_Bool) = _foofunc; """) p = ffi.new("struct foo_s *") p.x = 1 @@ -1617,7 +1617,7 @@ def test_FILE_stored_explicitly(): ffi = FFI() - ffi.cdef("int myprintf11(const char *, int); FILE *myfile;") + ffi.cdef("int myprintf11(const char *, int); extern FILE *myfile;") lib = ffi.verify(""" #include FILE *myfile; @@ -1643,13 +1643,13 @@ def test_global_array_with_missing_length(): ffi = FFI() - ffi.cdef("int fooarray[];") + ffi.cdef("extern int fooarray[];") lib = ffi.verify("int fooarray[50];") assert repr(lib.fooarray).startswith("" def test_bug_const_char_ptr_array_2(): ffi = FFI() - ffi.cdef("""const int a[];""") + ffi.cdef("""extern const int a[];""") lib = ffi.verify("""const int a[5];""") assert repr(ffi.typeof(lib.a)) == "" def _test_various_calls(force_libffi): cdef_source = """ - int xvalue; - long long ivalue, rvalue; - float fvalue; - double dvalue; - long double Dvalue; + extern int xvalue; + extern long long ivalue, rvalue; + extern float fvalue; + extern double dvalue; + extern long double Dvalue; signed char tf_bb(signed char x, signed char c); unsigned char tf_bB(signed char x, unsigned char c); short tf_bh(signed char x, short c); @@ -2111,7 +2111,7 @@ old = sys.getdlopenflags() try: ffi1 = FFI() - ffi1.cdef("int foo_verify_dlopen_flags_1;") + ffi1.cdef("extern int foo_verify_dlopen_flags_1;") sys.setdlopenflags(ffi1.RTLD_GLOBAL | ffi1.RTLD_NOW) lib1 = ffi1.verify("int foo_verify_dlopen_flags_1;") finally: @@ -2252,7 +2252,7 @@ def test_macro_var(): ffi = FFI() - ffi.cdef("int myarray[50], my_value;") + ffi.cdef("extern int myarray[50], my_value;") lib = ffi.verify(""" int myarray[50]; int *get_my_value(void) { diff --git a/testing/embedding/add_recursive.py b/testing/embedding/add_recursive.py --- a/testing/embedding/add_recursive.py +++ b/testing/embedding/add_recursive.py @@ -3,7 +3,7 @@ ffi = cffi.FFI() ffi.embedding_api(""" - int (*my_callback)(int); + extern int (*my_callback)(int); int add_rec(int, int); """) From pypy.commits at gmail.com Mon Oct 14 05:07:37 2019 From: pypy.commits at gmail.com (arigo) Date: Mon, 14 Oct 2019 02:07:37 -0700 (PDT) Subject: [pypy-commit] pypy fix-descrmismatch-crash: frozenset.__init__() does nothing on CPython, and can be called with random Message-ID: <5da43ad9.1c69fb81.1d381.fa1b@mx.google.com> Author: Armin Rigo Branch: fix-descrmismatch-crash Changeset: r97769:8c25c3e5ab7d Date: 2019-10-14 11:06 +0200 http://bitbucket.org/pypy/pypy/changeset/8c25c3e5ab7d/ Log: frozenset.__init__() does nothing on CPython, and can be called with random arguments. diff --git a/pypy/interpreter/test/test_gateway.py b/pypy/interpreter/test/test_gateway.py --- a/pypy/interpreter/test/test_gateway.py +++ b/pypy/interpreter/test/test_gateway.py @@ -1014,7 +1014,7 @@ def test_fast_path_crash(self): # issue bb-3091 crash in BuiltinCodePassThroughArguments0.funcrun - for obj in (dict, set, frozenset): + for obj in (dict, set): with raises(TypeError) as excinfo: if self.runappdirect: msg_fmt = "'%s' object but received a '%s'" diff --git a/pypy/objspace/std/setobject.py b/pypy/objspace/std/setobject.py --- a/pypy/objspace/std/setobject.py +++ b/pypy/objspace/std/setobject.py @@ -578,11 +578,6 @@ class W_FrozensetObject(W_BaseSetObject): hash = 0 - #overridden here so the error is reported correctly - def __init__(self, space, w_iterable=None): - """Initialize the frozenset by taking ownership of 'setdata'.""" - W_BaseSetObject.__init__(self, space, w_iterable) - def _cleanup_(self): # in case there are frozenset objects existing during # translation, make sure we don't translate a cached hash @@ -650,7 +645,6 @@ Build an immutable unordered collection.""", __new__ = gateway.interp2app(W_FrozensetObject.descr_new2), - __init__ = gateway.interp2app(W_FrozensetObject.descr_init), __repr__ = gateway.interp2app(W_BaseSetObject.descr_repr), __hash__ = gateway.interp2app(W_FrozensetObject.descr_hash), __cmp__ = gateway.interp2app(W_BaseSetObject.descr_cmp), diff --git a/pypy/objspace/std/test/test_setobject.py b/pypy/objspace/std/test/test_setobject.py --- a/pypy/objspace/std/test/test_setobject.py +++ b/pypy/objspace/std/test/test_setobject.py @@ -1037,3 +1037,8 @@ raise ValueError yield 1 raises(ValueError, set, f()) + + def test_frozenset_init_does_nothing(self): + f = frozenset([1, 2, 3]) + f.__init__(4, 5, 6) + assert f == frozenset([1, 2, 3]) From pypy.commits at gmail.com Mon Oct 14 05:25:56 2019 From: pypy.commits at gmail.com (arigo) Date: Mon, 14 Oct 2019 02:25:56 -0700 (PDT) Subject: [pypy-commit] pypy fix-descrmismatch-crash: Make a new helper method for the common code, and move there the logic that Message-ID: <5da43f24.1c69fb81.8c71.851a@mx.google.com> Author: Armin Rigo Branch: fix-descrmismatch-crash Changeset: r97770:9acddd120236 Date: 2019-10-14 11:25 +0200 http://bitbucket.org/pypy/pypy/changeset/9acddd120236/ Log: Make a new helper method for the common code, and move there the logic that should safely fetch the first argument. Includes tests but like the old SystemError these new cases are probably not reachable from app-level diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py --- a/pypy/interpreter/gateway.py +++ b/pypy/interpreter/gateway.py @@ -755,10 +755,7 @@ except DescrMismatch: if w_obj is not None: args = args.prepend(w_obj) - return scope_w[0].descr_call_mismatch(space, - self.descrmismatch_op, - self.descr_reqcls, - args) + return self.descr_call_mismatch(space, args) except Exception as e: self.handle_exception(space, e) w_result = None @@ -766,6 +763,15 @@ w_result = space.w_None return w_result + def descr_call_mismatch(self, space, args): + w_obj = args.firstarg() + if w_obj is None: + raise oefmt(space.w_SystemError, "unexpected DescrMismatch error") + return w_obj.descr_call_mismatch(space, + self.descrmismatch_op, + self.descr_reqcls, + args) + def handle_exception(self, space, e): try: if not we_are_translated(): @@ -788,10 +794,7 @@ try: w_result = self.func__args__(space, args) except DescrMismatch: - return args.firstarg().descr_call_mismatch(space, - self.descrmismatch_op, - self.descr_reqcls, - args) + return self.descr_call_mismatch(space, args) except Exception as e: self.handle_exception(space, e) w_result = None @@ -809,10 +812,7 @@ try: w_result = self.func__args__(space, w_obj, args) except DescrMismatch: - return w_obj.descr_call_mismatch(space, - self.descrmismatch_op, - self.descr_reqcls, - args.prepend(w_obj)) + return self.descr_call_mismatch(space, args.prepend(w_obj)) except Exception as e: self.handle_exception(space, e) w_result = None @@ -852,9 +852,7 @@ try: w_result = self.fastfunc_1(space, w1) except DescrMismatch: - return w1.descr_call_mismatch(space, - self.descrmismatch_op, - self.descr_reqcls, + return self.descr_call_mismatch(space, Arguments(space, [w1])) except Exception as e: self.handle_exception(space, e) @@ -878,9 +876,7 @@ try: w_result = self.fastfunc_2(space, w1, w2) except DescrMismatch: - return w1.descr_call_mismatch(space, - self.descrmismatch_op, - self.descr_reqcls, + return self.descr_call_mismatch(space, Arguments(space, [w1, w2])) except Exception as e: self.handle_exception(space, e) @@ -905,9 +901,7 @@ try: w_result = self.fastfunc_3(space, w1, w2, w3) except DescrMismatch: - return w1.descr_call_mismatch(space, - self.descrmismatch_op, - self.descr_reqcls, + return self.descr_call_mismatch(space, Arguments(space, [w1, w2, w3])) except Exception as e: self.handle_exception(space, e) @@ -933,9 +927,7 @@ try: w_result = self.fastfunc_4(space, w1, w2, w3, w4) except DescrMismatch: - return w1.descr_call_mismatch(space, - self.descrmismatch_op, - self.descr_reqcls, + return self.descr_call_mismatch(space, Arguments(space, [w1, w2, w3, w4])) except Exception as e: diff --git a/pypy/interpreter/test/test_gateway.py b/pypy/interpreter/test/test_gateway.py --- a/pypy/interpreter/test/test_gateway.py +++ b/pypy/interpreter/test/test_gateway.py @@ -966,6 +966,29 @@ # white-box check for opt assert called[0] is args + def test_base_regular_descr_mismatch(self): + space = self.space + + def f(): + raise gateway.DescrMismatch + + w_f = space.wrap(gateway.interp2app_temp(f, + unwrap_spec=[])) + args = argument.Arguments(space, []) + space.raises_w(space.w_SystemError, space.call_args, w_f, args) + + def test_pass_trough_arguments0_descr_mismatch(self): + space = self.space + + def f(space, __args__): + raise gateway.DescrMismatch + + w_f = space.wrap(gateway.interp2app_temp(f, + unwrap_spec=[gateway.ObjSpace, + gateway.Arguments])) + args = argument.Arguments(space, []) + space.raises_w(space.w_SystemError, space.call_args, w_f, args) + class AppTestKeywordsToBuiltinSanity(object): def test_type(self): From pypy.commits at gmail.com Mon Oct 14 05:41:03 2019 From: pypy.commits at gmail.com (arigo) Date: Mon, 14 Oct 2019 02:41:03 -0700 (PDT) Subject: [pypy-commit] pypy fix-descrmismatch-crash: Improve the error message by using the class name 'set-or-frozenset' Message-ID: <5da442af.1c69fb81.4bdfc.3d9a@mx.google.com> Author: Armin Rigo Branch: fix-descrmismatch-crash Changeset: r97771:78855fca6aa9 Date: 2019-10-14 11:40 +0200 http://bitbucket.org/pypy/pypy/changeset/78855fca6aa9/ Log: Improve the error message by using the class name 'set-or-frozenset' diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -419,6 +419,8 @@ @specialize.memo() def wrappable_class_name(Class): + if 'exact_class_applevel_name' in Class.__dict__: + return Class.exact_class_applevel_name try: return Class.typedef.name except AttributeError: diff --git a/pypy/objspace/std/setobject.py b/pypy/objspace/std/setobject.py --- a/pypy/objspace/std/setobject.py +++ b/pypy/objspace/std/setobject.py @@ -20,6 +20,7 @@ class W_BaseSetObject(W_Root): typedef = None + exact_class_applevel_name = 'set-or-frozenset' def __init__(self, space, w_iterable=None): """Initialize the set by taking ownership of 'setdata'.""" diff --git a/pypy/objspace/std/test/test_setobject.py b/pypy/objspace/std/test/test_setobject.py --- a/pypy/objspace/std/test/test_setobject.py +++ b/pypy/objspace/std/test/test_setobject.py @@ -1042,3 +1042,10 @@ f = frozenset([1, 2, 3]) f.__init__(4, 5, 6) assert f == frozenset([1, 2, 3]) + + def test_error_message_wrong_self(self): + e = raises(TypeError, frozenset.copy, 42) + assert "frozenset" in str(e.value) + if hasattr(frozenset.copy, 'im_func'): + e = raises(TypeError, frozenset.copy.im_func, 42) + assert "'set-or-frozenset'" in str(e.value) From pypy.commits at gmail.com Mon Oct 14 05:51:32 2019 From: pypy.commits at gmail.com (arigo) Date: Mon, 14 Oct 2019 02:51:32 -0700 (PDT) Subject: [pypy-commit] pypy fix-descrmismatch-crash: fix the error message also for W_AbstractBytesObject Message-ID: <5da44524.1c69fb81.23669.fdd8@mx.google.com> Author: Armin Rigo Branch: fix-descrmismatch-crash Changeset: r97772:c13fae180ec6 Date: 2019-10-14 11:45 +0200 http://bitbucket.org/pypy/pypy/changeset/c13fae180ec6/ Log: fix the error message also for W_AbstractBytesObject 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 @@ -24,6 +24,7 @@ class W_AbstractBytesObject(W_Root): __slots__ = () + exact_class_applevel_name = 'str' def is_w(self, space, w_other): if not isinstance(w_other, W_AbstractBytesObject): 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 @@ -933,3 +933,10 @@ def test_add(self): assert 'abc' + 'abc' == 'abcabc' assert isinstance('abc' + u'\u03a3', unicode) + + def test_error_message_wrong_self(self): + e = raises(TypeError, bytes.upper, 42) + assert "str" in str(e.value) + if hasattr(bytes.upper, 'im_func'): + e = raises(TypeError, bytes.upper.im_func, 42) + assert "'str'" in str(e.value) From pypy.commits at gmail.com Mon Oct 14 05:51:34 2019 From: pypy.commits at gmail.com (arigo) Date: Mon, 14 Oct 2019 02:51:34 -0700 (PDT) Subject: [pypy-commit] pypy fix-descrmismatch-crash: same fix for weakref-or-proxy Message-ID: <5da44526.1c69fb81.4a165.22c9@mx.google.com> Author: Armin Rigo Branch: fix-descrmismatch-crash Changeset: r97773:cc73504e9f94 Date: 2019-10-14 11:48 +0200 http://bitbucket.org/pypy/pypy/changeset/cc73504e9f94/ Log: same fix for weakref-or-proxy 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 @@ -156,6 +156,8 @@ class W_WeakrefBase(W_Root): + exact_class_applevel_name = 'weakref-or-proxy' + def __init__(self, space, w_obj, w_callable): assert w_callable is not space.w_None # should be really None self.space = space 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 @@ -543,3 +543,12 @@ p1[42] = p2 assert a1.setkey == 42 assert a1.setvalue is p2 + + def test_error_message_wrong_self(self): + import _weakref + unboundmeth = _weakref.ref.__repr__ + e = raises(TypeError, unboundmeth, 42) + assert "weakref" in str(e.value) + if hasattr(unboundmeth, 'im_func'): + e = raises(TypeError, unboundmeth.im_func, 42) + assert "'weakref-or-proxy'" in str(e.value) From pypy.commits at gmail.com Mon Oct 14 05:51:36 2019 From: pypy.commits at gmail.com (arigo) Date: Mon, 14 Oct 2019 02:51:36 -0700 (PDT) Subject: [pypy-commit] pypy fix-descrmismatch-crash: Same fix for cStringIO Message-ID: <5da44528.1c69fb81.a67ba.cf65@mx.google.com> Author: Armin Rigo Branch: fix-descrmismatch-crash Changeset: r97774:56cde2db6b50 Date: 2019-10-14 11:50 +0200 http://bitbucket.org/pypy/pypy/changeset/56cde2db6b50/ Log: Same fix for cStringIO diff --git a/pypy/module/cStringIO/interp_stringio.py b/pypy/module/cStringIO/interp_stringio.py --- a/pypy/module/cStringIO/interp_stringio.py +++ b/pypy/module/cStringIO/interp_stringio.py @@ -7,6 +7,8 @@ class W_InputOutputType(W_Root): + exact_class_applevel_name = "StringI-or-StringO" + softspace = 0 # part of the file object API def descr___iter__(self): diff --git a/pypy/module/cStringIO/test/test_interp_stringio.py b/pypy/module/cStringIO/test/test_interp_stringio.py --- a/pypy/module/cStringIO/test/test_interp_stringio.py +++ b/pypy/module/cStringIO/test/test_interp_stringio.py @@ -204,3 +204,12 @@ import cStringIO assert type(cStringIO.StringIO()) is cStringIO.OutputType assert type(cStringIO.StringIO('')) is cStringIO.InputType + + def test_error_message_wrong_self(self): + import cStringIO + unboundmeth = cStringIO.InputType.close + e = raises(TypeError, unboundmeth, 42) + assert "StringI" in str(e.value) + if hasattr(unboundmeth, 'im_func'): + e = raises(TypeError, unboundmeth.im_func, 42) + assert "'StringI-or-StringO'" in str(e.value) From pypy.commits at gmail.com Mon Oct 14 06:00:42 2019 From: pypy.commits at gmail.com (arigo) Date: Mon, 14 Oct 2019 03:00:42 -0700 (PDT) Subject: [pypy-commit] pypy fix-descrmismatch-crash: Some more cases of W_AbstractXxx but where the test already passes Message-ID: <5da4474a.1c69fb81.6c584.4bd1@mx.google.com> Author: Armin Rigo Branch: fix-descrmismatch-crash Changeset: r97775:c2572a2d1270 Date: 2019-10-14 11:59 +0200 http://bitbucket.org/pypy/pypy/changeset/c2572a2d1270/ Log: Some more cases of W_AbstractXxx but where the test already passes diff --git a/pypy/objspace/std/test/test_intobject.py b/pypy/objspace/std/test/test_intobject.py --- a/pypy/objspace/std/test/test_intobject.py +++ b/pypy/objspace/std/test/test_intobject.py @@ -684,6 +684,15 @@ x = int(-sys.maxint) assert x.__rsub__(2) == (2 + sys.maxint) + def test_error_message_wrong_self(self): + unboundmeth = int.__str__ + e = raises(TypeError, unboundmeth, "!") + assert "int" in str(e.value) + if hasattr(unboundmeth, 'im_func'): + e = raises(TypeError, unboundmeth.im_func, "!") + assert "'int'" in str(e.value) + + class AppTestIntShortcut(AppTestInt): spaceconfig = {"objspace.std.intshortcut": True} diff --git a/pypy/objspace/std/test/test_longobject.py b/pypy/objspace/std/test/test_longobject.py --- a/pypy/objspace/std/test/test_longobject.py +++ b/pypy/objspace/std/test/test_longobject.py @@ -450,3 +450,10 @@ expected = (2 << (size * 4)) // 3 assert long(n, 16) == expected + def test_error_message_wrong_self(self): + unboundmeth = long.__str__ + e = raises(TypeError, unboundmeth, 42) + assert "long" in str(e.value) + if hasattr(unboundmeth, 'im_func'): + e = raises(TypeError, unboundmeth.im_func, 42) + assert "'long'" in str(e.value) diff --git a/pypy/objspace/std/test/test_tupleobject.py b/pypy/objspace/std/test/test_tupleobject.py --- a/pypy/objspace/std/test/test_tupleobject.py +++ b/pypy/objspace/std/test/test_tupleobject.py @@ -454,3 +454,11 @@ (4.1, 2.3), (3.6, 4.8)] assert specialized_zip_2_lists(["foo", "bar"], [6, 2]) == [ ("foo", 6), ("bar", 2)] + + def test_error_message_wrong_self(self): + unboundmeth = tuple.__hash__ + e = raises(TypeError, unboundmeth, 42) + assert "tuple" in str(e.value) + if hasattr(unboundmeth, 'im_func'): + e = raises(TypeError, unboundmeth.im_func, 42) + assert "'tuple'" in str(e.value) From pypy.commits at gmail.com Mon Oct 14 06:04:46 2019 From: pypy.commits at gmail.com (arigo) Date: Mon, 14 Oct 2019 03:04:46 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: the merge of 'fix-descrmismatch-crash' into 'py3.6' will need this Message-ID: <5da4483e.1c69fb81.5a1eb.3bbf@mx.google.com> Author: Armin Rigo Branch: py3.6 Changeset: r97776:3c57a384aae3 Date: 2019-10-14 12:04 +0200 http://bitbucket.org/pypy/pypy/changeset/3c57a384aae3/ Log: the merge of 'fix-descrmismatch-crash' into 'py3.6' will need this line, not 'str' 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 @@ -19,6 +19,7 @@ class W_AbstractBytesObject(W_Root): __slots__ = () + exact_class_applevel_name = 'bytes' def is_w(self, space, w_other): if not isinstance(w_other, W_AbstractBytesObject): From pypy.commits at gmail.com Mon Oct 14 12:11:10 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 14 Oct 2019 09:11:10 -0700 (PDT) Subject: [pypy-commit] pypy default: Added tags for release-pypy2.7 Message-ID: <5da49e1e.1c69fb81.a4845.7e74@mx.google.com> Author: Matti Picus Branch: Changeset: r97777:c31b3fcf395f Date: 2019-10-14 19:09 +0300 http://bitbucket.org/pypy/pypy/changeset/c31b3fcf395f/ Log: Added tags for release-pypy2.7 diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -55,3 +55,5 @@ 4d6761df14ffd6f38450f183ac1fad32c946c21b release-pypy3.6-v7.2.0rc1 5da45ced70e515f94686be0df47c59abd1348ebc release-pypy3.6-v7.2.0rc2 4a68d8d3d2fc1faec2e83bcb4d28559099092574 release-pypy2.7-v7.2.0rc2 +4a68d8d3d2fc1faec2e83bcb4d28559099092574 release-pypy2.7-v7.2.0 +5da45ced70e515f94686be0df47c59abd1348ebc release-pypy3.6-v7.2.0 From pypy.commits at gmail.com Mon Oct 14 13:05:27 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 14 Oct 2019 10:05:27 -0700 (PDT) Subject: [pypy-commit] pypy.org extradoc: update to 3.6.9 and update downloads for 7.2.0 release (ppc links commented out) Message-ID: <5da4aad7.1c69fb81.1ad62.88c1@mx.google.com> Author: Matti Picus Branch: extradoc Changeset: r954:4620c2cbcf08 Date: 2019-10-14 20:04 +0300 http://bitbucket.org/pypy/pypy.org/changeset/4620c2cbcf08/ Log: update to 3.6.9 and update downloads for 7.2.0 release (ppc links commented out) diff --git a/compat.html b/compat.html --- a/compat.html +++ b/compat.html @@ -66,7 +66,7 @@ language, passing Python test suite (with minor modifications that were already accepted in the main python in newer versions). It supports most of the commonly used Python standard library modules; details below.

    -

    PyPy3 implements the Python language version 3.5.3. It has been released, +

    PyPy3 implements the Python language version 3.6.9. It has been released, but Python is a large language and it is quite possible that a few things are missing.

    List of installable top 1000 PyPI packages

    PyPy has support for the CPython C API, however there are constructs diff --git a/download.html b/download.html --- a/download.html +++ b/download.html @@ -66,11 +66,10 @@ as stable as the release, but they contain numerous bugfixes and performance improvements.

    We provide binaries for x86, ARM, PPC and s390x running on different operating systems such as -Linux, Mac OS X and Windows (what's new in PyPy 7.1.1?):

    +Linux, Mac OS X and Windows (what's new in PyPy 7.2.0?):

      -
    • the Python2.7 compatible release — PyPy2.7 v7.1.1
    • -
    • the Python3.6 compatible release, beta quality — PyPy3.6 v7.1.1
    • -
    • the (older) Python3.5 compatible release — PyPy3.5 v7.0
    • +
    • the Python2.7 compatible release — PyPy2.7 v7.2.0
    • +
    • the Python3.6 compatible release — PyPy3.6 v7.2.0
    • the Python2.7 Software Transactional Memory special release — PyPy-STM 2.5.1 (Linux x86-64 only)
      @@ -108,55 +107,35 @@
    • or translate your own PyPy.
    -
    -

    Python2.7 compatible PyPy 7.1.1

    +
    +

    Python2.7 compatible PyPy 7.2.0

    -
    -
    -

    Python 3.6 compatible PyPy3.6 v7.1.1-beta

    +
    +
    +

    Python 3.6 compatible PyPy3.6 v7.2.0

    -
    -
    -

    Python 3.5.3 compatible PyPy3.5 v7.0.0

    - -

    If your CPU is really, really old, it may be a x86-32 without SSE2. +

    If your CPU is really, really old, it may be a x86-32 without SSE2. There is untested support for manually translating PyPy's JIT without SSE2 (--jit-backend=x86-without-sse2) but note that your machine is probably low-spec enough that running CPython on it is a better @@ -229,14 +208,14 @@

    The trunk contains PyPy 2. For PyPy 3, switch to the correct branch:

    -# for PyPy 3: switch to the branch of PyPy that implements Python 3.5
    -hg update py3.5
    +# for PyPy 3: switch to the branch of PyPy that implements Python 3.6
    +hg update py3.6
     

    Alternatively, get one of the following smaller packages for the source at the same revision as the above binaries:

  • Make sure you installed the dependencies. See the list here.

    @@ -350,7 +329,18 @@
  • Checksums

    -

    Here are the checksums for each of the downloads of PyPy 7.1.1, 7.1.0, 7.0.0

    +

    Here are the checksums for each of the downloads of PyPy 7.2.0, 7.1.1, 7.1.0

    +

    pypy2.7-7.2.0 sha256:

    +
    +57b0be053c6a5f069e23b843f38863cf7920f5eef7bc89f2e086e5c3a28a2ba9  pypy2.7-v7.2.0-aarch64.tar.bz2
    +76d666e5aee54b519d6ec1af4ef0cbdc85f7f9276dd554e97deb026adfd0c936  pypy2.7-v7.2.0-linux32.tar.bz2
    +05acf28e6a243026ecad933b9361d8f74b41f00818071b76b38c4694cc4c9599  pypy2.7-v7.2.0-linux64.tar.bz2
    +36aa2f2440e762333569118dd0b3d5371d575c40966effa194d116c5453ddb52  pypy2.7-v7.2.0-osx64.tar.bz2
    +bb7ae585ecb4d904c890e28a2c5b6bd379f57cc3d9e38ff45597ff54fa935eaa  pypy2.7-v7.2.0-s390x.tar.bz2
    +55cb7757784fbe3952102447f65b27d80e6c885a464a7af1a9ce264492439dcc  pypy2.7-v7.2.0-src.tar.bz2
    +897038550614d558f9f6718409b107e27903ef2b2b57ec250939d1b1ebdf0aba  pypy2.7-v7.2.0-src.zip
    +956eeaaaac053e5d0917e77a3d2ad1933ab5561eb3e6e71235780b5aa5fd2bb7  pypy2.7-v7.2.0-win32.zip
    +

    pypy2.7-7.1.1 sha256:

     41ca390a76ca0d47b8353a0d6a20d5aab5fad8b0bb647b960d8c33e873d18ef5  pypy2.7-v7.1.1-linux32.tar.bz2
    @@ -371,6 +361,17 @@
     e60ce30f9947844da43daaa7658adc0c05330681305225954114772f42df06ec  pypy2.7-v7.1.0-src.zip
     76658c9ad679d562b8b6a09d006caa666406337b9834ff56db16980c5e549f20  pypy2.7-v7.1.0-win32.zip
     
    +

    pypy3.6-7.2.0 sha256:

    +
    +f82dc9dc6c692417ee9727f23beae75364a5757ebdc657a2a1d0010ac3ad17ab  pypy3.6-v7.2.0-aarch64.tar.bz2
    +45e99de197cb3e974cfc8d45e0076ad2066852e61e56b3eafd1237efafd2c43e  pypy3.6-v7.2.0-linux32.tar.bz2
    +aa128e555ad0fe5c4c15104ae0903052bd232b6e3a73f5fe023d27b8fd0d6089  pypy3.6-v7.2.0-linux64.tar.bz2
    +836abb0ec303b90a684533711ed3b8269d3e8c64805b595e410920abdea678ac  pypy3.6-v7.2.0-osx64.tar.bz2
    +a11da8118064db102d159e9221319c428b298c4a87f26166fd6ae94be8d6ae0d  pypy3.6-v7.2.0-s390x.tar.bz2
    +0d7c707df5041f1593fe82f29c40056c21e4d6cb66554bbd66769bd80bcbfafc  pypy3.6-v7.2.0-src.tar.bz2
    +405ac35695dd374d5ea192cb44cb47231f9a65812cc7b6549df33df12ffe54db  pypy3.6-v7.2.0-src.zip
    +c926f622bec24a8b348591d631717ace83b3a6c3c2dac02b157b622b97d1fc9c  pypy3.6-v7.2.0-win32.zip
    +

    pypy3.6-7.1.1 sha256:

     cb11ef4b0df569c28390b1ee93029159e1b90bfbad98df6abd629d5203b2abd9  pypy3.6-v7.1.1-linux32.tar.bz2
    @@ -403,18 +404,6 @@
     f51d8bbfc4e73a8a01820b7871a45d13c59f1399822cdf8a19388c69eb20c18c  pypy2.7-v7.0.0-src.tar.bz2
     77c8c02cf412a5f8182ffe8845877cffa506e5a5ce3a7cd835483fdc1202afd4  pypy2.7-v7.0.0-src.zip
     
    -

    pypy 3.5-v7.0.0 sha256:

    -
    -b8db8fbca9621de8ea8cd7184b322f2dddb2f385e8e5a63dfb75bb3fea4b2e3f  pypy3.5-v7.0.0-linux32.tar.bz2
    -729e3c54325969c98bd3658c6342b9f5987b96bad1d6def04250a08401b54c4b  pypy3.5-v7.0.0-linux64.tar.bz2
    -7c6d71653d9b1a7946d1eeebbf24b454fe934fba8b0c39f648bdc545fb2895ce  pypy3.5-v7.0.0-osx64.tar.bz2
    -d588b045cc0d3a75c31fce54c1d181b1206ad9a5dd272fe79160a6268401605f  pypy3.5-v7.0.0-s390x.tar.bz2
    -23e30b00ab61f24578059e4643fbf0221982faffd874898b5737fc5b334ca0ab  pypy3.5-v7.0.0-ppc64.tar.bz2
    -2912884da05abc2cdf71dd337c3f280095351312c1a1732a52b6878174a0fd02  pypy3.5-v7.0.0-ppc64le.tar.bz2
    -TODO win32
    -b2ddb0f45cb4e0384fb498ef7fcca2ac96c730b9000affcf8d730169397f017f  pypy3.5-v7.0.0-src.tar.bz2
    -3aa3a921c163667761165dbd2070e56d6715979fe9cc1f135d58ea0692a05a1e  pypy3.5-v7.0.0-src.zip
    -