Author: Carl Friedrich Bolz <cfb...@gmx.de> Branch: space-newtext Changeset: r88776:67d4325b8871 Date: 2016-11-30 16:36 +0100 http://bitbucket.org/pypy/pypy/changeset/67d4325b8871/
Log: merge default diff too long, truncating to 2000 out of 4915 lines diff --git a/lib-python/2.7/distutils/sysconfig_pypy.py b/lib-python/2.7/distutils/sysconfig_pypy.py --- a/lib-python/2.7/distutils/sysconfig_pypy.py +++ b/lib-python/2.7/distutils/sysconfig_pypy.py @@ -12,7 +12,6 @@ import sys import os -import shlex import imp from distutils.errors import DistutilsPlatformError @@ -62,11 +61,31 @@ def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" g = {} + g['CC'] = "gcc -pthread" + g['CXX'] = "g++ -pthread" + g['OPT'] = "-DNDEBUG -O2" + g['CFLAGS'] = "-DNDEBUG -O2" + g['CCSHARED'] = "-fPIC" + g['LDSHARED'] = "gcc -pthread -shared" + g['SO'] = [s[0] for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION][0] + g['AR'] = "ar" + g['ARFLAGS'] = "rc" g['EXE'] = "" - g['SO'] = [s[0] for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION][0] g['LIBDIR'] = os.path.join(sys.prefix, 'lib') - g['CC'] = "gcc -pthread" # -pthread might not be valid on OS/X, check - g['OPT'] = "" + g['VERSION'] = get_python_version() + + if sys.platform[:6] == "darwin": + import platform + if platform.machine() == 'i386': + if platform.architecture()[0] == '32bit': + arch = 'i386' + else: + arch = 'x86_64' + else: + # just a guess + arch = platform.machine() + g['LDSHARED'] += ' -undefined dynamic_lookup' + g['CC'] += ' -arch %s' % (arch,) global _config_vars _config_vars = g @@ -103,6 +122,12 @@ _config_vars['prefix'] = PREFIX _config_vars['exec_prefix'] = EXEC_PREFIX + # OS X platforms require special customization to handle + # multi-architecture, multi-os-version installers + if sys.platform == 'darwin': + import _osx_support + _osx_support.customize_config_vars(_config_vars) + if args: vals = [] for name in args: @@ -118,30 +143,80 @@ """ return get_config_vars().get(name) + def customize_compiler(compiler): - """Dummy method to let some easy_install packages that have - optional C speedup components. + """Do any platform-specific customization of a CCompiler instance. + + Mainly needed on Unix, so we can plug in the information that + varies across Unices and is stored in Python's Makefile (CPython) + or hard-coded in _init_posix() (PyPy). """ - def customize(executable, flags): - command = compiler.executables[executable] + flags - setattr(compiler, executable, command) + if compiler.compiler_type == "unix": + if sys.platform == "darwin": + # Perform first-time customization of compiler-related + # config vars on OS X now that we know we need a compiler. + # This is primarily to support Pythons from binary + # installers. The kind and paths to build tools on + # the user system may vary significantly from the system + # that Python itself was built on. Also the user OS + # version and build tools may not support the same set + # of CPU architectures for universal builds. + global _config_vars + # Use get_config_var() to ensure _config_vars is initialized. + if not get_config_var('CUSTOMIZED_OSX_COMPILER'): + import _osx_support + _osx_support.customize_compiler(_config_vars) + _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' - if compiler.compiler_type == "unix": - # compiler_so can be c++ which has no -Wimplicit - #compiler.compiler_so.extend(['-O2', '-fPIC', '-Wimplicit']) - compiler.compiler_so.extend(['-O2', '-fPIC']) - compiler.shared_lib_extension = get_config_var('SO') - if "CPPFLAGS" in os.environ: - cppflags = shlex.split(os.environ["CPPFLAGS"]) - for executable in ('compiler', 'compiler_so', 'linker_so'): - customize(executable, cppflags) - if "CFLAGS" in os.environ: - cflags = shlex.split(os.environ["CFLAGS"]) - for executable in ('compiler', 'compiler_so', 'linker_so'): - customize(executable, cflags) - if "LDFLAGS" in os.environ: - ldflags = shlex.split(os.environ["LDFLAGS"]) - customize('linker_so', ldflags) + (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ + get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', + 'CCSHARED', 'LDSHARED', 'SO', 'AR', + 'ARFLAGS') + + if 'CC' in os.environ: + newcc = os.environ['CC'] + if (sys.platform == 'darwin' + and 'LDSHARED' not in os.environ + and ldshared.startswith(cc)): + # On OS X, if CC is overridden, use that as the default + # command for LDSHARED as well + ldshared = newcc + ldshared[len(cc):] + cc = newcc + if 'CXX' in os.environ: + cxx = os.environ['CXX'] + if 'LDSHARED' in os.environ: + ldshared = os.environ['LDSHARED'] + if 'CPP' in os.environ: + cpp = os.environ['CPP'] + else: + cpp = cc + " -E" # not always + if 'LDFLAGS' in os.environ: + ldshared = ldshared + ' ' + os.environ['LDFLAGS'] + if 'CFLAGS' in os.environ: + cflags = opt + ' ' + os.environ['CFLAGS'] + ldshared = ldshared + ' ' + os.environ['CFLAGS'] + if 'CPPFLAGS' in os.environ: + cpp = cpp + ' ' + os.environ['CPPFLAGS'] + cflags = cflags + ' ' + os.environ['CPPFLAGS'] + ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] + if 'AR' in os.environ: + ar = os.environ['AR'] + if 'ARFLAGS' in os.environ: + archiver = ar + ' ' + os.environ['ARFLAGS'] + else: + archiver = ar + ' ' + ar_flags + + cc_cmd = cc + ' ' + cflags + compiler.set_executables( + preprocessor=cpp, + compiler=cc_cmd, + compiler_so=cc_cmd + ' ' + ccshared, + compiler_cxx=cxx, + linker_so=ldshared, + linker_exe=cc, + archiver=archiver) + + compiler.shared_lib_extension = so_ext from sysconfig_cpython import ( diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -20,3 +20,12 @@ .. branch: clean-exported-state Clean-ups in the jit optimizeopt + +.. branch: conditional_call_value_4 + +Add jit.conditional_call_elidable(), a way to tell the JIT "conditonally +call this function" returning a result. + +.. branch: desc-specialize + +Refactor FunctionDesc.specialize() and related code (RPython annotator). diff --git a/pypy/goal/targetpypystandalone.py b/pypy/goal/targetpypystandalone.py --- a/pypy/goal/targetpypystandalone.py +++ b/pypy/goal/targetpypystandalone.py @@ -298,6 +298,12 @@ if config.translation.sandbox: config.objspace.lonepycfiles = False + if config.objspace.usemodules.cpyext: + if config.translation.gc != 'incminimark': + raise Exception("The 'cpyext' module requires the 'incminimark'" + " GC. You need either 'targetpypystandalone.py" + " --withoutmod-cpyext' or '--gc=incminimark'") + config.translating = True import translate diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py --- a/pypy/interpreter/argument.py +++ b/pypy/interpreter/argument.py @@ -111,7 +111,9 @@ self.keywords = self.keywords + keywords self.keywords_w = self.keywords_w + values_w return + is_dict = False if space.isinstance_w(w_starstararg, space.w_dict): + is_dict = True keys_w = space.unpackiterable(w_starstararg) else: try: @@ -125,7 +127,9 @@ keys_w = space.unpackiterable(w_keys) keywords_w = [None] * len(keys_w) keywords = [None] * len(keys_w) - _do_combine_starstarargs_wrapped(space, keys_w, w_starstararg, keywords, keywords_w, self.keywords) + _do_combine_starstarargs_wrapped( + space, keys_w, w_starstararg, keywords, keywords_w, self.keywords, + is_dict) self.keyword_names_w = keys_w if self.keywords is None: self.keywords = keywords @@ -355,7 +359,7 @@ key) def _do_combine_starstarargs_wrapped(space, keys_w, w_starstararg, keywords, - keywords_w, existingkeywords): + keywords_w, existingkeywords, is_dict): i = 0 for w_key in keys_w: try: @@ -374,7 +378,16 @@ "got multiple values for keyword argument '%s'", key) keywords[i] = key - keywords_w[i] = space.getitem(w_starstararg, w_key) + if is_dict: + # issue 2435: bug-to-bug compatibility with cpython. for a subclass of + # dict, just ignore the __getitem__ and access the underlying dict + # directly + from pypy.objspace.descroperation import dict_getitem + w_descr = dict_getitem(space) + w_value = space.get_and_call_function(w_descr, w_starstararg, w_key) + else: + w_value = space.getitem(w_starstararg, w_key) + keywords_w[i] = w_value i += 1 @jit.look_inside_iff( diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -184,6 +184,14 @@ assert self._finalize_.im_func is not W_Root._finalize_.im_func space.finalizer_queue.register_finalizer(self) + def may_unregister_rpython_finalizer(self, space): + """Optimization hint only: if there is no user-defined __del__() + method, pass the hint ``don't call any finalizer'' to rgc. + """ + if not self.getclass(space).hasuserdel: + from rpython.rlib import rgc + rgc.may_ignore_finalizer(self) + # hooks that the mapdict implementations needs: def _get_mapdict_map(self): return None diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py --- a/pypy/interpreter/error.py +++ b/pypy/interpreter/error.py @@ -445,6 +445,7 @@ space.newtext(msg)) return OperationError(exc, w_error) +@specialize.arg(3) def wrap_oserror2(space, e, w_filename=None, exception_name='w_OSError', w_exception_class=None): assert isinstance(e, OSError) @@ -472,8 +473,8 @@ w_error = space.call_function(exc, space.newint(errno), space.newtext(msg)) return OperationError(exc, w_error) -wrap_oserror2._annspecialcase_ = 'specialize:arg(3)' +@specialize.arg(3) def wrap_oserror(space, e, filename=None, exception_name='w_OSError', w_exception_class=None): if filename is not None: @@ -484,7 +485,6 @@ return wrap_oserror2(space, e, None, exception_name=exception_name, w_exception_class=w_exception_class) -wrap_oserror._annspecialcase_ = 'specialize:arg(3)' def exception_from_saved_errno(space, w_type): from rpython.rlib.rposix import get_saved_errno diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py --- a/pypy/interpreter/generator.py +++ b/pypy/interpreter/generator.py @@ -2,7 +2,7 @@ from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.pyopcode import LoopBlock from pypy.interpreter.pycode import CO_YIELD_INSIDE_TRY -from rpython.rlib import jit +from rpython.rlib import jit, rgc class GeneratorIterator(W_Root): @@ -102,11 +102,11 @@ w_result = frame.execute_frame(w_arg, operr) except OperationError: # errors finish a frame - self.frame = None + self.frame_is_finished() raise # if the frame is now marked as finished, it was RETURNed from if frame.frame_finished_execution: - self.frame = None + self.frame_is_finished() raise OperationError(space.w_StopIteration, space.w_None) else: return w_result # YIELDed @@ -208,7 +208,7 @@ finally: frame.f_backref = jit.vref_None self.running = False - self.frame = None + self.frame_is_finished() return unpack_into unpack_into = _create_unpack_into() unpack_into_w = _create_unpack_into() @@ -227,6 +227,10 @@ break block = block.previous + def frame_is_finished(self): + self.frame = None + rgc.may_ignore_finalizer(self) + def get_printable_location_genentry(bytecode): return '%s <generator>' % (bytecode.get_repr(),) diff --git a/pypy/interpreter/test/test_argument.py b/pypy/interpreter/test/test_argument.py --- a/pypy/interpreter/test/test_argument.py +++ b/pypy/interpreter/test/test_argument.py @@ -123,6 +123,12 @@ raise OperationError(AttributeError, name) return method(*args) + def lookup_in_type(self, cls, name): + return getattr(cls, name) + + def get_and_call_function(self, w_descr, w_obj, *args): + return w_descr.__get__(w_obj)(*args) + def type(self, obj): class Type: def getname(self, space): @@ -808,3 +814,19 @@ assert str(e) == "myerror" else: assert False, "Expected TypeError" + + def test_dict_subclass_with_weird_getitem(self): + # issue 2435: bug-to-bug compatibility with cpython. for a subclass of + # dict, just ignore the __getitem__ and behave like ext_do_call in ceval.c + # which just uses the underlying dict + class d(dict): + def __getitem__(self, key): + return key + + for key in ["foo", u"foo"]: + q = d() + q[key] = "bar" + + def test(**kwargs): + return kwargs + assert test(**q) == {"foo": "bar"} diff --git a/pypy/module/_cffi_backend/cdataobj.py b/pypy/module/_cffi_backend/cdataobj.py --- a/pypy/module/_cffi_backend/cdataobj.py +++ b/pypy/module/_cffi_backend/cdataobj.py @@ -397,7 +397,7 @@ space = self.space if space.is_none(w_destructor): if isinstance(self, W_CDataGCP): - self.w_destructor = None + self.detach_destructor() return space.w_None raise oefmt(space.w_TypeError, "Can remove destructor only on a object " @@ -604,6 +604,10 @@ self.w_destructor = None self.space.call_function(w_destructor, self.w_original_cdata) + def detach_destructor(self): + self.w_destructor = None + self.may_unregister_rpython_finalizer(self.space) + W_CData.typedef = TypeDef( '_cffi_backend.CData', diff --git a/pypy/module/_cffi_backend/cdlopen.py b/pypy/module/_cffi_backend/cdlopen.py --- a/pypy/module/_cffi_backend/cdlopen.py +++ b/pypy/module/_cffi_backend/cdlopen.py @@ -55,6 +55,7 @@ if not libhandle: raise oefmt(self.ffi.w_FFIError, "library '%s' is already closed", self.libname) + self.may_unregister_rpython_finalizer(self.ffi.space) # Clear the dict to force further accesses to do cdlopen_fetch() # again, and fail because the library was closed. Note that the diff --git a/pypy/module/_collections/interp_deque.py b/pypy/module/_collections/interp_deque.py --- a/pypy/module/_collections/interp_deque.py +++ b/pypy/module/_collections/interp_deque.py @@ -1,4 +1,5 @@ import sys +from rpython.rlib.objectmodel import specialize from pypy.interpreter import gateway from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.typedef import TypeDef, make_weakref_descr @@ -6,7 +7,6 @@ from pypy.interpreter.gateway import interp2app, unwrap_spec from pypy.interpreter.error import OperationError, oefmt from rpython.rlib.debug import check_nonneg -from rpython.rlib.objectmodel import specialize # A `dequeobject` is composed of a doubly-linked list of `block` nodes. diff --git a/pypy/module/_file/interp_file.py b/pypy/module/_file/interp_file.py --- a/pypy/module/_file/interp_file.py +++ b/pypy/module/_file/interp_file.py @@ -172,6 +172,7 @@ self.newlines = self.stream.getnewlines() self.stream = None self.fd = -1 + self.may_unregister_rpython_finalizer(self.space) openstreams = getopenstreams(self.space) try: del openstreams[stream] @@ -507,11 +508,10 @@ if space.isinstance_w(w_data, space.w_unicode): # note: "encode" is called before we acquire the lock # for this file, which is done in file_write_str(). - # Direct access to unicodeobject because we don't want + # Use a specific space method because we don't want # to call user-defined "encode" methods here. - from pypy.objspace.std.unicodeobject import encode_object - w_data = encode_object(space, w_data, self.encoding, - self.errors) + w_data = space.encode_unicode_object(w_data, + self.encoding, self.errors) data = space.charbuf_w(w_data) self.file_write_str(data) diff --git a/pypy/module/_socket/interp_socket.py b/pypy/module/_socket/interp_socket.py --- a/pypy/module/_socket/interp_socket.py +++ b/pypy/module/_socket/interp_socket.py @@ -233,6 +233,7 @@ except SocketError: # cpython doesn't return any errors on close pass + self.may_unregister_rpython_finalizer(space) def connect_w(self, space, w_addr): """connect(address) diff --git a/pypy/module/_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 @@ -217,7 +217,7 @@ return self.space.w_None return w_obj - def descr__eq__(self, space, w_ref2): + def compare(self, space, w_ref2, invert): if not isinstance(w_ref2, W_Weakref): return space.w_NotImplemented ref1 = self @@ -225,11 +225,18 @@ w_obj1 = ref1.dereference() w_obj2 = ref2.dereference() if w_obj1 is None or w_obj2 is None: - return space.is_(ref1, ref2) - return space.eq(w_obj1, w_obj2) + w_res = space.is_(ref1, ref2) + else: + w_res = space.eq(w_obj1, w_obj2) + if invert: + w_res = space.not_(w_res) + return w_res + + def descr__eq__(self, space, w_ref2): + return self.compare(space, w_ref2, invert=False) def descr__ne__(self, space, w_ref2): - return space.not_(space.eq(self, w_ref2)) + return self.compare(space, w_ref2, invert=True) def getlifeline(space, w_obj): lifeline = w_obj.getweakref() 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 @@ -150,6 +150,14 @@ assert not (ref1 == []) assert ref1 != [] + def test_ne(self): + import _weakref + class X(object): + pass + ref1 = _weakref.ref(X()) + assert ref1.__eq__(X()) is NotImplemented + assert ref1.__ne__(X()) is NotImplemented + def test_getweakrefs(self): import _weakref, gc class A(object): diff --git a/pypy/module/bz2/interp_bz2.py b/pypy/module/bz2/interp_bz2.py --- a/pypy/module/bz2/interp_bz2.py +++ b/pypy/module/bz2/interp_bz2.py @@ -291,7 +291,7 @@ same_attributes_as_in_file.remove('__init__') same_attributes_as_in_file.extend([ 'name', 'mode', 'encoding', 'closed', 'newlines', 'softspace', - 'writelines', '__exit__', '__weakref__']) + 'writelines', '__exit__', '__weakref__', 'write']) W_BZ2File.typedef = TypeDef( "BZ2File", diff --git a/pypy/module/cppyy/test/test_zjit.py b/pypy/module/cppyy/test/test_zjit.py --- a/pypy/module/cppyy/test/test_zjit.py +++ b/pypy/module/cppyy/test/test_zjit.py @@ -189,13 +189,13 @@ assert isinstance(w_obj, FakeFloat) return w_obj.val + @specialize.arg(1) def interp_w(self, RequiredClass, w_obj, can_be_None=False): if can_be_None and w_obj is None: return None if not isinstance(w_obj, RequiredClass): raise TypeError return w_obj - interp_w._annspecialcase_ = 'specialize:arg(1)' def getarg_w(self, code, w_obj): # for retrieving buffers return FakeBuffer(w_obj) diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -36,8 +36,6 @@ from rpython.rlib.objectmodel import specialize from pypy.module import exceptions from pypy.module.exceptions import interp_exceptions -# CPython 2.4 compatibility -from py.builtin import BaseException from rpython.tool.sourcetools import func_with_new_name from rpython.rtyper.lltypesystem.lloperation import llop from rpython.rlib import rawrefcount @@ -985,7 +983,7 @@ py_type_ready(space, get_capsule_type()) INIT_FUNCTIONS.append(init_types) from pypy.module.posix.interp_posix import add_fork_hook - _reinit_tls = rffi.llexternal('%sThread_ReInitTLS' % prefix, [], + _reinit_tls = rffi.llexternal('%sThread_ReInitTLS' % prefix, [], lltype.Void, compilation_info=eci) def reinit_tls(space): _reinit_tls() @@ -1614,9 +1612,8 @@ miniglobals = {'__name__': __name__, # for module name propagation } exec source.compile() in miniglobals - call_external_function = miniglobals['cpy_call_external'] + call_external_function = specialize.ll()(miniglobals['cpy_call_external']) call_external_function._dont_inline_ = True - call_external_function._annspecialcase_ = 'specialize:ll' call_external_function._gctransformer_hint_close_stack_ = True # don't inline, as a hack to guarantee that no GC pointer is alive # anywhere in call_external_function 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 @@ -137,8 +137,7 @@ """This is the same as PyDict_Merge(a, b, 1) in C, or a.update(b) in Python. Return 0 on success or -1 if an exception was raised. """ - space.call_method(space.w_dict, "update", w_obj, w_other) - return 0 + return PyDict_Merge(space, w_obj, w_other, 1) @cpython_api([PyObject], PyObject) def PyDict_Keys(space, w_obj): diff --git a/pypy/module/cpyext/listobject.py b/pypy/module/cpyext/listobject.py --- a/pypy/module/cpyext/listobject.py +++ b/pypy/module/cpyext/listobject.py @@ -62,12 +62,14 @@ position must be positive, indexing from the end of the list is not supported. If pos is out of bounds, return NULL and set an IndexError exception.""" + from pypy.module.cpyext.sequence import CPyListStrategy if not isinstance(w_list, W_ListObject): PyErr_BadInternalCall(space) if index < 0 or index >= w_list.length(): raise oefmt(space.w_IndexError, "list index out of range") - w_list.ensure_object_strategy() # make sure we can return a borrowed obj - # XXX ^^^ how does this interact with CPyListStrategy? + cpy_strategy = space.fromcache(CPyListStrategy) + if w_list.strategy is not cpy_strategy: + w_list.ensure_object_strategy() # make sure we can return a borrowed obj w_res = w_list.getitem(index) return w_res # borrowed ref diff --git a/pypy/module/cpyext/modsupport.py b/pypy/module/cpyext/modsupport.py --- a/pypy/module/cpyext/modsupport.py +++ b/pypy/module/cpyext/modsupport.py @@ -1,7 +1,7 @@ from rpython.rtyper.lltypesystem import rffi, lltype from pypy.module.cpyext.api import cpython_api, cpython_struct, \ METH_STATIC, METH_CLASS, METH_COEXIST, CANNOT_FAIL, CONST_STRING -from pypy.module.cpyext.pyobject import PyObject +from pypy.module.cpyext.pyobject import PyObject, as_pyobj from pypy.interpreter.module import Module from pypy.module.cpyext.methodobject import ( W_PyCFunctionObject, PyCFunction_NewEx, PyDescr_NewMethod, @@ -124,11 +124,17 @@ else: PyErr_BadInternalCall(space) -@cpython_api([PyObject], rffi.CCHARP, error=0) -def PyModule_GetName(space, module): +@cpython_api([PyObject], rffi.CCHARP) +def PyModule_GetName(space, w_mod): """ Return module's __name__ value. If the module does not provide one, - or if it is not a string, SystemError is raised and NULL is returned.""" - raise NotImplementedError - - + or if it is not a string, SystemError is raised and NULL is returned. + """ + # NOTE: this version of the code works only because w_mod.w_name is + # a wrapped string object attached to w_mod; so it makes a + # PyStringObject that will live as long as the module itself, + # and returns a "char *" inside this PyStringObject. + if not isinstance(w_mod, Module): + raise oefmt(space.w_SystemError, "PyModule_GetName(): not a module") + from pypy.module.cpyext.bytesobject import PyString_AsString + return PyString_AsString(space, as_pyobj(space, w_mod.w_name)) 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 @@ -252,7 +252,10 @@ def PyObject_Format(space, w_obj, w_format_spec): if w_format_spec is None: w_format_spec = space.newtext('') - return space.call_method(w_obj, '__format__', w_format_spec) + w_ret = space.call_method(w_obj, '__format__', w_format_spec) + if space.isinstance_w(w_format_spec, space.w_unicode): + return space.unicode_from_object(w_ret) + return w_ret @cpython_api([PyObject], PyObject) def PyObject_Unicode(space, w_obj): diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -15,6 +15,7 @@ from rpython.rlib.objectmodel import keepalive_until_here from rpython.rtyper.annlowlevel import llhelper from rpython.rlib import rawrefcount +from rpython.rlib.debug import fatalerror #________________________________________________________ @@ -192,6 +193,8 @@ rawrefcount.create_link_pypy(w_obj, py_obj) +w_marker_deallocating = W_Root() + def from_ref(space, ref): """ Finds the interpreter object corresponding to the given reference. If the @@ -202,7 +205,23 @@ return None w_obj = rawrefcount.to_obj(W_Root, ref) if w_obj is not None: - return w_obj + if w_obj is not w_marker_deallocating: + return w_obj + fatalerror( + "*** Invalid usage of a dying CPython object ***\n" + "\n" + "cpyext, the emulation layer, detected that while it is calling\n" + "an object's tp_dealloc, the C code calls back a function that\n" + "tries to recreate the PyPy version of the object. Usually it\n" + "means that tp_dealloc calls some general PyXxx() API. It is\n" + "a dangerous and potentially buggy thing to do: even in CPython\n" + "the PyXxx() function could, in theory, cause a reference to the\n" + "object to be taken and stored somewhere, for an amount of time\n" + "exceeding tp_dealloc itself. Afterwards, the object will be\n" + "freed, making that reference point to garbage.\n" + ">>> PyPy could contain some workaround to still work if\n" + "you are lucky, but it is not done so far; better fix the bug in\n" + "the CPython extension.") # This reference is not yet a real interpreter object. # Realize it. @@ -233,7 +252,8 @@ INTERPLEVEL_API['as_pyobj'] = as_pyobj def pyobj_has_w_obj(pyobj): - return rawrefcount.to_obj(W_Root, pyobj) is not None + w_obj = rawrefcount.to_obj(W_Root, pyobj) + return w_obj is not None and w_obj is not w_marker_deallocating INTERPLEVEL_API['pyobj_has_w_obj'] = staticmethod(pyobj_has_w_obj) @@ -335,6 +355,7 @@ pto = obj.c_ob_type #print >>sys.stderr, "Calling dealloc slot", pto.c_tp_dealloc, "of", obj, \ # "'s type which is", rffi.charp2str(pto.c_tp_name) + rawrefcount.mark_deallocating(w_marker_deallocating, obj) generic_cpy_call(space, pto.c_tp_dealloc, obj) @cpython_api([rffi.VOIDP], lltype.Signed, error=CANNOT_FAIL) 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 @@ -103,6 +103,17 @@ api.PyDict_Update(w_d, w_d2) assert space.unwrap(w_d) == dict(a='b', c='d', e='f') + def test_update_doesnt_accept_list_of_tuples(self, space, api): + w_d = space.newdict() + space.setitem(w_d, space.wrap("a"), space.wrap("b")) + + w_d2 = space.wrap([("c", "d"), ("e", "f")]) + + api.PyDict_Update(w_d, w_d2) + assert api.PyErr_Occurred() is space.w_AttributeError + api.PyErr_Clear() + assert space.unwrap(w_d) == dict(a='b') # unchanged + def test_iter(self, space, api): w_dict = space.sys.getdict(space) py_dict = make_ref(space, w_dict) @@ -199,3 +210,18 @@ """), ]) assert module.dict_proxy({'a': 1, 'b': 2}) == 2 + + def test_update(self): + module = self.import_extension('foo', [ + ("update", "METH_VARARGS", + ''' + if (PyDict_Update(PyTuple_GetItem(args, 0), PyTuple_GetItem(args, 1))) + return NULL; + Py_RETURN_NONE; + ''')]) + d = {"a": 1} + module.update(d, {"c": 2}) + assert d == dict(a=1, c=2) + d = {"a": 1} + raises(AttributeError, module.update, d, [("c", 2)]) + diff --git a/pypy/module/cpyext/test/test_module.py b/pypy/module/cpyext/test/test_module.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_module.py @@ -0,0 +1,12 @@ +from pypy.module.cpyext.test.test_api import BaseApiTest +from rpython.rtyper.lltypesystem import rffi + + +class TestModuleObject(BaseApiTest): + def test_module_getname(self, space, api): + w_sys = space.wrap(space.sys) + p = api.PyModule_GetName(w_sys) + assert rffi.charp2str(p) == 'sys' + p2 = api.PyModule_GetName(w_sys) + assert p2 == p + self.raises(space, api, SystemError, api.PyModule_GetName, space.w_True) 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 @@ -312,6 +312,16 @@ assert isinstance(dict(), collections.Mapping) assert module.ismapping(dict()) + def test_format_returns_unicode(self): + module = self.import_extension('foo', [ + ("empty_format", "METH_O", + """ + PyObject* empty_unicode = PyUnicode_FromStringAndSize("", 0); + PyObject* obj = PyObject_Format(args, empty_unicode); + return obj; + """)]) + a = module.empty_format('hello') + assert isinstance(a, unicode) class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase): """ diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py --- a/pypy/module/cpyext/test/test_typeobject.py +++ b/pypy/module/cpyext/test/test_typeobject.py @@ -331,12 +331,34 @@ PyHeapTypeObject *heaptype = (PyHeapTypeObject *)args; Py_INCREF(heaptype->ht_name); return heaptype->ht_name; + '''), + ("setattr", "METH_O", ''' - ) + int ret; + PyObject* name = PyString_FromString("mymodule"); + PyObject *obj = PyType_Type.tp_alloc(&PyType_Type, 0); + PyHeapTypeObject *type = (PyHeapTypeObject*)obj; + if ((type->ht_type.tp_flags & Py_TPFLAGS_HEAPTYPE) == 0) + { + PyErr_SetString(PyExc_ValueError, + "Py_TPFLAGS_HEAPTYPE not set"); + return NULL; + } + type->ht_type.tp_name = ((PyTypeObject*)args)->tp_name; + PyType_Ready(&type->ht_type); + ret = PyObject_SetAttrString((PyObject*)&type->ht_type, + "__module__", name); + Py_DECREF(name); + if (ret < 0) + return NULL; + return PyLong_FromLong(ret); + '''), ]) class C(object): pass assert module.name_by_heaptype(C) == "C" + assert module.setattr(C) == 0 + def test_type_dict(self): foo = self.import_module("foo") diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py --- a/pypy/module/cpyext/typeobject.py +++ b/pypy/module/cpyext/typeobject.py @@ -471,7 +471,7 @@ W_TypeObject.__init__(self, space, name, bases_w or [space.w_object], dict_w, force_new_layout=new_layout) self.flag_cpytype = True - self.flag_heaptype = False + self.flag_heaptype = pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE # if a sequence or a mapping, then set the flag to force it if pto.c_tp_as_sequence and pto.c_tp_as_sequence.c_sq_item: self.flag_map_or_seq = 'S' @@ -854,14 +854,14 @@ w_obj = space.allocate_instance(W_PyCTypeObject, w_metatype) track_reference(space, py_obj, w_obj) # __init__ wraps all slotdefs functions from py_type via add_operators - w_obj.__init__(space, py_type) + w_obj.__init__(space, py_type) w_obj.ready() finish_type_2(space, py_type, w_obj) base = py_type.c_tp_base if base: # XXX refactor - parts of this are done in finish_type_2 -> inherit_slots - if not py_type.c_tp_as_number: + if not py_type.c_tp_as_number: py_type.c_tp_as_number = base.c_tp_as_number py_type.c_tp_flags |= base.c_tp_flags & Py_TPFLAGS_CHECKTYPES py_type.c_tp_flags |= base.c_tp_flags & Py_TPFLAGS_HAVE_INPLACEOPS diff --git a/pypy/module/faulthandler/test/test_ztranslation.py b/pypy/module/faulthandler/test/test_ztranslation.py --- a/pypy/module/faulthandler/test/test_ztranslation.py +++ b/pypy/module/faulthandler/test/test_ztranslation.py @@ -1,4 +1,5 @@ from pypy.objspace.fake.checkmodule import checkmodule def test_faulthandler_translates(): + import pypy.module._vmprof.interp_vmprof # register_code_object_class() checkmodule('faulthandler') diff --git a/pypy/module/posix/interp_posix.py b/pypy/module/posix/interp_posix.py --- a/pypy/module/posix/interp_posix.py +++ b/pypy/module/posix/interp_posix.py @@ -307,7 +307,8 @@ def __init__(self, space): self.stat_float_times = True -def stat_float_times(space, w_value=None): +@unwrap_spec(newval=int) +def stat_float_times(space, newval=-1): """stat_float_times([newval]) -> oldval Determine whether os.[lf]stat represents time stamps as float objects. @@ -317,10 +318,10 @@ """ state = space.fromcache(StatState) - if w_value is None: + if newval == -1: return space.newbool(state.stat_float_times) else: - state.stat_float_times = space.bool_w(w_value) + state.stat_float_times = (newval != 0) @unwrap_spec(fd=c_int) diff --git a/pypy/module/posix/test/test_posix2.py b/pypy/module/posix/test/test_posix2.py --- a/pypy/module/posix/test/test_posix2.py +++ b/pypy/module/posix/test/test_posix2.py @@ -159,11 +159,14 @@ st = posix.stat(path) assert isinstance(st.st_mtime, float) assert st[7] == int(st.st_atime) + assert posix.stat_float_times(-1) is True posix.stat_float_times(False) st = posix.stat(path) assert isinstance(st.st_mtime, (int, long)) assert st[7] == st.st_atime + assert posix.stat_float_times(-1) is False + finally: posix.stat_float_times(current) diff --git a/pypy/module/pyexpat/interp_pyexpat.py b/pypy/module/pyexpat/interp_pyexpat.py --- a/pypy/module/pyexpat/interp_pyexpat.py +++ b/pypy/module/pyexpat/interp_pyexpat.py @@ -3,11 +3,11 @@ from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault from pypy.interpreter.error import OperationError, oefmt from rpython.rlib import rgc, jit +from rpython.rlib.objectmodel import specialize from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rtyper.tool import rffi_platform from rpython.translator.tool.cbuild import ExternalCompilationInfo from rpython.translator.platform import platform -from rpython.rlib.objectmodel import specialize import sys import weakref diff --git a/pypy/module/pypyjit/test_pypy_c/test_containers.py b/pypy/module/pypyjit/test_pypy_c/test_containers.py --- a/pypy/module/pypyjit/test_pypy_c/test_containers.py +++ b/pypy/module/pypyjit/test_pypy_c/test_containers.py @@ -67,7 +67,8 @@ p10 = call_r(ConstClass(ll_str__IntegerR_SignedConst_Signed), i5, descr=<Callr . i EF=3>) guard_no_exception(descr=...) guard_nonnull(p10, descr=...) - i12 = call_i(ConstClass(_ll_strhash__rpy_stringPtr), p10, descr=<Calli . r EF=0>) + i99 = strhash(p10) + i12 = cond_call_value_i(i99, ConstClass(_ll_strhash__rpy_stringPtr), p10, descr=<Calli . r EF=2>) p13 = new(descr=...) p15 = new_array_clear(16, descr=<ArrayU 1>) {{{ @@ -86,6 +87,7 @@ call_n(ConstClass(_ll_dict_setitem_lookup_done_trampoline), p13, p10, p20, i12, i17, descr=<Callv 0 rrrii EF=5>) setfield_gc(p20, i5, descr=<FieldS .*W_IntObject.inst_intval .* pure>) guard_no_exception(descr=...) + i98 = strhash(p10) i23 = call_i(ConstClass(ll_call_lookup_function), p13, p10, i12, 0, descr=<Calli . rrii EF=5 OS=4>) guard_no_exception(descr=...) i27 = int_lt(i23, 0) diff --git a/pypy/module/pypyjit/test_pypy_c/test_misc.py b/pypy/module/pypyjit/test_pypy_c/test_misc.py --- a/pypy/module/pypyjit/test_pypy_c/test_misc.py +++ b/pypy/module/pypyjit/test_pypy_c/test_misc.py @@ -387,7 +387,8 @@ def test_long_comparison(self): def main(n): while n: - 12345L > 123L # ID: long_op + x = 12345L + x > 123L # ID: long_op n -= 1 log = self.run(main, [300]) diff --git a/pypy/module/select/interp_epoll.py b/pypy/module/select/interp_epoll.py --- a/pypy/module/select/interp_epoll.py +++ b/pypy/module/select/interp_epoll.py @@ -79,6 +79,7 @@ class W_Epoll(W_Root): def __init__(self, space, epfd): + self.space = space self.epfd = epfd self.register_finalizer(space) @@ -113,6 +114,7 @@ if not self.get_closed(): socketclose(self.epfd) self.epfd = -1 + self.may_unregister_rpython_finalizer(self.space) def epoll_ctl(self, space, ctl, w_fd, eventmask, ignore_ebadf=False): fd = space.c_filedescriptor_w(w_fd) diff --git a/pypy/module/select/interp_kqueue.py b/pypy/module/select/interp_kqueue.py --- a/pypy/module/select/interp_kqueue.py +++ b/pypy/module/select/interp_kqueue.py @@ -108,6 +108,7 @@ class W_Kqueue(W_Root): def __init__(self, space, kqfd): + self.space = space self.kqfd = kqfd self.register_finalizer(space) @@ -132,6 +133,7 @@ kqfd = self.kqfd self.kqfd = -1 socketclose_no_errno(kqfd) + self.may_unregister_rpython_finalizer(self.space) def check_closed(self, space): if self.get_closed(): diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -202,6 +202,7 @@ if mode == rzlib.Z_FINISH: # release the data structures now rzlib.deflateEnd(self.stream) self.stream = rzlib.null_stream + self.may_unregister_rpython_finalizer(space) finally: self.unlock() except rzlib.RZlibError as e: diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py --- a/pypy/objspace/descroperation.py +++ b/pypy/objspace/descroperation.py @@ -12,65 +12,53 @@ @specialize.memo() def object_getattribute(space): "Utility that returns the app-level descriptor object.__getattribute__." - w_src, w_getattribute = space.lookup_in_type_where(space.w_object, - '__getattribute__') - return w_getattribute + return space.lookup_in_type(space.w_object, '__getattribute__') @specialize.memo() def object_setattr(space): "Utility that returns the app-level descriptor object.__setattr__." - w_src, w_setattr = space.lookup_in_type_where(space.w_object, - '__setattr__') - return w_setattr + return space.lookup_in_type(space.w_object, '__setattr__') @specialize.memo() def object_delattr(space): "Utility that returns the app-level descriptor object.__delattr__." - w_src, w_delattr = space.lookup_in_type_where(space.w_object, - '__delattr__') - return w_delattr + return space.lookup_in_type(space.w_object, '__delattr__') @specialize.memo() def object_hash(space): "Utility that returns the app-level descriptor object.__hash__." - w_src, w_hash = space.lookup_in_type_where(space.w_object, - '__hash__') - return w_hash + return space.lookup_in_type(space.w_object, '__hash__') @specialize.memo() def type_eq(space): "Utility that returns the app-level descriptor type.__eq__." - w_src, w_eq = space.lookup_in_type_where(space.w_type, - '__eq__') - return w_eq + return space.lookup_in_type(space.w_type, '__eq__') @specialize.memo() def list_iter(space): "Utility that returns the app-level descriptor list.__iter__." - w_src, w_iter = space.lookup_in_type_where(space.w_list, - '__iter__') - return w_iter + return space.lookup_in_type(space.w_list, '__iter__') @specialize.memo() def tuple_iter(space): "Utility that returns the app-level descriptor tuple.__iter__." - w_src, w_iter = space.lookup_in_type_where(space.w_tuple, - '__iter__') - return w_iter + return space.lookup_in_type(space.w_tuple, '__iter__') @specialize.memo() def str_getitem(space): "Utility that returns the app-level descriptor str.__getitem__." - w_src, w_iter = space.lookup_in_type_where(space.w_str, - '__getitem__') - return w_iter + return space.lookup_in_type(space.w_str, '__getitem__') @specialize.memo() def unicode_getitem(space): "Utility that returns the app-level descriptor unicode.__getitem__." - w_src, w_iter = space.lookup_in_type_where(space.w_unicode, - '__getitem__') - return w_iter + return space.lookup_in_type(space.w_unicode, '__getitem__') + +@specialize.memo() +def dict_getitem(space): + "Utility that returns the app-level descriptor dict.__getitem__." + return space.lookup_in_type(space.w_dict, '__getitem__') + def raiseattrerror(space, w_obj, name, w_descr=None): if w_descr is None: @@ -865,7 +853,7 @@ if not e.match(space, space.w_TypeError): raise raise oefmt(space.w_TypeError, - "%(specialname)s returned non-%(targetname)s " + "%(specialname)s returned non-string " "(type '%%T')", w_result) else: # re-wrap the result as a real string diff --git a/pypy/objspace/fake/objspace.py b/pypy/objspace/fake/objspace.py --- a/pypy/objspace/fake/objspace.py +++ b/pypy/objspace/fake/objspace.py @@ -66,15 +66,16 @@ class W_MyType(W_MyObject): name = "foobar" flag_map_or_seq = '?' + hasuserdel = False def __init__(self): self.mro_w = [w_some_obj(), w_some_obj()] self.dict_w = {'__str__': w_some_obj()} + self.hasuserdel = True def get_module(self): return w_some_obj() - def getname(self, space): return self.name @@ -206,6 +207,7 @@ newtext = newbytes newtext_or_none = newbytes + @specialize.argtype(1) def wrap(self, x): if not we_are_translated(): if isinstance(x, gateway.interp2app): @@ -219,7 +221,6 @@ return w_some_obj() self._wrap_not_rpython(x) return w_some_obj() - wrap._annspecialcase_ = "specialize:argtype(1)" def _wrap_not_rpython(self, x): "NOT_RPYTHON" @@ -309,10 +310,10 @@ is_root(w_complex) return 1.1, 2.2 + @specialize.arg(1) def allocate_instance(self, cls, w_subtype): is_root(w_subtype) return instantiate(cls) - allocate_instance._annspecialcase_ = "specialize:arg(1)" def decode_index(self, w_index_or_slice, seqlength): is_root(w_index_or_slice) @@ -334,12 +335,18 @@ def unicode_from_object(self, w_obj): return w_some_obj() + def encode_unicode_object(self, w_unicode, encoding, errors): + return w_some_obj() + def _try_fetch_pycode(self, w_func): return None def is_generator(self, w_obj): return NonConstant(False) + def lookup_in_type(self, w_type, name): + return w_some_obj() + # ---------- def translates(self, func=None, argtypes=None, seeobj_w=[], **kwds): diff --git a/pypy/objspace/fake/test/test_checkmodule.py b/pypy/objspace/fake/test/test_checkmodule.py --- a/pypy/objspace/fake/test/test_checkmodule.py +++ b/pypy/objspace/fake/test/test_checkmodule.py @@ -9,9 +9,9 @@ def make_checker(): check = [] + @specialize.memo() def see(): check.append(True) - see._annspecialcase_ = 'specialize:memo' return see, check def test_wrap_interp2app(): diff --git a/pypy/objspace/std/formatting.py b/pypy/objspace/std/formatting.py --- a/pypy/objspace/std/formatting.py +++ b/pypy/objspace/std/formatting.py @@ -2,12 +2,12 @@ import sys from rpython.rlib import jit +from rpython.rlib.objectmodel import specialize from rpython.rlib.rarithmetic import INT_MAX from rpython.rlib.rfloat import DTSF_ALT, formatd, isnan, isinf from rpython.rlib.rstring import StringBuilder, UnicodeBuilder from rpython.rlib.unroll import unrolling_iterable from rpython.tool.sourcetools import func_with_new_name -from rpython.rlib.objectmodel import specialize from pypy.interpreter.error import OperationError, oefmt diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py --- a/pypy/objspace/std/objspace.py +++ b/pypy/objspace/std/objspace.py @@ -345,6 +345,10 @@ return w_type.lookup(name) lookup._annspecialcase_ = 'specialize:lookup' + def lookup_in_type(self, w_type, name): + w_src, w_descr = self.lookup_in_type_where(w_type, name) + return w_descr + def lookup_in_type_where(self, w_type, name): return w_type.lookup_where(name) lookup_in_type_where._annspecialcase_ = 'specialize:lookup_in_type_where' @@ -632,6 +636,10 @@ from pypy.objspace.std.unicodeobject import unicode_from_object return unicode_from_object(self, w_obj) + def encode_unicode_object(self, w_unicode, encoding, errors): + from pypy.objspace.std.unicodeobject import encode_object + return encode_object(self, w_unicode, encoding, errors) + def call_method(self, w_obj, methname, *arg_w): return callmethod.call_method_opt(self, w_obj, methname, *arg_w) @@ -661,4 +669,4 @@ @specialize.arg(2, 3) def is_overloaded(self, w_obj, tp, method): return (self.lookup(w_obj, method) is not - self.lookup_in_type_where(tp, method)[1]) + self.lookup_in_type(tp, method)) diff --git a/pypy/objspace/test/test_descroperation.py b/pypy/objspace/test/test_descroperation.py --- a/pypy/objspace/test/test_descroperation.py +++ b/pypy/objspace/test/test_descroperation.py @@ -315,7 +315,8 @@ assert operate(A()) == "world" * n assert type(operate(A())) is str answer = 42 - raises(TypeError, operate, A()) + excinfo = raises(TypeError, operate, A()) + assert "returned non-string (type 'int')" in str(excinfo.value) def test_missing_getattribute(self): class X(object): diff --git a/pypy/tool/cpyext/extbuild.py b/pypy/tool/cpyext/extbuild.py --- a/pypy/tool/cpyext/extbuild.py +++ b/pypy/tool/cpyext/extbuild.py @@ -204,6 +204,10 @@ pass from distutils.ccompiler import new_compiler from distutils import sysconfig + + # XXX for Darwin running old versions of CPython 2.7.x + sysconfig.get_config_vars() + compiler = new_compiler(force=1) sysconfig.customize_compiler(compiler) # XXX objects = [] diff --git a/rpython/annotator/annrpython.py b/rpython/annotator/annrpython.py --- a/rpython/annotator/annrpython.py +++ b/rpython/annotator/annrpython.py @@ -2,6 +2,7 @@ import types from collections import defaultdict +from contextlib import contextmanager from rpython.tool.ansi_print import AnsiLogger from rpython.tool.pairtype import pair @@ -22,7 +23,8 @@ """Block annotator for RPython. See description in doc/translation.txt.""" - def __init__(self, translator=None, policy=None, bookkeeper=None): + def __init__(self, translator=None, policy=None, bookkeeper=None, + keepgoing=False): import rpython.rtyper.extfuncregistry # has side effects if translator is None: @@ -50,6 +52,9 @@ if bookkeeper is None: bookkeeper = Bookkeeper(self) self.bookkeeper = bookkeeper + self.keepgoing = keepgoing + self.failed_blocks = set() + self.errors = [] def __getstate__(self): attrs = """translator pendingblocks annotated links_followed @@ -79,22 +84,17 @@ annmodel.TLS.check_str_without_nul = ( self.translator.config.translation.check_str_without_nul) - flowgraph, inputs_s = self.get_call_parameters(function, args_s, policy) + with self.using_policy(policy): + flowgraph, inputs_s = self.get_call_parameters(function, args_s) if main_entry_point: self.translator.entry_point_graph = flowgraph return self.build_graph_types(flowgraph, inputs_s, complete_now=complete_now) - def get_call_parameters(self, function, args_s, policy): - desc = self.bookkeeper.getdesc(function) - prevpolicy = self.policy - self.policy = policy - self.bookkeeper.enter(None) - try: + def get_call_parameters(self, function, args_s): + with self.bookkeeper.at_position(None): + desc = self.bookkeeper.getdesc(function) return desc.get_call_parameters(args_s) - finally: - self.bookkeeper.leave() - self.policy = prevpolicy def annotate_helper(self, function, args_s, policy=None): if policy is None: @@ -103,21 +103,29 @@ # XXX hack annmodel.TLS.check_str_without_nul = ( self.translator.config.translation.check_str_without_nul) - graph, inputcells = self.get_call_parameters(function, args_s, policy) - self.build_graph_types(graph, inputcells, complete_now=False) - self.complete_helpers(policy) + with self.using_policy(policy): + graph, inputcells = self.get_call_parameters(function, args_s) + self.build_graph_types(graph, inputcells, complete_now=False) + self.complete_helpers() return graph - def complete_helpers(self, policy): - saved = self.policy, self.added_blocks - self.policy = policy + def complete_helpers(self): + saved = self.added_blocks + self.added_blocks = {} try: - self.added_blocks = {} self.complete() # invoke annotation simplifications for the new blocks self.simplify(block_subset=self.added_blocks) finally: - self.policy, self.added_blocks = saved + self.added_blocks = saved + + @contextmanager + def using_policy(self, policy): + """A context manager that temporarily replaces the annotator policy""" + old_policy = self.policy + self.policy = policy + yield + self.policy = old_policy def build_graph_types(self, flowgraph, inputcells, complete_now=True): checkgraph(flowgraph) @@ -202,6 +210,12 @@ else: newgraphs = self.translator.graphs #all of them got_blocked_blocks = False in self.annotated.values() + if self.failed_blocks: + text = ('Annotation failed, %s errors were recorded:' % + len(self.errors)) + text += '\n-----'.join(str(e) for e in self.errors) + raise annmodel.AnnotatorError(text) + if got_blocked_blocks: for graph in self.blocked_graphs.values(): self.blocked_graphs[graph] = True @@ -348,6 +362,8 @@ #print '* processblock', block, cells self.annotated[block] = graph + if block in self.failed_blocks: + return if block in self.blocked_blocks: del self.blocked_blocks[block] try: @@ -392,6 +408,10 @@ except annmodel.UnionError as e: # Add source code to the UnionError e.source = '\n'.join(source_lines(graph, block, None, long=True)) + if self.keepgoing: + self.errors.append(e) + self.failed_blocks.add(block) + return raise # if the merged cells changed, we must redo the analysis if unions != oldcells: @@ -482,6 +502,10 @@ except annmodel.AnnotatorError as e: # note that UnionError is a subclass e.source = gather_error(self, graph, block, i) + if self.keepgoing: + self.errors.append(e) + self.failed_blocks.add(block) + return raise else: diff --git a/rpython/annotator/bookkeeper.py b/rpython/annotator/bookkeeper.py --- a/rpython/annotator/bookkeeper.py +++ b/rpython/annotator/bookkeeper.py @@ -9,6 +9,7 @@ from collections import OrderedDict from rpython.flowspace.model import Constant +from rpython.flowspace.bytecode import cpython_code_signature from rpython.annotator.model import ( SomeOrderedDict, SomeString, SomeChar, SomeFloat, unionof, SomeInstance, SomeDict, SomeBuiltin, SomePBC, SomeInteger, TLS, SomeUnicodeCodePoint, @@ -21,6 +22,7 @@ from rpython.annotator import description from rpython.annotator.signature import annotationoftype from rpython.annotator.argument import simple_args +from rpython.annotator.specialize import memo from rpython.rlib.objectmodel import r_dict, r_ordereddict, Symbolic from rpython.tool.algo.unionfind import UnionFind from rpython.rtyper import extregistry @@ -358,7 +360,7 @@ return self.descs[obj_key] except KeyError: if isinstance(pyobj, types.FunctionType): - result = description.FunctionDesc(self, pyobj) + result = self.newfuncdesc(pyobj) elif isinstance(pyobj, (type, types.ClassType)): if pyobj is object: raise Exception("ClassDesc for object not supported") @@ -403,6 +405,23 @@ self.descs[obj_key] = result return result + def newfuncdesc(self, pyfunc): + name = pyfunc.__name__ + if hasattr(pyfunc, '_generator_next_method_of_'): + from rpython.flowspace.argument import Signature + signature = Signature(['entry']) # haaaaaack + defaults = () + else: + signature = cpython_code_signature(pyfunc.func_code) + defaults = pyfunc.func_defaults + # get the specializer based on the tag of the 'pyobj' + # (if any), according to the current policy + tag = getattr(pyfunc, '_annspecialcase_', None) + specializer = self.annotator.policy.get_specializer(tag) + if specializer is memo: + return description.MemoDesc(self, pyfunc, name, signature, defaults, specializer) + return description.FunctionDesc(self, pyfunc, name, signature, defaults, specializer) + def getfrozen(self, pyobj): return description.FrozenDesc(self, pyobj) diff --git a/rpython/annotator/classdesc.py b/rpython/annotator/classdesc.py --- a/rpython/annotator/classdesc.py +++ b/rpython/annotator/classdesc.py @@ -608,7 +608,7 @@ if mixin: # make a new copy of the FunctionDesc for this class, # but don't specialize further for all subclasses - funcdesc = FunctionDesc(self.bookkeeper, value) + funcdesc = self.bookkeeper.newfuncdesc(value) self.classdict[name] = funcdesc return # NB. if value is, say, AssertionError.__init__, then we diff --git a/rpython/annotator/description.py b/rpython/annotator/description.py --- a/rpython/annotator/description.py +++ b/rpython/annotator/description.py @@ -3,11 +3,10 @@ from rpython.annotator.signature import ( enforce_signature_args, enforce_signature_return, finish_type) from rpython.flowspace.model import FunctionGraph -from rpython.flowspace.bytecode import cpython_code_signature from rpython.annotator.argument import rawshape, ArgErr, simple_args from rpython.tool.sourcetools import valid_identifier from rpython.tool.pairtype import extendabletype -from rpython.annotator.model import AnnotatorError, s_ImpossibleValue +from rpython.annotator.model import AnnotatorError, s_ImpossibleValue, unionof class CallFamily(object): """A family of Desc objects that could be called from common call sites. @@ -117,7 +116,6 @@ self.s_value = s_ImpossibleValue # union of possible values def update(self, other): - from rpython.annotator.model import unionof self.descs.update(other.descs) self.read_locations.update(other.read_locations) self.s_value = unionof(self.s_value, other.s_value) @@ -192,24 +190,12 @@ class FunctionDesc(Desc): knowntype = types.FunctionType - def __init__(self, bookkeeper, pyobj=None, - name=None, signature=None, defaults=None, + def __init__(self, bookkeeper, pyobj, name, signature, defaults, specializer=None): super(FunctionDesc, self).__init__(bookkeeper, pyobj) - if name is None: - name = pyobj.func_name - if signature is None: - if hasattr(pyobj, '_generator_next_method_of_'): - from rpython.flowspace.argument import Signature - signature = Signature(['entry']) # haaaaaack - defaults = () - else: - signature = cpython_code_signature(pyobj.func_code) - if defaults is None: - defaults = pyobj.func_defaults self.name = name self.signature = signature - self.defaults = defaults or () + self.defaults = defaults if defaults is not None else () # 'specializer' is a function with the following signature: # specializer(funcdesc, args_s) => graph # or => s_result (overridden/memo cases) @@ -288,12 +274,43 @@ getattr(self.bookkeeper, "position_key", None) is not None): _, block, i = self.bookkeeper.position_key op = block.operations[i] - if self.specializer is None: - # get the specializer based on the tag of the 'pyobj' - # (if any), according to the current policy - tag = getattr(self.pyobj, '_annspecialcase_', None) - policy = self.bookkeeper.annotator.policy - self.specializer = policy.get_specializer(tag) + self.normalize_args(inputcells) + if getattr(self.pyobj, '_annspecialcase_', '').endswith("call_location"): + return self.specializer(self, inputcells, op) + else: + return self.specializer(self, inputcells) + + def pycall(self, whence, args, s_previous_result, op=None): + inputcells = self.parse_arguments(args) + graph = self.specialize(inputcells, op) + assert isinstance(graph, FunctionGraph) + # if that graph has a different signature, we need to re-parse + # the arguments. + # recreate the args object because inputcells may have been changed + new_args = args.unmatch_signature(self.signature, inputcells) + inputcells = self.parse_arguments(new_args, graph) + annotator = self.bookkeeper.annotator + result = annotator.recursivecall(graph, whence, inputcells) + signature = getattr(self.pyobj, '_signature_', None) + if signature: + sigresult = enforce_signature_return(self, signature[1], result) + if sigresult is not None: + annotator.addpendingblock( + graph, graph.returnblock, [sigresult]) + result = sigresult + # Some specializations may break the invariant of returning + # annotations that are always more general than the previous time. + # We restore it here: + result = unionof(result, s_previous_result) + return result + + def normalize_args(self, inputs_s): + """ + Canonicalize argument annotations into the exact parameter + annotations of a specific specialized graph. + + Note: this method has no return value but mutates its argument instead. + """ enforceargs = getattr(self.pyobj, '_annenforceargs_', None) signature = getattr(self.pyobj, '_signature_', None) if enforceargs and signature: @@ -304,39 +321,9 @@ from rpython.annotator.signature import Sig enforceargs = Sig(*enforceargs) self.pyobj._annenforceargs_ = enforceargs - enforceargs(self, inputcells) # can modify inputcells in-place + enforceargs(self, inputs_s) # can modify inputs_s in-place if signature: - enforce_signature_args(self, signature[0], inputcells) # mutates inputcells - if getattr(self.pyobj, '_annspecialcase_', '').endswith("call_location"): - return self.specializer(self, inputcells, op) - else: - return self.specializer(self, inputcells) - - def pycall(self, whence, args, s_previous_result, op=None): - inputcells = self.parse_arguments(args) - result = self.specialize(inputcells, op) - if isinstance(result, FunctionGraph): - graph = result # common case - annotator = self.bookkeeper.annotator - # if that graph has a different signature, we need to re-parse - # the arguments. - # recreate the args object because inputcells may have been changed - new_args = args.unmatch_signature(self.signature, inputcells) - inputcells = self.parse_arguments(new_args, graph) - result = annotator.recursivecall(graph, whence, inputcells) - signature = getattr(self.pyobj, '_signature_', None) - if signature: - sigresult = enforce_signature_return(self, signature[1], result) - if sigresult is not None: - annotator.addpendingblock( - graph, graph.returnblock, [sigresult]) - result = sigresult - # Some specializations may break the invariant of returning - # annotations that are always more general than the previous time. - # We restore it here: - from rpython.annotator.model import unionof - result = unionof(result, s_previous_result) - return result + enforce_signature_args(self, signature[0], inputs_s) # mutates inputs_s def get_graph(self, args, op): inputs_s = self.parse_arguments(args) @@ -405,6 +392,16 @@ return s_sigs +class MemoDesc(FunctionDesc): + def pycall(self, whence, args, s_previous_result, op=None): + inputcells = self.parse_arguments(args) + s_result = self.specialize(inputcells, op) + if isinstance(s_result, FunctionGraph): + s_result = s_result.getreturnvar().annotation + s_result = unionof(s_result, s_previous_result) + return s_result + + class MethodDesc(Desc): knowntype = types.MethodType diff --git a/rpython/annotator/specialize.py b/rpython/annotator/specialize.py --- a/rpython/annotator/specialize.py +++ b/rpython/annotator/specialize.py @@ -3,11 +3,13 @@ from rpython.tool.sourcetools import func_with_new_name from rpython.tool.algo.unionfind import UnionFind -from rpython.flowspace.model import Block, Link, Variable, SpaceOperation +from rpython.flowspace.model import Block, Link, Variable from rpython.flowspace.model import checkgraph from rpython.flowspace.operation import op from rpython.annotator import model as annmodel from rpython.flowspace.argument import Signature +from rpython.annotator.model import SomePBC, SomeImpossibleValue, SomeBool +from rpython.annotator.model import unionof def flatten_star_args(funcdesc, args_s): argnames, vararg, kwarg = funcdesc.signature @@ -127,7 +129,6 @@ def finish(self): if self.do_not_process: return - from rpython.annotator.model import unionof assert self.graph is None, "MemoTable already finished" # list of which argument positions can take more than one value example_args, example_value = self.table.iteritems().next() @@ -246,34 +247,36 @@ args_s.append(unionof(*values_s)) annotator.addpendinggraph(self.graph, args_s) +def all_values(s): + """Return the exhaustive list of possible values matching annotation `s`. -def memo(funcdesc, arglist_s): - from rpython.annotator.model import SomePBC, SomeImpossibleValue, SomeBool - from rpython.annotator.model import unionof + Raises `AnnotatorError` if no such (reasonably small) finite list exists. + """ + if s.is_constant(): + return [s.const] + elif isinstance(s, SomePBC): + values = [] + assert not s.can_be_None, "memo call: cannot mix None and PBCs" + for desc in s.descriptions: + if desc.pyobj is None: + raise annmodel.AnnotatorError( + "memo call with a class or PBC that has no " + "corresponding Python object (%r)" % (desc,)) + values.append(desc.pyobj) + return values + elif isinstance(s, SomeImpossibleValue): + return [] + elif isinstance(s, SomeBool): + return [False, True] + else: + raise annmodel.AnnotatorError("memo call: argument must be a class " + "or a frozen PBC, got %r" % (s,)) + +def memo(funcdesc, args_s): # call the function now, and collect possible results - argvalues = [] - for s in arglist_s: - if s.is_constant(): - values = [s.const] - elif isinstance(s, SomePBC): - values = [] - assert not s.can_be_None, "memo call: cannot mix None and PBCs" - for desc in s.descriptions: - if desc.pyobj is None: - raise annmodel.AnnotatorError( - "memo call with a class or PBC that has no " - "corresponding Python object (%r)" % (desc,)) - values.append(desc.pyobj) - elif isinstance(s, SomeImpossibleValue): - return s # we will probably get more possible args later - elif isinstance(s, SomeBool): - values = [False, True] - else: - raise annmodel.AnnotatorError("memo call: argument must be a class " - "or a frozen PBC, got %r" % (s,)) - argvalues.append(values) + # the list of all possible tuples of arguments to give to the memo function - possiblevalues = cartesian_product(argvalues) + possiblevalues = cartesian_product([all_values(s_arg) for s_arg in args_s]) # a MemoTable factory -- one MemoTable per family of arguments that can # be called together, merged via a UnionFind. diff --git a/rpython/annotator/test/test_model.py b/rpython/annotator/test/test_model.py --- a/rpython/annotator/test/test_model.py +++ b/rpython/annotator/test/test_model.py @@ -192,6 +192,20 @@ assert union(union(s1, s2), s3) == union(s1, union(s2, s3)) +@pytest.mark.xfail +@given(st_annotation, st_annotation) +def test_generalize_isinstance(annotator, s1, s2): + try: + s_12 = union(s1, s2) + except UnionError: + assume(False) + assume(s1 != s_ImpossibleValue) + from rpython.annotator.unaryop import s_isinstance + s_int = annotator.bookkeeper.immutablevalue(int) + s_res_12 = s_isinstance(annotator, s_12, s_int, []) + s_res_1 = s_isinstance(annotator, s1, s_int, []) + assert s_res_12.contains(s_res_1) + def compile_function(function, annotation=[]): t = TranslationContext() t.buildannotator().build_types(function, annotation) diff --git a/rpython/config/translationoption.py b/rpython/config/translationoption.py --- a/rpython/config/translationoption.py +++ b/rpython/config/translationoption.py @@ -188,6 +188,10 @@ "When true, enable the use of tagged pointers. " "If false, use normal boxing", default=False), + BoolOption("keepgoing", + "Continue annotating when errors are encountered, and report " + "them all at the end of the annotation phase", + default=False, cmdline="--keepgoing"), BoolOption("lldebug", "If true, makes an lldebug build", default=False, cmdline="--lldebug"), diff --git a/rpython/jit/backend/arm/assembler.py b/rpython/jit/backend/arm/assembler.py --- a/rpython/jit/backend/arm/assembler.py +++ b/rpython/jit/backend/arm/assembler.py @@ -268,12 +268,15 @@ """ mc = InstrBuilder(self.cpu.cpuinfo.arch_version) # - self._push_all_regs_to_jitframe(mc, [], self.cpu.supports_floats, callee_only) + # We don't save/restore r4; instead the return value (if any) + # will be stored there. + self._push_all_regs_to_jitframe(mc, [r.r4], self.cpu.supports_floats, callee_only) ## args are in their respective positions mc.PUSH([r.ip.value, r.lr.value]) mc.BLX(r.r4.value) + mc.MOV_rr(r.r4.value, r.r0.value) self._reload_frame_if_necessary(mc) - self._pop_all_regs_from_jitframe(mc, [], supports_floats, + self._pop_all_regs_from_jitframe(mc, [r.r4], supports_floats, callee_only) # return mc.POP([r.ip.value, r.pc.value]) diff --git a/rpython/jit/backend/arm/opassembler.py b/rpython/jit/backend/arm/opassembler.py --- a/rpython/jit/backend/arm/opassembler.py +++ b/rpython/jit/backend/arm/opassembler.py @@ -357,7 +357,13 @@ return fcond def emit_op_cond_call(self, op, arglocs, regalloc, fcond): - [call_loc] = arglocs + call_loc = arglocs[0] + if len(arglocs) == 2: + res_loc = arglocs[1] # cond_call_value + else: + res_loc = None # cond_call + # useless to list res_loc in the gcmap, because if the call is + # done it means res_loc was initially NULL gcmap = regalloc.get_gcmap([call_loc]) assert call_loc is r.r4 @@ -378,8 +384,13 @@ floats = True cond_call_adr = self.cond_call_slowpath[floats * 2 + callee_only] self.mc.BL(cond_call_adr) + # if this is a COND_CALL_VALUE, we need to move the result in place + # from its current location (which is, unusually, in r4: see + # cond_call_slowpath) + if res_loc is not None and res_loc is not r.r4: + self.mc.MOV_rr(res_loc.value, r.r4.value) + # self.pop_gcmap(self.mc) - # never any result value cond = c.get_opposite_of(self.guard_success_cc) self.guard_success_cc = c.cond_none pmc = OverwritingBuilder(self.mc, jmp_adr, WORD) @@ -389,6 +400,9 @@ self.previous_cond_call_jcond = jmp_adr, cond return fcond + emit_op_cond_call_value_i = emit_op_cond_call + emit_op_cond_call_value_r = emit_op_cond_call + def emit_op_jump(self, op, arglocs, regalloc, fcond): target_token = op.getdescr() assert isinstance(target_token, TargetToken) diff --git a/rpython/jit/backend/arm/regalloc.py b/rpython/jit/backend/arm/regalloc.py --- a/rpython/jit/backend/arm/regalloc.py +++ b/rpython/jit/backend/arm/regalloc.py @@ -1004,7 +1004,6 @@ def prepare_op_cond_call(self, op, fcond): # XXX don't force the arguments to be loaded in specific # locations before knowing if we can take the fast path - # XXX add cond_call_value support assert 2 <= op.numargs() <= 4 + 2 tmpreg = self.get_scratch_reg(INT, selected_reg=r.r4) v = op.getarg(1) @@ -1017,8 +1016,33 @@ arg = op.getarg(i) self.make_sure_var_in_reg(arg, args_so_far, selected_reg=reg) args_so_far.append(arg) - self.load_condition_into_cc(op.getarg(0)) - return [tmpreg] + + if op.type == 'v': + # a plain COND_CALL. Calls the function when args[0] is + # true. Often used just after a comparison operation. + self.load_condition_into_cc(op.getarg(0)) + return [tmpreg] + else: + # COND_CALL_VALUE_I/R. Calls the function when args[0] + # is equal to 0 or NULL. Returns the result from the + # function call if done, or args[0] if it was not 0/NULL. + # Implemented by forcing the result to live in the same + # register as args[0], and overwriting it if we really do + # the call. + + # Load the register for the result. Possibly reuse 'args[0]'. + # But the old value of args[0], if it survives, is first + # spilled away. We can't overwrite any of op.args[2:] here. + args = op.getarglist() + resloc = self.rm.force_result_in_reg(op, args[0], + forbidden_vars=args[2:]) + # Test the register for the result. + self.assembler.mc.CMP_ri(resloc.value, 0) + self.assembler.guard_success_cc = c.EQ + return [tmpreg, resloc] + + prepare_op_cond_call_value_i = prepare_op_cond_call + prepare_op_cond_call_value_r = prepare_op_cond_call def prepare_op_force_token(self, op, fcond): # XXX for now we return a regular reg diff --git a/rpython/jit/backend/llgraph/runner.py b/rpython/jit/backend/llgraph/runner.py --- a/rpython/jit/backend/llgraph/runner.py +++ b/rpython/jit/backend/llgraph/runner.py @@ -15,11 +15,12 @@ from rpython.rtyper.llinterp import LLInterpreter, LLException from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rstr from rpython.rtyper.lltypesystem.lloperation import llop +from rpython.rtyper.annlowlevel import hlstr, hlunicode from rpython.rtyper import rclass from rpython.rlib.clibffi import FFI_DEFAULT_ABI from rpython.rlib.rarithmetic import ovfcheck, r_uint, r_ulonglong, intmask -from rpython.rlib.objectmodel import Symbolic +from rpython.rlib.objectmodel import Symbolic, compute_hash class LLAsmInfo(object): def __init__(self, lltrace): @@ -326,7 +327,6 @@ supports_longlong = r_uint is not r_ulonglong supports_singlefloats = True supports_guard_gc_type = True - supports_cond_call_value = True translate_support_code = False is_llgraph = True vector_ext = VectorExt() @@ -789,6 +789,10 @@ assert 0 <= dststart <= dststart + length <= len(dst.chars) rstr.copy_string_contents(src, dst, srcstart, dststart, length) + def bh_strhash(self, s): + lls = s._obj.container + return compute_hash(hlstr(lls._as_ptr())) + def bh_newunicode(self, length): return lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.UNICODE, length, @@ -811,6 +815,10 @@ assert 0 <= dststart <= dststart + length <= len(dst.chars) rstr.copy_unicode_contents(src, dst, srcstart, dststart, length) + def bh_unicodehash(self, s): + lls = s._obj.container + return compute_hash(hlunicode(lls._as_ptr())) + def bh_new(self, sizedescr): return lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(sizedescr.S, zero=True)) diff --git a/rpython/jit/backend/llsupport/llmodel.py b/rpython/jit/backend/llsupport/llmodel.py --- a/rpython/jit/backend/llsupport/llmodel.py +++ b/rpython/jit/backend/llsupport/llmodel.py @@ -3,8 +3,9 @@ from rpython.rtyper.lltypesystem.lloperation import llop from rpython.rtyper.llinterp import LLInterpreter from rpython.rtyper.annlowlevel import llhelper, MixLevelHelperAnnotator +from rpython.rtyper.annlowlevel import hlstr, hlunicode from rpython.rtyper.llannotation import lltype_to_annotation -from rpython.rlib.objectmodel import we_are_translated, specialize +from rpython.rlib.objectmodel import we_are_translated, specialize, compute_hash from rpython.jit.metainterp import history, compile from rpython.jit.metainterp.optimize import SpeculativeError from rpython.jit.codewriter import heaptracker, longlong @@ -663,6 +664,14 @@ u = lltype.cast_opaque_ptr(lltype.Ptr(rstr.UNICODE), string) return len(u.chars) + def bh_strhash(self, string): + s = lltype.cast_opaque_ptr(lltype.Ptr(rstr.STR), string) + return compute_hash(hlstr(s)) + + def bh_unicodehash(self, string): + u = lltype.cast_opaque_ptr(lltype.Ptr(rstr.UNICODE), string) + return compute_hash(hlunicode(u)) + def bh_strgetitem(self, string, index): s = lltype.cast_opaque_ptr(lltype.Ptr(rstr.STR), string) return ord(s.chars[index]) diff --git a/rpython/jit/backend/llsupport/rewrite.py b/rpython/jit/backend/llsupport/rewrite.py --- a/rpython/jit/backend/llsupport/rewrite.py +++ b/rpython/jit/backend/llsupport/rewrite.py @@ -9,9 +9,9 @@ from rpython.jit.metainterp.typesystem import rd_eq, rd_hash from rpython.jit.codewriter import heaptracker from rpython.jit.backend.llsupport.symbolic import (WORD, - get_array_token) + get_field_token, get_array_token) from rpython.jit.backend.llsupport.descr import SizeDescr, ArrayDescr,\ - FLAG_POINTER, CallDescr + FLAG_POINTER from rpython.jit.metainterp.history import JitCellToken from rpython.jit.backend.llsupport.descr import (unpack_arraydescr, unpack_fielddescr, unpack_interiorfielddescr) @@ -262,6 +262,18 @@ self.cpu.translate_support_code) self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0), WORD, 1, ofs_length, NOT_SIGNED) + elif opnum == rop.STRHASH: + offset, size = get_field_token(rstr.STR, + 'hash', self.cpu.translate_support_code) + assert size == WORD + self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0), + WORD, 1, offset, sign=True) + elif opnum == rop.UNICODEHASH: + offset, size = get_field_token(rstr.UNICODE, + 'hash', self.cpu.translate_support_code) + assert size == WORD + self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0), + WORD, 1, offset, sign=True) elif opnum == rop.STRGETITEM: basesize, itemsize, ofs_length = get_array_token(rstr.STR, self.cpu.translate_support_code) @@ -347,9 +359,7 @@ self.consider_setfield_gc(op) elif op.getopnum() == rop.SETARRAYITEM_GC: self.consider_setarrayitem_gc(op) - # ---------- calls ----------- - if OpHelpers.is_plain_call(op.getopnum()): - self.expand_call_shortcut(op) + # ---------- call assembler ----------- if OpHelpers.is_call_assembler(op.getopnum()): self.handle_call_assembler(op) continue @@ -595,33 +605,6 @@ self.emit_gc_store_or_indexed(None, ptr, ConstInt(0), value, size, 1, ofs) - def expand_call_shortcut(self, op): - if not self.cpu.supports_cond_call_value: - return - descr = op.getdescr() _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit