[pypy-commit] pypy space-newtext: merge default
cfbolz
pypy.commits at gmail.com
Wed Nov 30 10:37:22 EST 2016
Author: Carl Friedrich Bolz <cfbolz at 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)
+ at 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)'
+ at 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)
- at cpython_api([PyObject], rffi.CCHARP, error=0)
-def PyModule_GetName(space, module):
+ at 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):
+ at 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__')
+
+ at 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))
+ at pytest.mark.xfail
+ at 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()
More information about the pypy-commit
mailing list