diff options
Diffstat (limited to 'pypy/module')
38 files changed, 360 insertions, 71 deletions
diff --git a/pypy/module/__builtin__/descriptor.py b/pypy/module/__builtin__/descriptor.py index dbad647609..6c51fa3d27 100644 --- a/pypy/module/__builtin__/descriptor.py +++ b/pypy/module/__builtin__/descriptor.py @@ -96,6 +96,8 @@ class C(B): ) class W_Property(Wrappable): + _immutable_fields_ = ["w_fget", "w_fset", "w_fdel"] + def init(self, space, w_fget=None, w_fset=None, w_fdel=None, w_doc=None): self.w_fget = w_fget self.w_fset = w_fset @@ -183,4 +185,3 @@ class C(object): fget = interp_attrproperty_w('w_fget', W_Property), fset = interp_attrproperty_w('w_fset', W_Property), ) - diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py index 3b1d69e2fe..c3fe0ce594 100644 --- a/pypy/module/__pypy__/__init__.py +++ b/pypy/module/__pypy__/__init__.py @@ -23,5 +23,12 @@ class Module(MixedModule): 'interp_magic.method_cache_counter') self.extra_interpdef('reset_method_cache_counter', 'interp_magic.reset_method_cache_counter') + if self.space.config.objspace.std.withmapdict: + self.extra_interpdef('mapdict_cache_counter', + 'interp_magic.mapdict_cache_counter') PYC_MAGIC = get_pyc_magic(self.space) self.extra_interpdef('PYC_MAGIC', 'space.wrap(%d)' % PYC_MAGIC) + # + from pypy.jit.backend import detect_cpu + model = detect_cpu.autodetect_main_model_and_size() + self.extra_interpdef('cpumodel', 'space.wrap(%r)' % model) diff --git a/pypy/module/__pypy__/interp_magic.py b/pypy/module/__pypy__/interp_magic.py index 71a7c89eba..0dafafe546 100644 --- a/pypy/module/__pypy__/interp_magic.py +++ b/pypy/module/__pypy__/interp_magic.py @@ -2,6 +2,7 @@ from pypy.interpreter.error import OperationError from pypy.interpreter.gateway import ObjSpace from pypy.rlib.objectmodel import we_are_translated from pypy.objspace.std.typeobject import MethodCache +from pypy.objspace.std.mapdict import IndexCache def internal_repr(space, w_object): return space.wrap('%r' % (w_object,)) @@ -36,4 +37,17 @@ def reset_method_cache_counter(space): cache = space.fromcache(MethodCache) cache.misses = {} cache.hits = {} + if space.config.objspace.std.withmapdict: + cache = space.fromcache(IndexCache) + cache.misses = {} + cache.hits = {} +def mapdict_cache_counter(space, name): + """Return a tuple (index_cache_hits, index_cache_misses) for lookups + in the mapdict cache with the given attribute name.""" + assert space.config.objspace.std.withmethodcachecounter + assert space.config.objspace.std.withmapdict + cache = space.fromcache(IndexCache) + return space.newtuple([space.newint(cache.hits.get(name, 0)), + space.newint(cache.misses.get(name, 0))]) +mapdict_cache_counter.unwrap_spec = [ObjSpace, str] diff --git a/pypy/module/__pypy__/test/test_special.py b/pypy/module/__pypy__/test/test_special.py index 516247822d..af0cd80210 100644 --- a/pypy/module/__pypy__/test/test_special.py +++ b/pypy/module/__pypy__/test/test_special.py @@ -17,3 +17,7 @@ class AppTest(object): from __pypy__ import isfake import select assert isfake(select) + + def test_cpumodel(self): + import __pypy__ + assert hasattr(__pypy__, 'cpumodel') diff --git a/pypy/module/_rawffi/test/test__rawffi.py b/pypy/module/_rawffi/test/test__rawffi.py index a232b9422e..81d60ec085 100644 --- a/pypy/module/_rawffi/test/test__rawffi.py +++ b/pypy/module/_rawffi/test/test__rawffi.py @@ -296,6 +296,7 @@ class AppTestFfi: assert _rawffi.charp2string(res[0]) is None arg1.free() arg2.free() + a.free() def test_raw_callable(self): import _rawffi diff --git a/pypy/module/_rawffi/test/test_nested.py b/pypy/module/_rawffi/test/test_nested.py index 077dfe3f78..e0bbd87a84 100644 --- a/pypy/module/_rawffi/test/test_nested.py +++ b/pypy/module/_rawffi/test/test_nested.py @@ -107,7 +107,6 @@ class AppTestNested: assert S.fieldoffset('x') == 0 assert S.fieldoffset('ar') == A5alignment s = S() - s = S() s.x = 'G' raises(TypeError, 's.ar') assert s.fieldaddress('ar') == s.buffer + S.fieldoffset('ar') diff --git a/pypy/module/_stackless/test/conftest.py b/pypy/module/_stackless/test/conftest.py new file mode 100644 index 0000000000..1c5e26a206 --- /dev/null +++ b/pypy/module/_stackless/test/conftest.py @@ -0,0 +1,7 @@ +import sys +import py.test + +def pytest_runtest_setup(item): + if sys.platform == 'win32': + py.test.skip("stackless tests segfault on Windows") + diff --git a/pypy/module/array/interp_array.py b/pypy/module/array/interp_array.py index 2c81b7fcff..ff62c17bee 100644 --- a/pypy/module/array/interp_array.py +++ b/pypy/module/array/interp_array.py @@ -27,7 +27,7 @@ def w_array(space, w_cls, typecode, w_args=None): typecode = typecode[0] if space.is_w(w_cls, space.gettypeobject(W_ArrayBase.typedef)): - if len(w_args.keywords_w) > 0: + if w_args.keywords: # XXX this might be forbidden fishing msg = 'array.array() does not take keyword arguments' raise OperationError(space.w_TypeError, space.wrap(msg)) diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py index fc1577c7fa..7805d6f3e2 100644 --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -226,7 +226,7 @@ def cpython_api(argtypes, restype, error=_NOT_SPECIFIED, external=True): def unwrapper(space, *args): from pypy.module.cpyext.pyobject import Py_DecRef from pypy.module.cpyext.pyobject import make_ref, from_ref - from pypy.module.cpyext.pyobject import BorrowPair + from pypy.module.cpyext.pyobject import Reference newargs = () to_decref = [] assert len(args) == len(api_function.argtypes) @@ -270,8 +270,8 @@ def cpython_api(argtypes, restype, error=_NOT_SPECIFIED, external=True): return api_function.error_value if res is None: return None - elif isinstance(res, BorrowPair): - return res.w_borrowed + elif isinstance(res, Reference): + return res.get_wrapped(space) else: return res finally: @@ -473,7 +473,7 @@ def make_wrapper(space, callable): @specialize.ll() def wrapper(*args): from pypy.module.cpyext.pyobject import make_ref, from_ref - from pypy.module.cpyext.pyobject import BorrowPair + from pypy.module.cpyext.pyobject import Reference # we hope that malloc removal removes the newtuple() that is # inserted exactly here by the varargs specializer llop.gc_stack_bottom(lltype.Void) # marker for trackgcroot.py @@ -525,7 +525,7 @@ def make_wrapper(space, callable): elif is_PyObject(callable.api_func.restype): if result is None: retval = make_ref(space, None) - elif isinstance(result, BorrowPair): + elif isinstance(result, Reference): retval = result.get_ref(space) elif not rffi._isllptr(result): retval = rffi.cast(callable.api_func.restype, @@ -908,8 +908,10 @@ def load_extension_module(space, path, name): from pypy.rlib import rdynload try: ll_libname = rffi.str2charp(path) - dll = rdynload.dlopen(ll_libname) - lltype.free(ll_libname, flavor='raw') + try: + dll = rdynload.dlopen(ll_libname) + finally: + lltype.free(ll_libname, flavor='raw') except rdynload.DLOpenError, e: raise operationerrfmt( space.w_ImportError, diff --git a/pypy/module/cpyext/cdatetime.py b/pypy/module/cpyext/cdatetime.py index 3081233375..6aa54f695e 100644 --- a/pypy/module/cpyext/cdatetime.py +++ b/pypy/module/cpyext/cdatetime.py @@ -4,7 +4,7 @@ from pypy.module.cpyext.pyobject import PyObject, make_ref, Py_DecRef from pypy.module.cpyext.api import ( cpython_api, CANNOT_FAIL, cpython_struct, PyObjectFields) from pypy.module.cpyext.import_ import PyImport_Import -from pypy.module.cpyext.typeobject import PyTypeObjectPtr +from pypy.module.cpyext.typeobject import PyTypeObjectPtr, render_immortal from pypy.module.cpyext.state import State from pypy.interpreter.error import OperationError from pypy.tool.sourcetools import func_renamer @@ -22,25 +22,34 @@ PyDateTime_CAPI = cpython_struct( @cpython_api([], lltype.Ptr(PyDateTime_CAPI), error=lltype.nullptr(PyDateTime_CAPI)) def _PyDateTime_Import(space): - datetimeAPI = lltype.malloc(PyDateTime_CAPI, flavor='raw') + datetimeAPI = lltype.malloc(PyDateTime_CAPI, flavor='raw', + track_allocation=False) if not we_are_translated(): datetimeAPI_dealloc(space) space.fromcache(State).datetimeAPI = datetimeAPI w_datetime = PyImport_Import(space, space.wrap("datetime")) + w_type = space.getattr(w_datetime, space.wrap("date")) datetimeAPI.c_DateType = rffi.cast( PyTypeObjectPtr, make_ref(space, w_type)) + render_immortal(datetimeAPI.c_DateType, w_type) + w_type = space.getattr(w_datetime, space.wrap("datetime")) datetimeAPI.c_DateTimeType = rffi.cast( PyTypeObjectPtr, make_ref(space, w_type)) + render_immortal(datetimeAPI.c_DateTimeType, w_type) + w_type = space.getattr(w_datetime, space.wrap("time")) datetimeAPI.c_TimeType = rffi.cast( PyTypeObjectPtr, make_ref(space, w_type)) + render_immortal(datetimeAPI.c_TimeType, w_type) + w_type = space.getattr(w_datetime, space.wrap("timedelta")) datetimeAPI.c_DeltaType = rffi.cast( PyTypeObjectPtr, make_ref(space, w_type)) + render_immortal(datetimeAPI.c_DeltaType, w_type) return datetimeAPI diff --git a/pypy/module/cpyext/presetup.py b/pypy/module/cpyext/presetup.py index 5a1605f048..16c205ad79 100644 --- a/pypy/module/cpyext/presetup.py +++ b/pypy/module/cpyext/presetup.py @@ -21,6 +21,8 @@ from distutils import sysconfig from pypy.conftest import gettestobjspace from pypy.module.cpyext.api import build_bridge +from pypy.module.imp.importing import get_so_extension + usemodules = ['cpyext', 'thread'] if sys.platform == 'win32': usemodules.append('_winreg') # necessary in distutils @@ -35,6 +37,7 @@ def get_python_inc(plat_specific=0, prefix=None): def patch_distutils(): sysconfig.get_python_inc = get_python_inc + sysconfig.get_config_vars()['SO'] = get_so_extension(space) patch_distutils() diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py index e596398e5c..a0372dd475 100644 --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -424,7 +424,18 @@ def make_borrowed_ref(space, w_container, w_borrowed): state = space.fromcache(RefcountState) return state.make_borrowed(w_container, w_borrowed) -class BorrowPair: +class Reference: + def __init__(self, pyobj): + assert not isinstance(pyobj, W_Root) + self.pyobj = pyobj + + def get_ref(self, space): + return self.pyobj + + def get_wrapped(self, space): + return from_ref(space, self.pyobj) + +class BorrowPair(Reference): """ Delays the creation of a borrowed reference. """ @@ -435,6 +446,9 @@ class BorrowPair: def get_ref(self, space): return make_borrowed_ref(space, self.w_container, self.w_borrowed) + def get_wrapped(self, space): + return self.w_borrowed + def borrow_from(container, borrowed): return BorrowPair(container, borrowed) diff --git a/pypy/module/cpyext/test/test_borrow.py b/pypy/module/cpyext/test/test_borrow.py index 30fc28f4b5..e02a39854d 100644 --- a/pypy/module/cpyext/test/test_borrow.py +++ b/pypy/module/cpyext/test/test_borrow.py @@ -31,6 +31,7 @@ class AppTestBorrow(AppTestCpythonExtensionBase): g = PyTuple_GetItem(t, 0); // borrows reference again printf("Refcnt4: %i\\n", f->ob_refcnt); printf("COMPARE: %i\\n", f == g); + fflush(stdout); Py_DECREF(t); Py_RETURN_TRUE; """), diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py index 60e993c6a5..e27435c23b 100644 --- a/pypy/module/cpyext/test/test_cpyext.py +++ b/pypy/module/cpyext/test/test_cpyext.py @@ -42,7 +42,7 @@ class AppTestApi: raises(ImportError, cpyext.load_module, "missing.file", "foo") raises(ImportError, cpyext.load_module, self.libc, "invalid.function") -def compile_module(modname, **kwds): +def compile_module(space, modname, **kwds): """ Build an extension module and return the filename of the resulting native code file. @@ -65,10 +65,8 @@ def compile_module(modname, **kwds): [], eci, outputfilename=str(dirname/modname), standalone=False) - if sys.platform == 'win32': - pydname = soname.new(purebasename=modname, ext='.pyd') - else: - pydname = soname.new(purebasename=modname, ext='.so') + from pypy.module.imp.importing import get_so_extension + pydname = soname.new(purebasename=modname, ext=get_so_extension(space)) soname.rename(pydname) return str(pydname) @@ -153,7 +151,7 @@ class AppTestCpythonExtensionBase(LeakCheckingTest): kwds["link_files"] = [str(api_library + '.so')] if sys.platform == 'linux2': kwds["compile_extra"]=["-Werror=implicit-function-declaration"] - return compile_module(name, **kwds) + return compile_module(self.space, name, **kwds) def import_module(self, name, init=None, body='', load_it=True, filename=None): diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py index 1796ebc374..e400a4ccbf 100644 --- a/pypy/module/cpyext/typeobject.py +++ b/pypy/module/cpyext/typeobject.py @@ -182,10 +182,10 @@ def tp_new_wrapper(space, self, w_args, w_kwds): subtype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_subtype)) try: - obj = generic_cpy_call(space, tp_new, subtype, w_args, w_kwds) + w_obj = generic_cpy_call(space, tp_new, subtype, w_args, w_kwds) finally: Py_DecRef(space, w_subtype) - return obj + return w_obj @specialize.memo() def get_new_method_def(space): @@ -193,10 +193,14 @@ def get_new_method_def(space): if state.new_method_def: return state.new_method_def from pypy.module.cpyext.modsupport import PyMethodDef - ptr = lltype.malloc(PyMethodDef, flavor="raw", zero=True) + ptr = lltype.malloc(PyMethodDef, flavor="raw", zero=True, + immortal=True) ptr.c_ml_name = rffi.str2charp("__new__") + lltype.render_immortal(ptr.c_ml_name) rffi.setintfield(ptr, 'c_ml_flags', METH_VARARGS | METH_KEYWORDS) - ptr.c_ml_doc = rffi.str2charp("T.__new__(S, ...) -> a new object with type S, a subtype of T") + ptr.c_ml_doc = rffi.str2charp( + "T.__new__(S, ...) -> a new object with type S, a subtype of T") + lltype.render_immortal(ptr.c_ml_doc) state.new_method_def = ptr return ptr @@ -429,6 +433,12 @@ def type_attach(space, py_obj, w_type): finish_type_1(space, pto) finish_type_2(space, pto, w_type) + if space.type(w_type).is_cpytype(): + # XXX Types with a C metatype are never freed, try to see why... + render_immortal(pto, w_type) + lltype.render_immortal(pto) + lltype.render_immortal(pto.c_tp_name) + pto.c_tp_basicsize = rffi.sizeof(typedescr.basestruct) if pto.c_tp_base: if pto.c_tp_base.c_tp_basicsize > pto.c_tp_basicsize: @@ -534,12 +544,25 @@ def type_realize(space, py_obj): w_obj.ready() finish_type_2(space, py_type, w_obj) + render_immortal(py_type, w_obj) state = space.fromcache(RefcountState) state.non_heaptypes_w.append(w_obj) return w_obj +def render_immortal(py_type, w_obj): + lltype.render_immortal(py_type.c_tp_bases) + lltype.render_immortal(py_type.c_tp_mro) + + assert isinstance(w_obj, W_TypeObject) + if w_obj.is_cpytype(): + lltype.render_immortal(py_type.c_tp_dict) + else: + lltype.render_immortal(py_type.c_tp_name) + if not w_obj.is_cpytype() and w_obj.is_heaptype(): + lltype.render_immortal(py_type) + def finish_type_1(space, pto): """ Sets up tp_bases, necessary before creating the interpreter type. diff --git a/pypy/module/gc/__init__.py b/pypy/module/gc/__init__.py index e76bbfaad3..e003d7b683 100644 --- a/pypy/module/gc/__init__.py +++ b/pypy/module/gc/__init__.py @@ -29,6 +29,7 @@ class Module(MixedModule): 'get_referents': 'referents.get_referents', 'get_referrers': 'referents.get_referrers', '_dump_rpy_heap': 'referents._dump_rpy_heap', + 'get_typeids_z': 'referents.get_typeids_z', 'GcRef': 'referents.W_GcRef', }) MixedModule.__init__(self, space, w_name) diff --git a/pypy/module/gc/app_referents.py b/pypy/module/gc/app_referents.py index 461cb989ac..b97a5baa05 100644 --- a/pypy/module/gc/app_referents.py +++ b/pypy/module/gc/app_referents.py @@ -14,11 +14,25 @@ def dump_rpy_heap(file): and [addr1]..[addrn] are addresses of other objects that this object points to. The full dump is a list of such objects, with a marker [0][0][0][-1] inserted after all GC roots, before all non-roots. + + If the argument is a filename and the 'zlib' module is available, + we also write a 'typeids.txt' in the same directory, if none exists. """ if isinstance(file, str): f = open(file, 'wb') gc._dump_rpy_heap(f.fileno()) f.close() + try: + import zlib, os + except ImportError: + pass + else: + filename2 = os.path.join(os.path.dirname(file), 'typeids.txt') + if not os.path.exists(filename2): + data = zlib.decompress(gc.get_typeids_z()) + f = open(filename2, 'wb') + f.write(data) + f.close() else: if isinstance(file, int): fd = file diff --git a/pypy/module/gc/interp_gc.py b/pypy/module/gc/interp_gc.py index 541316c494..a22dd1e644 100644 --- a/pypy/module/gc/interp_gc.py +++ b/pypy/module/gc/interp_gc.py @@ -10,6 +10,10 @@ def collect(space): from pypy.objspace.std.typeobject import MethodCache cache = space.fromcache(MethodCache) cache.clear() + if space.config.objspace.std.withmapdict: + from pypy.objspace.std.mapdict import IndexCache + cache = space.fromcache(IndexCache) + cache.clear() rgc.collect() return space.wrap(0) diff --git a/pypy/module/gc/referents.py b/pypy/module/gc/referents.py index 0aba55771b..8233419611 100644 --- a/pypy/module/gc/referents.py +++ b/pypy/module/gc/referents.py @@ -177,3 +177,9 @@ def _dump_rpy_heap(space, fd): if not ok: raise missing_operation(space) _dump_rpy_heap.unwrap_spec = [ObjSpace, int] + +def get_typeids_z(space): + a = rgc.get_typeids_z() + s = ''.join([a[i] for i in range(len(a))]) + return space.wrap(s) +get_typeids_z.unwrap_spec = [ObjSpace] diff --git a/pypy/module/gc/test/test_gc.py b/pypy/module/gc/test/test_gc.py index dcca7a853f..295fcf1813 100644 --- a/pypy/module/gc/test/test_gc.py +++ b/pypy/module/gc/test/test_gc.py @@ -117,6 +117,33 @@ class AppTestGcMethodCache(object): pass C().f() # Fill the method cache rlist.append(weakref.ref(C)) + for i in range(10): + f() + gc.collect() # the classes C should all go away here + # the last class won't go in mapdict, as long as the code object of f + # is around + rlist.pop() + for r in rlist: + assert r() is None + +class AppTestGcMapDictIndexCache(AppTestGcMethodCache): + def setup_class(cls): + cls.space = gettestobjspace(**{"objspace.std.withmethodcache": True, + "objspace.std.withmapdict": True}) + + + def test_clear_index_cache(self): + import gc, weakref + rlist = [] + def f(): + class C(object): + def f(self): + pass + c = C() + c.x = 1 + getattr(c, "x") # fill the index cache without using the local cache + getattr(c, "x") + rlist.append(weakref.ref(C)) for i in range(5): f() gc.collect() # the classes C should all go away here diff --git a/pypy/module/imp/importing.py b/pypy/module/imp/importing.py index b69c621e42..bb0314fb50 100644 --- a/pypy/module/imp/importing.py +++ b/pypy/module/imp/importing.py @@ -12,7 +12,7 @@ from pypy.interpreter.eval import Code from pypy.rlib import streamio, jit from pypy.rlib.streamio import StreamErrors from pypy.rlib.rarithmetic import intmask -from pypy.rlib.objectmodel import we_are_translated +from pypy.rlib.objectmodel import we_are_translated, specialize SEARCH_ERROR = 0 PY_SOURCE = 1 @@ -25,10 +25,26 @@ PY_FROZEN = 7 # PY_CODERESOURCE = 8 IMP_HOOK = 9 -if sys.platform.startswith('win'): - so_extension = ".pyd" +if sys.platform == 'win32': + SO = ".pyd" else: - so_extension = ".so" + SO = ".so" +DEFAULT_SOABI = 'pypy-14' + +@specialize.memo() +def get_so_extension(space): + if space.config.objspace.soabi is not None: + soabi = space.config.objspace.soabi + else: + soabi = DEFAULT_SOABI + + if not soabi: + return SO + + if not space.config.translating: + soabi += 'i' + + return '.' + soabi + SO def find_modtype(space, filepart): """Check which kind of module to import for the given filepart, @@ -53,6 +69,7 @@ def find_modtype(space, filepart): return PY_COMPILED, ".pyc", "rb" if space.config.objspace.usemodules.cpyext: + so_extension = get_so_extension(space) pydfile = filepart + so_extension if os.path.exists(pydfile) and case_ok(pydfile): return C_EXTENSION, so_extension, "rb" diff --git a/pypy/module/imp/interp_imp.py b/pypy/module/imp/interp_imp.py index e94453b1dc..fea66141b4 100644 --- a/pypy/module/imp/interp_imp.py +++ b/pypy/module/imp/interp_imp.py @@ -8,10 +8,16 @@ import struct def get_suffixes(space): w = space.wrap - return space.newlist([ + suffixes_w = [] + if space.config.objspace.usemodules.cpyext: + suffixes_w.append( + space.newtuple([w(importing.get_so_extension(space)), + w('rb'), w(importing.C_EXTENSION)])) + suffixes_w.extend([ space.newtuple([w('.py'), w('U'), w(importing.PY_SOURCE)]), space.newtuple([w('.pyc'), w('rb'), w(importing.PY_COMPILED)]), ]) + return space.newlist(suffixes_w) def get_magic(space): x = importing.get_pyc_magic(space) diff --git a/pypy/module/imp/test/test_app.py b/pypy/module/imp/test/test_app.py index e96588fbf2..49b3ad5f0f 100644 --- a/pypy/module/imp/test/test_app.py +++ b/pypy/module/imp/test/test_app.py @@ -47,6 +47,9 @@ class AppTestImpModule: elif mode == self.imp.PY_COMPILED: assert suffix in ('.pyc', '.pyo') assert type == 'rb' + elif mode == self.imp.C_EXTENSION: + assert suffix.endswith(('.pyd', '.so')) + assert type == 'rb' def test_obscure_functions(self): diff --git a/pypy/module/imp/test/test_import.py b/pypy/module/imp/test/test_import.py index 056fe9caf9..5a27ab84df 100644 --- a/pypy/module/imp/test/test_import.py +++ b/pypy/module/imp/test/test_import.py @@ -473,6 +473,17 @@ class AppTestImport: except ImportError: pass +class TestAbi: + def test_abi_tag(self): + space1 = gettestobjspace(soabi='TEST') + space2 = gettestobjspace(soabi='') + if sys.platform == 'win32': + assert importing.get_so_extension(space1) == '.TESTi.pyd' + assert importing.get_so_extension(space2) == '.pyd' + else: + assert importing.get_so_extension(space1) == '.TESTi.so' + assert importing.get_so_extension(space2) == '.so' + def _getlong(data): x = marshal.dumps(data) return x[-4:] diff --git a/pypy/module/posix/interp_posix.py b/pypy/module/posix/interp_posix.py index 47c98ad319..042e20f16f 100644 --- a/pypy/module/posix/interp_posix.py +++ b/pypy/module/posix/interp_posix.py @@ -454,7 +454,8 @@ class State: self.w_environ = space.newdict() if _WIN: self.cryptProviderPtr = lltype.malloc( - rffi.CArray(HCRYPTPROV), 1, zero=True, flavor='raw') + rffi.CArray(HCRYPTPROV), 1, zero=True, + flavor='raw', immortal=True) def startup(self, space): _convertenviron(space, self.w_environ) def _freeze_(self): diff --git a/pypy/module/pypyjit/interp_jit.py b/pypy/module/pypyjit/interp_jit.py index e2a71a04b4..77057a1879 100644 --- a/pypy/module/pypyjit/interp_jit.py +++ b/pypy/module/pypyjit/interp_jit.py @@ -6,6 +6,7 @@ This is transformed to become a JIT by code elsewhere: pypy/jit/* from pypy.tool.pairtype import extendabletype from pypy.rlib.rarithmetic import r_uint, intmask from pypy.rlib.jit import JitDriver, hint, we_are_jitted, dont_look_inside +from pypy.rlib.jit import current_trace_length import pypy.interpreter.pyopcode # for side-effects from pypy.interpreter.error import OperationError, operationerrfmt from pypy.interpreter.gateway import ObjSpace, Arguments, W_Root @@ -80,9 +81,22 @@ class __extend__(PyFrame): def jump_absolute(self, jumpto, _, ec=None): if we_are_jitted(): + # Normally, the tick counter is decremented by 100 for every + # Python opcode. Here, to better support JIT compilation of + # small loops, we decrement it by a possibly smaller constant. + # We get the maximum 100 when the (unoptimized) trace length + # is at least 3200 (a bit randomly). + trace_length = r_uint(current_trace_length()) + decr_by = trace_length // 32 + if decr_by < 1: + decr_by = 1 + elif decr_by > 100: # also if current_trace_length() returned -1 + decr_by = 100 + # self.last_instr = intmask(jumpto) - ec.bytecode_trace(self) + ec.bytecode_trace(self, intmask(decr_by)) jumpto = r_uint(self.last_instr) + # pypyjitdriver.can_enter_jit(frame=self, ec=ec, next_instr=jumpto, pycode=self.getcode()) return jumpto diff --git a/pypy/module/pypyjit/policy.py b/pypy/module/pypyjit/policy.py index 20a6452b2a..d80893476c 100644 --- a/pypy/module/pypyjit/policy.py +++ b/pypy/module/pypyjit/policy.py @@ -6,7 +6,8 @@ class PyPyJitPolicy(JitPolicy): if (modname == '__builtin__.operation' or modname == '__builtin__.abstractinst' or modname == '__builtin__.interp_classobj' or - modname == '__builtin__.functional'): + modname == '__builtin__.functional' or + modname == '__builtin__.descriptor'): return True if '.' in modname: modname, _ = modname.split('.', 1) @@ -19,7 +20,7 @@ class PyPyJitPolicy(JitPolicy): # this function should never actually return True directly # but instead call the base implementation mod = func.__module__ or '?' - + if mod.startswith('pypy.objspace.'): # gc_id operation if func.__name__ == 'id__ANY': @@ -36,5 +37,5 @@ class PyPyJitPolicy(JitPolicy): modname = mod[len('pypy.module.'):] if not self.look_inside_pypy_module(modname): return False - + return True diff --git a/pypy/module/pypyjit/test/test_policy.py b/pypy/module/pypyjit/test/test_policy.py index 02ac51ae06..1f781e10de 100644 --- a/pypy/module/pypyjit/test/test_policy.py +++ b/pypy/module/pypyjit/test/test_policy.py @@ -12,7 +12,7 @@ def test_bigint(): def test_rlocale(): from pypy.rlib.rlocale import setlocale - assert not pypypolicy.look_inside_function(setlocale) + assert not pypypolicy.look_inside_function(setlocale) def test_geninterp(): d = {'_geninterp_': True} @@ -28,6 +28,10 @@ def test_pyparser(): from pypy.interpreter.pyparser import parser assert not pypypolicy.look_inside_function(parser.Grammar.__init__.im_func) +def test_property(): + from pypy.module.__builtin__.descriptor import W_Property + assert pypypolicy.look_inside_function(W_Property.get.im_func) + def test_pypy_module(): from pypy.module._random.interp_random import W_Random assert not pypypolicy.look_inside_function(W_Random.random) @@ -35,6 +39,7 @@ def test_pypy_module(): assert pypypolicy.look_inside_pypy_module('__builtin__.operation') assert pypypolicy.look_inside_pypy_module('__builtin__.abstractinst') assert pypypolicy.look_inside_pypy_module('__builtin__.functional') + assert pypypolicy.look_inside_pypy_module('__builtin__.descriptor') assert pypypolicy.look_inside_pypy_module('exceptions.interp_exceptions') for modname in 'pypyjit', 'signal', 'micronumpy', 'math', 'imp': assert pypypolicy.look_inside_pypy_module(modname) @@ -42,4 +47,3 @@ def test_pypy_module(): def test_see_jit_module(): assert pypypolicy.look_inside_pypy_module('pypyjit.interp_jit') - diff --git a/pypy/module/pypyjit/test/test_pypy_c.py b/pypy/module/pypyjit/test/test_pypy_c.py index 43550aa6b6..c9be155293 100644 --- a/pypy/module/pypyjit/test/test_pypy_c.py +++ b/pypy/module/pypyjit/test/test_pypy_c.py @@ -422,10 +422,75 @@ class PyPyCJITTests(object): ([1000], 49500), ([10000], 495000), ([100000], 4950000)) - assert len(self.loops) == 2 + assert len(self.loops) == 3 op, = self.get_by_bytecode("CALL_FUNCTION_KW") # XXX a bit too many guards, but better than before - assert len(op.get_opnames("guard")) <= 10 + assert len(op.get_opnames("guard")) <= 12 + + def test_stararg_virtual(self): + self.run_source(''' + d = {} + + def g(*args): + return len(args) + def h(a, b, c): + return c + + def main(x): + s = 0 + for i in range(x): + l = [i, x, 2] + s += g(*l) + s += h(*l) + s += g(i, x, 2) + for i in range(x): + l = [x, 2] + s += g(i, *l) + s += h(i, *l) + return s + ''', 100000, ([100], 1300), + ([1000], 13000), + ([10000], 130000), + ([100000], 1300000)) + assert len(self.loops) == 2 + ops = self.get_by_bytecode("CALL_FUNCTION_VAR") + assert len(ops) == 4 + for op in ops: + assert len(op.get_opnames("new")) == 0 + assert len(op.get_opnames("call_may_force")) == 0 + + ops = self.get_by_bytecode("CALL_FUNCTION") + for op in ops: + assert len(op.get_opnames("new")) == 0 + assert len(op.get_opnames("call_may_force")) == 0 + + def test_stararg(self): + self.run_source(''' + d = {} + + def g(*args): + return args[-1] + def h(*args): + return len(args) + + def main(x): + s = 0 + l = [] + i = 0 + while i < x: + l.append(1) + s += g(*l) + i = h(*l) + return s + ''', 100000, ([100], 100), + ([1000], 1000), + ([2000], 2000), + ([4000], 4000)) + assert len(self.loops) == 1 + ops = self.get_by_bytecode("CALL_FUNCTION_VAR") + for op in ops: + assert len(op.get_opnames("new_with_vtable")) == 0 + assert len(op.get_opnames("call_may_force")) == 0 def test_virtual_instance(self): self.run_source(''' diff --git a/pypy/module/rctime/interp_time.py b/pypy/module/rctime/interp_time.py index 9a06bef3f9..dbb51df6d8 100644 --- a/pypy/module/rctime/interp_time.py +++ b/pypy/module/rctime/interp_time.py @@ -69,7 +69,7 @@ if _POSIX: CLOCKS_PER_SEC = cConfig.CLOCKS_PER_SEC clock_t = cConfig.clock_t tm = cConfig.tm -glob_buf = lltype.malloc(tm, flavor='raw', zero=True) +glob_buf = lltype.malloc(tm, flavor='raw', zero=True, immortal=True) if cConfig.has_gettimeofday: c_gettimeofday = external('gettimeofday', [rffi.VOIDP, rffi.VOIDP], rffi.INT) diff --git a/pypy/module/signal/__init__.py b/pypy/module/signal/__init__.py index 613de46930..a861feddff 100644 --- a/pypy/module/signal/__init__.py +++ b/pypy/module/signal/__init__.py @@ -34,9 +34,7 @@ class Module(MixedModule): MixedModule.__init__(self, space, *args) # add the signal-checking callback as an action on the space space.check_signal_action = interp_signal.CheckSignalAction(space) - space.actionflag.register_action(space.check_signal_action) - # use the C-level pypysig_occurred variable as the action flag - # (the result is that the C-level signal handler will directly - # set the flag for the CheckSignalAction) + space.actionflag.register_periodic_action(space.check_signal_action, + use_bytecode_counter=False) space.actionflag.__class__ = interp_signal.SignalActionFlag # xxx yes I know the previous line is a hack diff --git a/pypy/module/signal/interp_signal.py b/pypy/module/signal/interp_signal.py index 26efaecfb2..73b1904075 100644 --- a/pypy/module/signal/interp_signal.py +++ b/pypy/module/signal/interp_signal.py @@ -1,6 +1,7 @@ from pypy.interpreter.error import OperationError from pypy.interpreter.baseobjspace import W_Root, ObjSpace from pypy.interpreter.executioncontext import AsyncAction, AbstractActionFlag +from pypy.interpreter.executioncontext import PeriodicAsyncAction import signal as cpy_signal from pypy.rpython.lltypesystem import lltype, rffi from pypy.translator.tool.cbuild import ExternalCompilationInfo @@ -52,19 +53,29 @@ c_pause = external('pause', [], rffi.INT) class SignalActionFlag(AbstractActionFlag): - def get(self): + # This class uses the C-level pypysig_counter variable as the tick + # counter. The C-level signal handler will reset it to -1 whenever + # a signal is received. + + def get_ticker(self): p = pypysig_getaddr_occurred() return p.c_value - def set(self, value): + + def reset_ticker(self, value): p = pypysig_getaddr_occurred() p.c_value = value + def decrement_ticker(self, by): + p = pypysig_getaddr_occurred() + value = p.c_value + if self.has_bytecode_counter: # this 'if' is constant-folded + value -= by + p.c_value = value + return value -class CheckSignalAction(AsyncAction): - """An action that is automatically invoked when a signal is received.""" - # The C-level signal handler sets the bit 30 of pypysig_occurred: - bitmask = 1 << 30 +class CheckSignalAction(PeriodicAsyncAction): + """An action that is automatically invoked when a signal is received.""" def __init__(self, space): AsyncAction.__init__(self, space) @@ -73,7 +84,6 @@ class CheckSignalAction(AsyncAction): # need a helper action in case signals arrive in a non-main thread self.pending_signals = {} self.reissue_signal_action = ReissueSignalAction(space) - space.actionflag.register_action(self.reissue_signal_action) else: self.reissue_signal_action = None diff --git a/pypy/module/signal/test/test_signal.py b/pypy/module/signal/test/test_signal.py index 10c29a531f..729c404a8b 100644 --- a/pypy/module/signal/test/test_signal.py +++ b/pypy/module/signal/test/test_signal.py @@ -1,6 +1,38 @@ import os, py +import signal as cpy_signal from pypy.conftest import gettestobjspace + +class TestCheckSignals: + + def setup_class(cls): + if not hasattr(os, 'kill') or not hasattr(os, 'getpid'): + py.test.skip("requires os.kill() and os.getpid()") + cls.space = gettestobjspace(usemodules=['signal']) + + def test_checksignals(self): + space = self.space + w_received = space.appexec([], """(): + import signal + received = [] + def myhandler(signum, frame): + received.append(signum) + signal.signal(signal.SIGUSR1, myhandler) + return received""") + # + assert not space.is_true(w_received) + # + # send the signal now + os.kill(os.getpid(), cpy_signal.SIGUSR1) + # + # myhandler() should not be immediately called + assert not space.is_true(w_received) + # + # calling ec.checksignals() should call it + space.getexecutioncontext().checksignals() + assert space.is_true(w_received) + + class AppTestSignal: def setup_class(cls): @@ -24,18 +56,12 @@ class AppTestSignal: signal.signal(signal.SIGUSR1, myhandler) posix.kill(posix.getpid(), signal.SIGUSR1) - for i in range(10000): - # wait a bit for the signal to be delivered to the handler - if received: - break + # the signal should be delivered to the handler immediately assert received == [signal.SIGUSR1] del received[:] posix.kill(posix.getpid(), signal.SIGUSR1) - for i in range(10000): - # wait a bit for the signal to be delivered to the handler - if received: - break + # the signal should be delivered to the handler immediately assert received == [signal.SIGUSR1] del received[:] diff --git a/pypy/module/sys/__init__.py b/pypy/module/sys/__init__.py index 7e3b8ab9ba..492ad1560e 100644 --- a/pypy/module/sys/__init__.py +++ b/pypy/module/sys/__init__.py @@ -10,7 +10,6 @@ class Module(MixedModule): if space.config.translating: del self.__class__.interpleveldefs['pypy_getudir'] super(Module, self).__init__(space, w_name) - self.checkinterval = 100 self.recursionlimit = 100 self.w_default_encoder = None self.defaultencoding = "ascii" diff --git a/pypy/module/sys/version.py b/pypy/module/sys/version.py index 4b6bd08fd4..6d80545fee 100644 --- a/pypy/module/sys/version.py +++ b/pypy/module/sys/version.py @@ -8,7 +8,7 @@ import os CPYTHON_VERSION = (2, 5, 2, "beta", 42) #XXX # sync patchlevel.h CPYTHON_API_VERSION = 1012 #XXX # sync with include/modsupport.h -PYPY_VERSION = (1, 3, 0, "beta", '?') #XXX # sync patchlevel.h +PYPY_VERSION = (1, 4, 0, "beta", '?') #XXX # sync patchlevel.h # the last item is replaced by the svn revision ^^^ TRIM_URL_UP_TO = 'svn/pypy/' diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py index 154cce149e..a21324aac0 100644 --- a/pypy/module/sys/vm.py +++ b/pypy/module/sys/vm.py @@ -67,7 +67,7 @@ def getrecursionlimit(space): def setcheckinterval(space, interval): """Tell the Python interpreter to check for asynchronous events every n instructions. This also affects how often thread switches occur.""" - space.actionflag.setcheckinterval(space, interval) + space.actionflag.setcheckinterval(interval) setcheckinterval.unwrap_spec = [ObjSpace, int] def getcheckinterval(space): @@ -77,7 +77,7 @@ def getcheckinterval(space): # return 0. The idea is that according to the CPython docs, <= 0 # means "check every virtual instruction, maximizing responsiveness # as well as overhead". - result = space.sys.checkinterval + result = space.actionflag.getcheckinterval() if result <= 1: result = 0 return space.wrap(result) diff --git a/pypy/module/thread/gil.py b/pypy/module/thread/gil.py index 87fee7de2c..500ca17058 100644 --- a/pypy/module/thread/gil.py +++ b/pypy/module/thread/gil.py @@ -20,7 +20,8 @@ class GILThreadLocals(OSThreadLocals): def initialize(self, space): # add the GIL-releasing callback as an action on the space - space.actionflag.register_action(GILReleaseAction(space)) + space.actionflag.register_periodic_action(GILReleaseAction(space), + use_bytecode_counter=True) def setup_threads(self, space): """Enable threads in the object space, if they haven't already been.""" @@ -44,7 +45,6 @@ class GILThreadLocals(OSThreadLocals): # test_compile_lock. As a workaround, we repatch these global # fields systematically. spacestate.ll_GIL = self.ll_GIL - spacestate.actionflag = space.actionflag invoke_around_extcall(before_external_call, after_external_call) return result @@ -68,18 +68,17 @@ class SpaceState: def _freeze_(self): self.ll_GIL = thread.null_ll_lock - self.actionflag = None - self.set_actionflag_bit_after_thread_switch = 0 + self.action_after_thread_switch = None + # ^^^ set by AsyncAction.fire_after_thread_switch() return False def after_thread_switch(self): # this is support logic for the signal module, to help it deliver # signals to the main thread. - actionflag = self.actionflag - if actionflag is not None: - flag = actionflag.get() - flag |= self.set_actionflag_bit_after_thread_switch - actionflag.set(flag) + action = self.action_after_thread_switch + if action is not None: + self.action_after_thread_switch = None + action.fire() spacestate = SpaceState() spacestate._freeze_() diff --git a/pypy/module/thread/test/test_gil.py b/pypy/module/thread/test/test_gil.py index b2dacf0a06..03db6e7f92 100644 --- a/pypy/module/thread/test/test_gil.py +++ b/pypy/module/thread/test/test_gil.py @@ -8,7 +8,7 @@ class FakeEC(object): pass class FakeActionFlag(object): - def register_action(self, action): + def register_periodic_action(self, action, use_bytecode_counter): pass def get(self): return 0 |