[pypy-commit] pypy space-newtext: merge default
cfbolz
pypy.commits at gmail.com
Mon Dec 19 08:47:44 EST 2016
Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch: space-newtext
Changeset: r89180:8e2bcfb77e80
Date: 2016-12-19 14:47 +0100
http://bitbucket.org/pypy/pypy/changeset/8e2bcfb77e80/
Log: merge default
diff too long, truncating to 2000 out of 3192 lines
diff --git a/lib-python/2.7/weakref.py b/lib-python/2.7/weakref.py
--- a/lib-python/2.7/weakref.py
+++ b/lib-python/2.7/weakref.py
@@ -213,10 +213,10 @@
if o is None:
if args:
return args[0]
- raise KeyError, key
+ else:
+ raise KeyError, key
else:
return o
- # The logic above was fixed in PyPy
def setdefault(self, key, default=None):
try:
@@ -230,7 +230,6 @@
return default
else:
return o
- # The logic above was fixed in PyPy
def update(*args, **kwargs):
if not args:
diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -190,6 +190,12 @@
"make sure that all calls go through space.call_args",
default=False),
+ BoolOption("disable_entrypoints",
+ "Disable external entry points, notably the"
+ " cpyext module and cffi's embedding mode.",
+ default=False,
+ requires=[("objspace.usemodules.cpyext", False)]),
+
OptionDescription("std", "Standard Object Space Options", [
BoolOption("withtproxy", "support transparent proxies",
default=True),
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
@@ -56,3 +56,11 @@
The Cling-backend brings support for modern C++ (11, 14, etc.), dynamic
template instantations, and improved integration with CFFI for better
performance. It also provides interactive C++ (and bindings to that).
+
+.. branch: better-PyDict_Next
+
+Improve the performance of ``PyDict_Next``. When trying ``PyDict_Next`` on a
+typedef dict, the test exposed a problem converting a ``GetSetProperty`` to a
+``PyGetSetDescrObject``. The other direction seem to be fully implemented.
+This branch made a minimal effort to convert the basic fields to avoid
+segfaults, but trying to use the ``PyGetSetDescrObject`` will probably fail.
diff --git a/pypy/goal/targetpypystandalone.py b/pypy/goal/targetpypystandalone.py
--- a/pypy/goal/targetpypystandalone.py
+++ b/pypy/goal/targetpypystandalone.py
@@ -83,12 +83,18 @@
return 1
return exitcode
+ return entry_point, get_additional_entrypoints(space, w_initstdio)
+
+
+def get_additional_entrypoints(space, w_initstdio):
# register the minimal equivalent of running a small piece of code. This
# should be used as sparsely as possible, just to register callbacks
-
from rpython.rlib.entrypoint import entrypoint_highlevel
from rpython.rtyper.lltypesystem import rffi, lltype
+ if space.config.objspace.disable_entrypoints:
+ return {}
+
@entrypoint_highlevel('main', [rffi.CCHARP, rffi.INT],
c_name='pypy_setup_home')
def pypy_setup_home(ll_home, verbose):
@@ -188,11 +194,11 @@
return -1
return 0
- return entry_point, {'pypy_execute_source': pypy_execute_source,
- 'pypy_execute_source_ptr': pypy_execute_source_ptr,
- 'pypy_init_threads': pypy_init_threads,
- 'pypy_thread_attach': pypy_thread_attach,
- 'pypy_setup_home': pypy_setup_home}
+ return {'pypy_execute_source': pypy_execute_source,
+ 'pypy_execute_source_ptr': pypy_execute_source_ptr,
+ 'pypy_init_threads': pypy_init_threads,
+ 'pypy_thread_attach': pypy_thread_attach,
+ 'pypy_setup_home': pypy_setup_home}
# _____ Define and setup target ___
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -4,6 +4,7 @@
import sys
from rpython.rlib import jit
from rpython.rlib.debug import make_sure_not_resized, check_nonneg
+from rpython.rlib.debug import ll_assert_not_none
from rpython.rlib.jit import hint
from rpython.rlib.objectmodel import instantiate, specialize, we_are_translated
from rpython.rlib.rarithmetic import intmask, r_uint
@@ -298,7 +299,13 @@
# stack manipulation helpers
def pushvalue(self, w_object):
depth = self.valuestackdepth
- self.locals_cells_stack_w[depth] = w_object
+ self.locals_cells_stack_w[depth] = ll_assert_not_none(w_object)
+ self.valuestackdepth = depth + 1
+
+ def pushvalue_none(self):
+ depth = self.valuestackdepth
+ # the entry is already None, and remains None
+ assert self.locals_cells_stack_w[depth] is None
self.valuestackdepth = depth + 1
def _check_stack_index(self, index):
@@ -311,6 +318,9 @@
return index >= stackstart
def popvalue(self):
+ return ll_assert_not_none(self.popvalue_maybe_none())
+
+ def popvalue_maybe_none(self):
depth = self.valuestackdepth - 1
assert self._check_stack_index(depth)
assert depth >= 0
@@ -385,6 +395,9 @@
def peekvalue(self, index_from_top=0):
# NOTE: top of the stack is peekvalue(0).
# Contrast this with CPython where it's PEEK(-1).
+ return ll_assert_not_none(self.peekvalue_maybe_none(index_from_top))
+
+ def peekvalue_maybe_none(self, index_from_top=0):
index_from_top = hint(index_from_top, promote=True)
index = self.valuestackdepth + ~index_from_top
assert self._check_stack_index(index)
@@ -396,7 +409,7 @@
index = self.valuestackdepth + ~index_from_top
assert self._check_stack_index(index)
assert index >= 0
- self.locals_cells_stack_w[index] = w_object
+ self.locals_cells_stack_w[index] = ll_assert_not_none(w_object)
@jit.unroll_safe
def dropvaluesuntil(self, finaldepth):
diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py
--- a/pypy/module/_cffi_backend/__init__.py
+++ b/pypy/module/_cffi_backend/__init__.py
@@ -1,6 +1,6 @@
import sys
from pypy.interpreter.mixedmodule import MixedModule
-from rpython.rlib import rdynload, clibffi, entrypoint
+from rpython.rlib import rdynload, clibffi
from rpython.rtyper.lltypesystem import rffi
VERSION = "1.9.1"
@@ -68,9 +68,14 @@
if has_stdcall:
interpleveldefs['FFI_STDCALL'] = 'space.wrap(%d)' % FFI_STDCALL
- def startup(self, space):
- from pypy.module._cffi_backend import embedding
- embedding.glob.space = space
+ def __init__(self, space, *args):
+ MixedModule.__init__(self, space, *args)
+ #
+ if not space.config.objspace.disable_entrypoints:
+ # import 'embedding', which has the side-effect of registering
+ # the 'pypy_init_embedded_cffi_module' entry point
+ from pypy.module._cffi_backend import embedding
+ embedding.glob.space = space
def get_dict_rtld_constants():
@@ -85,11 +90,3 @@
for _name, _value in get_dict_rtld_constants().items():
Module.interpleveldefs[_name] = 'space.wrap(%d)' % _value
-
-
-# write this entrypoint() here, to make sure it is registered early enough
- at entrypoint.entrypoint_highlevel('main', [rffi.INT, rffi.VOIDP],
- c_name='pypy_init_embedded_cffi_module')
-def pypy_init_embedded_cffi_module(version, init_struct):
- from pypy.module._cffi_backend import embedding
- return embedding.pypy_init_embedded_cffi_module(version, init_struct)
diff --git a/pypy/module/_cffi_backend/embedding.py b/pypy/module/_cffi_backend/embedding.py
--- a/pypy/module/_cffi_backend/embedding.py
+++ b/pypy/module/_cffi_backend/embedding.py
@@ -1,4 +1,5 @@
import os
+from rpython.rlib import entrypoint
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.translator.tool.cbuild import ExternalCompilationInfo
@@ -46,6 +47,8 @@
glob = Global()
+ at entrypoint.entrypoint_highlevel('main', [rffi.INT, rffi.VOIDP],
+ c_name='pypy_init_embedded_cffi_module')
def pypy_init_embedded_cffi_module(version, init_struct):
# called from __init__.py
name = "?"
diff --git a/pypy/module/_ssl/test/test_ssl.py b/pypy/module/_ssl/test/test_ssl.py
--- a/pypy/module/_ssl/test/test_ssl.py
+++ b/pypy/module/_ssl/test/test_ssl.py
@@ -169,8 +169,8 @@
}
def setup_method(self, method):
- # https://www.verisign.net/
- ADDR = "www.verisign.net", 443
+ # https://gmail.com/
+ ADDR = "gmail.com", 443
self.w_s = self.space.appexec([self.space.wrap(ADDR)], """(ADDR):
import socket
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
@@ -602,7 +602,7 @@
GLOBALS['%s#%s' % (cpyname, pypy_decl)] = ('PyTypeObject*', pypyexpr)
for cpyname in '''PyMethodObject PyListObject PyLongObject
- PyDictObject PyClassObject'''.split():
+ PyClassObject'''.split():
FORWARD_DECLS.append('typedef struct { PyObject_HEAD } %s'
% (cpyname, ))
build_exported_objects()
diff --git a/pypy/module/cpyext/bufferobject.py b/pypy/module/cpyext/bufferobject.py
--- a/pypy/module/cpyext/bufferobject.py
+++ b/pypy/module/cpyext/bufferobject.py
@@ -31,7 +31,7 @@
dealloc=buffer_dealloc,
realize=buffer_realize)
-def buffer_attach(space, py_obj, w_obj):
+def buffer_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PyBufferObject with the given (str) buffer object.
"""
diff --git a/pypy/module/cpyext/bytearrayobject.py b/pypy/module/cpyext/bytearrayobject.py
--- a/pypy/module/cpyext/bytearrayobject.py
+++ b/pypy/module/cpyext/bytearrayobject.py
@@ -7,7 +7,7 @@
PyVarObjectFields, Py_ssize_t, CONST_STRING, CANNOT_FAIL)
from pypy.module.cpyext.pyerrors import PyErr_BadArgument
from pypy.module.cpyext.pyobject import (
- PyObject, PyObjectP, Py_DecRef, make_ref, from_ref, track_reference,
+ PyObject, PyObjectP, Py_DecRef, make_ref, from_ref,
make_typedescr, get_typedescr, Py_IncRef)
# Type PyByteArrayObject represents a mutable array of bytes.
# The Python API is that of a sequence;
diff --git a/pypy/module/cpyext/bytesobject.py b/pypy/module/cpyext/bytesobject.py
--- a/pypy/module/cpyext/bytesobject.py
+++ b/pypy/module/cpyext/bytesobject.py
@@ -73,7 +73,7 @@
py_str.c_ob_sstate = rffi.cast(rffi.INT, 0) # SSTATE_NOT_INTERNED
return py_str
-def bytes_attach(space, py_obj, w_obj):
+def bytes_attach(space, py_obj, w_obj, w_userdata=None):
"""
Copy RPython string object contents to a PyBytesObject. The
c_ob_sval must not be modified.
diff --git a/pypy/module/cpyext/complexobject.py b/pypy/module/cpyext/complexobject.py
--- a/pypy/module/cpyext/complexobject.py
+++ b/pypy/module/cpyext/complexobject.py
@@ -29,7 +29,7 @@
attach=complex_attach,
realize=complex_realize)
-def complex_attach(space, py_obj, w_obj):
+def complex_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PyComplexObject with the given complex object. The
value must not be modified.
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
@@ -1,11 +1,66 @@
from rpython.rtyper.lltypesystem import rffi, lltype
+from rpython.rlib.objectmodel import specialize
+from pypy.interpreter.error import OperationError
+from pypy.objspace.std.classdict import ClassDictStrategy
+from pypy.interpreter.typedef import GetSetProperty
from pypy.module.cpyext.api import (
cpython_api, CANNOT_FAIL, build_type_checkers, Py_ssize_t,
- Py_ssize_tP, CONST_STRING)
-from pypy.module.cpyext.pyobject import PyObject, PyObjectP, as_pyobj
+ Py_ssize_tP, CONST_STRING, PyObjectFields, cpython_struct,
+ bootstrap_function)
+from pypy.module.cpyext.pyobject import (PyObject, PyObjectP, as_pyobj,
+ make_typedescr, track_reference, create_ref, from_ref, decref,
+ Py_IncRef)
+from pypy.module.cpyext.object import _dealloc
from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
-from pypy.interpreter.error import OperationError
-from rpython.rlib.objectmodel import specialize
+
+PyDictObjectStruct = lltype.ForwardReference()
+PyDictObject = lltype.Ptr(PyDictObjectStruct)
+PyDictObjectFields = PyObjectFields + \
+ (("ob_keys", PyObject),)
+cpython_struct("PyDictObject", PyDictObjectFields, PyDictObjectStruct)
+
+ at bootstrap_function
+def init_dictobject(space):
+ "Type description of PyDictObject"
+ make_typedescr(space.w_dict.layout.typedef,
+ basestruct=PyDictObject.TO,
+ attach=dict_attach,
+ dealloc=dict_dealloc,
+ realize=dict_realize)
+
+def dict_attach(space, py_obj, w_obj, w_userdata=None):
+ """
+ Fills a newly allocated PyDictObject with the given dict object.
+ """
+ py_dict = rffi.cast(PyDictObject, py_obj)
+ py_dict.c_ob_keys = lltype.nullptr(PyObject.TO)
+ # Problems: if this dict is a typedict, we may have unbound GetSetProperty
+ # functions in the dict. The corresponding PyGetSetDescrObject must be
+ # bound to a class, but the actual w_type will be unavailable later on.
+ # Solution: use the w_userdata argument when assigning a PyTypeObject's
+ # tp_dict slot to pass a w_type in, and force creation of the pair here
+ if not space.is_w(w_userdata, space.gettypefor(GetSetProperty)):
+ # do not do this for type dict of GetSetProperty, that would recurse
+ w_vals = space.call_method(space.w_dict, "values", w_obj)
+ vals = space.listview(w_vals)
+ for w_v in vals:
+ if isinstance(w_v, GetSetProperty):
+ pyobj = as_pyobj(space, w_v, w_userdata)
+ # refcnt will be REFCNT_FROM_PYPY, no need to inc or dec
+
+def dict_realize(space, py_obj):
+ """
+ Creates the dict in the interpreter
+ """
+ w_obj = space.newdict()
+ track_reference(space, py_obj, w_obj)
+
+ at cpython_api([PyObject], lltype.Void, header=None)
+def dict_dealloc(space, py_obj):
+ py_dict = rffi.cast(PyDictObject, py_obj)
+ decref(space, py_dict.c_ob_keys)
+ py_dict.c_ob_keys = lltype.nullptr(PyObject.TO)
+ _dealloc(space, py_obj)
@cpython_api([], PyObject)
def PyDict_New(space):
@@ -181,9 +236,9 @@
}
The dictionary p should not be mutated during iteration. It is safe
- (since Python 2.1) to modify the values of the keys as you iterate over the
- dictionary, but only so long as the set of keys does not change. For
- example:
+ (since Python 2.1) to modify the values but not the keys as you iterate
+ over the dictionary, the keys must not change.
+ For example:
PyObject *key, *value;
Py_ssize_t pos = 0;
@@ -199,34 +254,32 @@
}
Py_DECREF(o);
}"""
+
if w_dict is None:
return 0
- # XXX XXX PyDict_Next is not efficient. Storing an iterator would probably
- # work, but we can't work out how to not leak it if iteration does
- # not complete. Alternatively, we could add some RPython-only
- # dict-iterator method to move forward by N steps.
-
- w_dict.ensure_object_strategy() # make sure both keys and values can
- # be borrwed
- try:
- w_iter = space.call_method(space.w_dict, "iteritems", w_dict)
- pos = ppos[0]
- while pos:
- space.call_method(w_iter, "next")
- pos -= 1
-
- w_item = space.call_method(w_iter, "next")
- w_key, w_value = space.fixedview(w_item, 2)
- if pkey:
- pkey[0] = as_pyobj(space, w_key)
- if pvalue:
- pvalue[0] = as_pyobj(space, w_value)
- ppos[0] += 1
- except OperationError as e:
- if not e.match(space, space.w_StopIteration):
- raise
+ pos = ppos[0]
+ py_obj = as_pyobj(space, w_dict)
+ py_dict = rffi.cast(PyDictObject, py_obj)
+ if pos == 0:
+ # Store the current keys in the PyDictObject.
+ decref(space, py_dict.c_ob_keys)
+ w_keys = space.call_method(space.w_dict, "keys", w_dict)
+ py_dict.c_ob_keys = create_ref(space, w_keys)
+ Py_IncRef(space, py_dict.c_ob_keys)
+ else:
+ w_keys = from_ref(space, py_dict.c_ob_keys)
+ ppos[0] += 1
+ if pos >= space.len_w(w_keys):
+ decref(space, py_dict.c_ob_keys)
+ py_dict.c_ob_keys = lltype.nullptr(PyObject.TO)
return 0
+ w_key = space.listview(w_keys)[pos]
+ w_value = space.getitem(w_dict, w_key)
+ if pkey:
+ pkey[0] = as_pyobj(space, w_key)
+ if pvalue:
+ pvalue[0] = as_pyobj(space, w_value)
return 1
@specialize.memo()
diff --git a/pypy/module/cpyext/floatobject.py b/pypy/module/cpyext/floatobject.py
--- a/pypy/module/cpyext/floatobject.py
+++ b/pypy/module/cpyext/floatobject.py
@@ -22,7 +22,7 @@
attach=float_attach,
realize=float_realize)
-def float_attach(space, py_obj, w_obj):
+def float_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PyFloatObject with the given float object. The
value must not be modified.
diff --git a/pypy/module/cpyext/frameobject.py b/pypy/module/cpyext/frameobject.py
--- a/pypy/module/cpyext/frameobject.py
+++ b/pypy/module/cpyext/frameobject.py
@@ -30,7 +30,7 @@
dealloc=frame_dealloc,
realize=frame_realize)
-def frame_attach(space, py_obj, w_obj):
+def frame_attach(space, py_obj, w_obj, w_userdata=None):
"Fills a newly allocated PyFrameObject with a frame object"
frame = space.interp_w(PyFrame, w_obj)
py_frame = rffi.cast(PyFrameObject, py_obj)
diff --git a/pypy/module/cpyext/funcobject.py b/pypy/module/cpyext/funcobject.py
--- a/pypy/module/cpyext/funcobject.py
+++ b/pypy/module/cpyext/funcobject.py
@@ -51,7 +51,7 @@
PyMethod_Check, PyMethod_CheckExact = build_type_checkers("Method", Method)
PyCode_Check, PyCode_CheckExact = build_type_checkers("Code", PyCode)
-def function_attach(space, py_obj, w_obj):
+def function_attach(space, py_obj, w_obj, w_userdata=None):
py_func = rffi.cast(PyFunctionObject, py_obj)
assert isinstance(w_obj, Function)
py_func.c_func_name = make_ref(space, space.newtext(w_obj.name))
@@ -63,7 +63,7 @@
from pypy.module.cpyext.object import _dealloc
_dealloc(space, py_obj)
-def code_attach(space, py_obj, w_obj):
+def code_attach(space, py_obj, w_obj, w_userdata=None):
py_code = rffi.cast(PyCodeObject, py_obj)
assert isinstance(w_obj, PyCode)
py_code.c_co_name = make_ref(space, space.newtext(w_obj.co_name))
diff --git a/pypy/module/cpyext/import_.py b/pypy/module/cpyext/import_.py
--- a/pypy/module/cpyext/import_.py
+++ b/pypy/module/cpyext/import_.py
@@ -1,6 +1,6 @@
from pypy.interpreter import module
from pypy.module.cpyext.api import (
- generic_cpy_call, cpython_api, PyObject, CONST_STRING)
+ generic_cpy_call, cpython_api, PyObject, CONST_STRING, CANNOT_FAIL)
from rpython.rtyper.lltypesystem import lltype, rffi
from pypy.interpreter.error import OperationError
from pypy.interpreter.module import Module
@@ -124,3 +124,22 @@
w_mod = importing.add_module(space, w_name)
space.setattr(w_mod, space.newtext('__file__'), space.newtext(pathname))
return importing.exec_code_module(space, w_mod, code, w_name)
+
+ at cpython_api([], lltype.Void, error=CANNOT_FAIL)
+def _PyImport_AcquireLock(space):
+ """Locking primitive to prevent parallel imports of the same module
+ in different threads to return with a partially loaded module.
+ These calls are serialized by the global interpreter lock."""
+ try:
+ space.call_method(space.getbuiltinmodule('imp'), 'acquire_lock')
+ except OperationError as e:
+ e.write_unraisable(space, "_PyImport_AcquireLock")
+
+ at cpython_api([], rffi.INT_real, error=CANNOT_FAIL)
+def _PyImport_ReleaseLock(space):
+ try:
+ space.call_method(space.getbuiltinmodule('imp'), 'release_lock')
+ return 1
+ except OperationError as e:
+ e.write_unraisable(space, "_PyImport_ReleaseLock")
+ return -1
diff --git a/pypy/module/cpyext/include/dictobject.h b/pypy/module/cpyext/include/dictobject.h
--- a/pypy/module/cpyext/include/dictobject.h
+++ b/pypy/module/cpyext/include/dictobject.h
@@ -7,6 +7,10 @@
extern "C" {
#endif
+typedef struct {
+ PyObject_HEAD
+ PyObject *ob_keys; /* a private place to put keys during PyDict_Next */
+} PyDictObject;
#ifdef __cplusplus
}
diff --git a/pypy/module/cpyext/intobject.py b/pypy/module/cpyext/intobject.py
--- a/pypy/module/cpyext/intobject.py
+++ b/pypy/module/cpyext/intobject.py
@@ -24,7 +24,7 @@
attach=int_attach,
realize=int_realize)
-def int_attach(space, py_obj, w_obj):
+def int_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PyIntObject with the given int object. The
value must not be modified.
diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py
--- a/pypy/module/cpyext/methodobject.py
+++ b/pypy/module/cpyext/methodobject.py
@@ -44,7 +44,7 @@
attach=cfunction_attach,
dealloc=cfunction_dealloc)
-def cfunction_attach(space, py_obj, w_obj):
+def cfunction_attach(space, py_obj, w_obj, w_userdata=None):
assert isinstance(w_obj, W_PyCFunctionObject)
py_func = rffi.cast(PyCFunctionObject, py_obj)
py_func.c_m_ml = w_obj.ml
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
@@ -61,7 +61,7 @@
pyobj.c_ob_type = pytype
return pyobj
- def attach(self, space, pyobj, w_obj):
+ def attach(self, space, pyobj, w_obj, w_userdata=None):
pass
def realize(self, space, obj):
@@ -111,8 +111,8 @@
return tp_dealloc.api_func
if tp_attach:
- def attach(self, space, pyobj, w_obj):
- tp_attach(space, pyobj, w_obj)
+ def attach(self, space, pyobj, w_obj, w_userdata=None):
+ tp_attach(space, pyobj, w_obj, w_userdata)
if tp_realize:
def realize(self, space, ref):
@@ -152,7 +152,7 @@
class InvalidPointerException(Exception):
pass
-def create_ref(space, w_obj):
+def create_ref(space, w_obj, w_userdata=None):
"""
Allocates a PyObject, and fills its fields with info from the given
interpreter object.
@@ -173,7 +173,7 @@
assert py_obj.c_ob_refcnt > rawrefcount.REFCNT_FROM_PYPY
py_obj.c_ob_refcnt -= 1
#
- typedescr.attach(space, py_obj, w_obj)
+ typedescr.attach(space, py_obj, w_obj, w_userdata)
return py_obj
def track_reference(space, py_obj, w_obj):
@@ -228,7 +228,7 @@
assert isinstance(w_type, W_TypeObject)
return get_typedescr(w_type.layout.typedef).realize(space, ref)
-def as_pyobj(space, w_obj):
+def as_pyobj(space, w_obj, w_userdata=None):
"""
Returns a 'PyObject *' representing the given intepreter object.
This doesn't give a new reference, but the returned 'PyObject *'
@@ -240,7 +240,7 @@
assert not is_pyobj(w_obj)
py_obj = rawrefcount.from_obj(PyObject, w_obj)
if not py_obj:
- py_obj = create_ref(space, w_obj)
+ py_obj = create_ref(space, w_obj, w_userdata)
return py_obj
else:
return lltype.nullptr(PyObject.TO)
@@ -269,14 +269,14 @@
return hop.inputconst(lltype.Bool, hop.s_result.const)
@specialize.ll()
-def make_ref(space, obj):
+def make_ref(space, obj, w_userdata=None):
"""Increment the reference counter of the PyObject and return it.
Can be called with either a PyObject or a W_Root.
"""
if is_pyobj(obj):
pyobj = rffi.cast(PyObject, obj)
else:
- pyobj = as_pyobj(space, obj)
+ pyobj = as_pyobj(space, obj, w_userdata)
if pyobj:
assert pyobj.c_ob_refcnt > 0
pyobj.c_ob_refcnt += 1
diff --git a/pypy/module/cpyext/pytraceback.py b/pypy/module/cpyext/pytraceback.py
--- a/pypy/module/cpyext/pytraceback.py
+++ b/pypy/module/cpyext/pytraceback.py
@@ -28,7 +28,7 @@
dealloc=traceback_dealloc)
-def traceback_attach(space, py_obj, w_obj):
+def traceback_attach(space, py_obj, w_obj, w_userdata=None):
py_traceback = rffi.cast(PyTracebackObject, py_obj)
traceback = space.interp_w(PyTraceback, w_obj)
if traceback.next is None:
diff --git a/pypy/module/cpyext/sliceobject.py b/pypy/module/cpyext/sliceobject.py
--- a/pypy/module/cpyext/sliceobject.py
+++ b/pypy/module/cpyext/sliceobject.py
@@ -25,7 +25,7 @@
attach=slice_attach,
dealloc=slice_dealloc)
-def slice_attach(space, py_obj, w_obj):
+def slice_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PySliceObject with the given slice object. The
fields must not be modified.
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
@@ -1,7 +1,7 @@
import py
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.module.cpyext.test.test_api import BaseApiTest
-from pypy.module.cpyext.api import Py_ssize_tP, PyObjectP
+from pypy.module.cpyext.api import Py_ssize_tP, PyObjectP, PyTypeObjectPtr
from pypy.module.cpyext.pyobject import make_ref, from_ref
from pypy.interpreter.error import OperationError
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
@@ -181,6 +181,27 @@
raises(OperationError, space.call_method, w_proxy, 'clear')
assert api.PyDictProxy_Check(w_proxy)
+ def test_typedict1(self, space, api):
+ py_type = make_ref(space, space.w_int)
+ py_dict = rffi.cast(PyTypeObjectPtr, py_type).c_tp_dict
+ ppos = lltype.malloc(Py_ssize_tP.TO, 1, flavor='raw')
+
+ ppos[0] = 0
+ pkey = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
+ pvalue = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
+ try:
+ w_copy = space.newdict()
+ while api.PyDict_Next(py_dict, ppos, pkey, pvalue):
+ w_key = from_ref(space, pkey[0])
+ w_value = from_ref(space, pvalue[0])
+ space.setitem(w_copy, w_key, w_value)
+ finally:
+ lltype.free(ppos, flavor='raw')
+ lltype.free(pkey, flavor='raw')
+ lltype.free(pvalue, flavor='raw')
+ api.Py_DecRef(py_type) # release borrowed references
+ # do something with w_copy ?
+
class AppTestDictObject(AppTestCpythonExtensionBase):
def test_dictproxytype(self):
module = self.import_extension('foo', [
@@ -225,3 +246,16 @@
d = {"a": 1}
raises(AttributeError, module.update, d, [("c", 2)])
+ def test_typedict2(self):
+ module = self.import_extension('foo', [
+ ("get_type_dict", "METH_O",
+ '''
+ PyObject* value = args->ob_type->tp_dict;
+ if (value == NULL) value = Py_None;
+ Py_INCREF(value);
+ return value;
+ '''),
+ ])
+ d = module.get_type_dict(1)
+ assert d['real'].__get__(1, 1) == 1
+
diff --git a/pypy/module/cpyext/test/test_import.py b/pypy/module/cpyext/test/test_import.py
--- a/pypy/module/cpyext/test/test_import.py
+++ b/pypy/module/cpyext/test/test_import.py
@@ -37,6 +37,14 @@
stat = api.PyImport_ReloadModule(stat)
assert space.getattr(stat, space.wrap("S_IMODE"))
+ def test_lock(self, space, api):
+ # "does not crash"
+ api._PyImport_AcquireLock()
+ api._PyImport_AcquireLock()
+ api._PyImport_ReleaseLock()
+ api._PyImport_ReleaseLock()
+
+
class AppTestImportLogic(AppTestCpythonExtensionBase):
def test_import_logic(self):
import sys, os
diff --git a/pypy/module/cpyext/tupleobject.py b/pypy/module/cpyext/tupleobject.py
--- a/pypy/module/cpyext/tupleobject.py
+++ b/pypy/module/cpyext/tupleobject.py
@@ -63,7 +63,7 @@
p[i] = lltype.nullptr(PyObject.TO)
return py_obj
-def tuple_attach(space, py_obj, w_obj):
+def tuple_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PyTupleObject with the given tuple object. The
buffer must not be modified.
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
@@ -32,7 +32,7 @@
from pypy.module.cpyext.state import State
from pypy.module.cpyext.structmember import PyMember_GetOne, PyMember_SetOne
from pypy.module.cpyext.typeobjectdefs import (
- PyGetSetDef, PyMemberDef, newfunc,
+ PyGetSetDef, PyMemberDef, newfunc, getter, setter,
PyNumberMethods, PyMappingMethods, PySequenceMethods, PyBufferProcs)
from pypy.objspace.std.typeobject import W_TypeObject, find_best_base
@@ -61,6 +61,7 @@
self.w_type = w_type
doc = set = get = None
if doc:
+ # XXX dead code?
doc = rffi.charp2str(getset.c_doc)
if getset.c_get:
get = GettersAndSetters.getter.im_func
@@ -73,6 +74,21 @@
def PyDescr_NewGetSet(space, getset, w_type):
return W_GetSetPropertyEx(getset, w_type)
+def make_GetSet(space, getsetprop):
+ py_getsetdef = lltype.malloc(PyGetSetDef, flavor='raw')
+ doc = getsetprop.doc
+ if doc:
+ py_getsetdef.c_doc = rffi.str2charp(doc)
+ else:
+ py_getsetdef.c_doc = rffi.cast(rffi.CCHARP, 0)
+ py_getsetdef.c_name = rffi.str2charp(getsetprop.getname(space))
+ # XXX FIXME - actually assign these !!!
+ py_getsetdef.c_get = rffi.cast(getter, 0)
+ py_getsetdef.c_set = rffi.cast(setter, 0)
+ py_getsetdef.c_closure = rffi.cast(rffi.VOIDP, 0)
+ return py_getsetdef
+
+
class W_MemberDescr(GetSetProperty):
name = 'member_descriptor'
def __init__(self, member, w_type):
@@ -160,7 +176,7 @@
realize=methoddescr_realize,
)
-def memberdescr_attach(space, py_obj, w_obj):
+def memberdescr_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PyMemberDescrObject with the given W_MemberDescr
object. The values must not be modified.
@@ -179,17 +195,21 @@
track_reference(space, obj, w_obj)
return w_obj
-def getsetdescr_attach(space, py_obj, w_obj):
+def getsetdescr_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PyGetSetDescrObject with the given W_GetSetPropertyEx
object. The values must not be modified.
"""
py_getsetdescr = rffi.cast(PyGetSetDescrObject, py_obj)
+ if isinstance(w_obj, GetSetProperty):
+ py_getsetdef = make_GetSet(space, w_obj)
+ assert space.isinstance_w(w_userdata, space.w_type)
+ w_obj = W_GetSetPropertyEx(py_getsetdef, w_userdata)
# XXX assign to d_dname, d_type?
assert isinstance(w_obj, W_GetSetPropertyEx)
py_getsetdescr.c_d_getset = w_obj.getset
-def methoddescr_attach(space, py_obj, w_obj):
+def methoddescr_attach(space, py_obj, w_obj, w_userdata=None):
py_methoddescr = rffi.cast(PyMethodDescrObject, py_obj)
# XXX assign to d_dname, d_type?
assert isinstance(w_obj, W_PyCFunctionObject)
@@ -665,7 +685,7 @@
return rffi.cast(PyObject, heaptype)
-def type_attach(space, py_obj, w_type):
+def type_attach(space, py_obj, w_type, w_userdata=None):
"""
Fills a newly allocated PyTypeObject from an existing type.
"""
@@ -892,7 +912,9 @@
if w_obj.is_cpytype():
Py_DecRef(space, pto.c_tp_dict)
w_dict = w_obj.getdict(space)
- pto.c_tp_dict = make_ref(space, w_dict)
+ # pass in the w_obj to convert any values that are
+ # unbound GetSetProperty into bound PyGetSetDescrObject
+ pto.c_tp_dict = make_ref(space, w_dict, w_obj)
@cpython_api([PyTypeObjectPtr, PyTypeObjectPtr], rffi.INT_real, error=CANNOT_FAIL)
def PyType_IsSubtype(space, a, b):
diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py
--- a/pypy/module/cpyext/unicodeobject.py
+++ b/pypy/module/cpyext/unicodeobject.py
@@ -62,7 +62,7 @@
py_uni.c_defenc = lltype.nullptr(PyObject.TO)
return py_uni
-def unicode_attach(space, py_obj, w_obj):
+def unicode_attach(space, py_obj, w_obj, w_userdata=None):
"Fills a newly allocated PyUnicodeObject with a unicode string"
py_unicode = rffi.cast(PyUnicodeObject, py_obj)
py_unicode.c_length = len(space.unicode_w(w_obj))
diff --git a/pypy/module/micronumpy/compile.py b/pypy/module/micronumpy/compile.py
--- a/pypy/module/micronumpy/compile.py
+++ b/pypy/module/micronumpy/compile.py
@@ -66,7 +66,7 @@
w_SystemExit = W_TypeObject("SystemExit")
w_KeyboardInterrupt = W_TypeObject("KeyboardInterrupt")
w_VisibleDeprecationWarning = W_TypeObject("VisibleDeprecationWarning")
- w_None = None
+ w_None = W_Root()
w_bool = W_TypeObject("bool")
w_int = W_TypeObject("int")
diff --git a/pypy/objspace/std/callmethod.py b/pypy/objspace/std/callmethod.py
--- a/pypy/objspace/std/callmethod.py
+++ b/pypy/objspace/std/callmethod.py
@@ -80,14 +80,14 @@
if w_value is None:
w_value = space.getattr(w_obj, w_name)
f.pushvalue(w_value)
- f.pushvalue(None)
+ f.pushvalue_none()
@jit.unroll_safe
def CALL_METHOD(f, oparg, *ignored):
# opargs contains the arg, and kwarg count, excluding the implicit 'self'
n_args = oparg & 0xff
n_kwargs = (oparg >> 8) & 0xff
- w_self = f.peekvalue(n_args + (2 * n_kwargs))
+ w_self = f.peekvalue_maybe_none(n_args + (2 * n_kwargs))
n = n_args + (w_self is not None)
if not n_kwargs:
@@ -115,7 +115,7 @@
arguments, keywords, keywords_w, None, None,
methodcall=w_self is not None)
if w_self is None:
- f.popvalue() # removes w_self, which is None
+ f.popvalue_maybe_none() # removes w_self, which is None
w_callable = f.popvalue()
if f.get_is_being_profiled() and function.is_builtin_code(w_callable):
w_result = f.space.call_args_and_c_profile(f, w_callable, args)
diff --git a/rpython/annotator/annrpython.py b/rpython/annotator/annrpython.py
--- a/rpython/annotator/annrpython.py
+++ b/rpython/annotator/annrpython.py
@@ -231,6 +231,12 @@
v = graph.getreturnvar()
if v.annotation is None:
self.setbinding(v, s_ImpossibleValue)
+ v = graph.exceptblock.inputargs[1]
+ if v.annotation is not None and v.annotation.can_be_none():
+ raise annmodel.AnnotatorError(
+ "%r is found by annotation to possibly raise None, "
+ "but the None was not suppressed by the flow space" %
+ (graph,))
def validate(self):
"""Check that the annotation results are valid"""
diff --git a/rpython/annotator/model.py b/rpython/annotator/model.py
--- a/rpython/annotator/model.py
+++ b/rpython/annotator/model.py
@@ -484,6 +484,9 @@
def __init__(self, classdefs):
self.classdefs = classdefs
+ def can_be_none(self):
+ return False
+
def as_SomeInstance(self):
return unionof(*[SomeInstance(cdef) for cdef in self.classdefs])
diff --git a/rpython/annotator/test/test_annrpython.py b/rpython/annotator/test/test_annrpython.py
--- a/rpython/annotator/test/test_annrpython.py
+++ b/rpython/annotator/test/test_annrpython.py
@@ -4652,6 +4652,17 @@
assert ('string formatting requires a constant string/unicode'
in str(e.value))
+ def test_cannot_raise_none(self):
+ def f(x):
+ s = None
+ if x > 5:
+ s = ValueError()
+ raise s
+ a = self.RPythonAnnotator()
+ a.build_types(f, [int])
+ s_exc = a.binding(graphof(a, f).exceptblock.inputargs[1])
+ assert not s_exc.can_be_none()
+
def g(n):
return [0, 1, 2, n]
diff --git a/rpython/flowspace/flowcontext.py b/rpython/flowspace/flowcontext.py
--- a/rpython/flowspace/flowcontext.py
+++ b/rpython/flowspace/flowcontext.py
@@ -597,6 +597,9 @@
Returns an FSException object whose w_value is an instance of w_type.
"""
+ from rpython.rlib.debug import ll_assert_not_none
+
+ check_not_none = False
w_is_type = op.isinstance(w_arg1, const(type)).eval(self)
if self.guessbool(w_is_type):
# this is for all cases of the form (Class, something)
@@ -608,6 +611,7 @@
if self.guessbool(op.issubtype(w_valuetype, w_arg1).eval(self)):
# raise Type, Instance: let etype be the exact type of value
w_value = w_arg2
+ check_not_none = True
else:
# raise Type, X: assume X is the constructor argument
w_value = op.simple_call(w_arg1, w_arg2).eval(self)
@@ -618,6 +622,10 @@
"separate value")
raise Raise(const(exc))
w_value = w_arg1
+ check_not_none = True
+ if check_not_none:
+ w_value = op.simple_call(const(ll_assert_not_none),
+ w_value).eval(self)
w_type = op.type(w_value).eval(self)
return FSException(w_type, w_value)
diff --git a/rpython/jit/codewriter/jtransform.py b/rpython/jit/codewriter/jtransform.py
--- a/rpython/jit/codewriter/jtransform.py
+++ b/rpython/jit/codewriter/jtransform.py
@@ -283,6 +283,12 @@
def rewrite_op_jit_record_exact_class(self, op):
return SpaceOperation("record_exact_class", [op.args[0], op.args[1]], None)
+ def rewrite_op_debug_assert_not_none(self, op):
+ if isinstance(op.args[0], Variable):
+ return SpaceOperation('assert_not_none', [op.args[0]], None)
+ else:
+ return []
+
def rewrite_op_cast_bool_to_int(self, op): pass
def rewrite_op_cast_bool_to_uint(self, op): pass
def rewrite_op_cast_char_to_int(self, op): pass
diff --git a/rpython/jit/codewriter/test/test_flatten.py b/rpython/jit/codewriter/test/test_flatten.py
--- a/rpython/jit/codewriter/test/test_flatten.py
+++ b/rpython/jit/codewriter/test/test_flatten.py
@@ -402,7 +402,7 @@
self.encoding_test(f, [65], """
raise $<* struct object>
- """)
+ """, transform=True)
def test_exc_raise_2(self):
def g(i):
@@ -466,6 +466,14 @@
int_return $True
""", transform=True)
+ def test_assert_disappears(self):
+ def f(i):
+ assert i > 5
+ return i
+ self.encoding_test(f, [7], """
+ int_return %i0
+ """)
+
def test_int_floordiv_ovf_zer(self):
def f(i, j):
assert i >= 0
diff --git a/rpython/jit/metainterp/blackhole.py b/rpython/jit/metainterp/blackhole.py
--- a/rpython/jit/metainterp/blackhole.py
+++ b/rpython/jit/metainterp/blackhole.py
@@ -563,6 +563,10 @@
ll_assert((i & 1) == 1, "bhimpl_cast_int_to_ptr: not an odd int")
return lltype.cast_int_to_ptr(llmemory.GCREF, i)
+ @arguments("r")
+ def bhimpl_assert_not_none(a):
+ assert a
+
@arguments("r", "i")
def bhimpl_record_exact_class(a, b):
pass
diff --git a/rpython/jit/metainterp/executor.py b/rpython/jit/metainterp/executor.py
--- a/rpython/jit/metainterp/executor.py
+++ b/rpython/jit/metainterp/executor.py
@@ -5,6 +5,7 @@
from rpython.rlib.rarithmetic import ovfcheck, r_longlong, is_valid_int
from rpython.rlib.unroll import unrolling_iterable
from rpython.rlib.objectmodel import specialize
+from rpython.rlib.debug import fatalerror
from rpython.jit.metainterp.history import check_descr
from rpython.jit.metainterp.history import INT, REF, FLOAT, VOID, AbstractDescr
from rpython.jit.metainterp.history import ConstInt, ConstFloat, ConstPtr
@@ -321,6 +322,10 @@
def do_keepalive(cpu, _, x):
pass
+def do_assert_not_none(cpu, _, box):
+ if not box.getref_base():
+ fatalerror("found during JITting: ll_assert_not_none() failed")
+
# ____________________________________________________________
diff --git a/rpython/jit/metainterp/heapcache.py b/rpython/jit/metainterp/heapcache.py
--- a/rpython/jit/metainterp/heapcache.py
+++ b/rpython/jit/metainterp/heapcache.py
@@ -230,7 +230,8 @@
opnum != rop.PTR_EQ and
opnum != rop.PTR_NE and
opnum != rop.INSTANCE_PTR_EQ and
- opnum != rop.INSTANCE_PTR_NE):
+ opnum != rop.INSTANCE_PTR_NE and
+ opnum != rop.ASSERT_NOT_NONE):
for box in argboxes:
self._escape_box(box)
@@ -263,7 +264,8 @@
opnum == rop.SETFIELD_RAW or
opnum == rop.SETARRAYITEM_RAW or
opnum == rop.SETINTERIORFIELD_RAW or
- opnum == rop.RAW_STORE):
+ opnum == rop.RAW_STORE or
+ opnum == rop.ASSERT_NOT_NONE):
return
if (rop._OVF_FIRST <= opnum <= rop._OVF_LAST or
rop._NOSIDEEFFECT_FIRST <= opnum <= rop._NOSIDEEFFECT_LAST or
@@ -371,7 +373,7 @@
def class_now_known(self, box):
if isinstance(box, Const):
return
- self._set_flag(box, HF_KNOWN_CLASS)
+ self._set_flag(box, HF_KNOWN_CLASS | HF_KNOWN_NULLITY)
def is_nullity_known(self, box):
if isinstance(box, Const):
@@ -401,7 +403,8 @@
def new(self, box):
assert isinstance(box, RefFrontendOp)
self.update_version(box)
- add_flags(box, HF_LIKELY_VIRTUAL | HF_SEEN_ALLOCATION | HF_IS_UNESCAPED)
+ add_flags(box, HF_LIKELY_VIRTUAL | HF_SEEN_ALLOCATION | HF_IS_UNESCAPED
+ | HF_KNOWN_NULLITY)
def new_array(self, box, lengthbox):
self.new(box)
diff --git a/rpython/jit/metainterp/optimizeopt/rewrite.py b/rpython/jit/metainterp/optimizeopt/rewrite.py
--- a/rpython/jit/metainterp/optimizeopt/rewrite.py
+++ b/rpython/jit/metainterp/optimizeopt/rewrite.py
@@ -499,6 +499,9 @@
box = self.get_box_replacement(op.getarg(0))
self.make_constant(box, CONST_0)
+ def optimize_ASSERT_NOT_NONE(self, op):
+ self.make_nonnull(op.getarg(0))
+
def optimize_RECORD_EXACT_CLASS(self, op):
opinfo = self.getptrinfo(op.getarg(0))
expectedclassbox = op.getarg(1)
diff --git a/rpython/jit/metainterp/optimizeopt/simplify.py b/rpython/jit/metainterp/optimizeopt/simplify.py
--- a/rpython/jit/metainterp/optimizeopt/simplify.py
+++ b/rpython/jit/metainterp/optimizeopt/simplify.py
@@ -42,6 +42,9 @@
# but it's a bit hard to implement robustly if heap.py is also run
pass
+ def optimize_ASSERT_NOT_NONE(self, op):
+ pass
+
def optimize_RECORD_EXACT_CLASS(self, op):
pass
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
@@ -5595,6 +5595,19 @@
"""
self.optimize_loop(ops, expected)
+ def test_assert_not_none(self):
+ ops = """
+ [p0]
+ assert_not_none(p0)
+ guard_nonnull(p0) []
+ finish()
+ """
+ expected = """
+ [p0]
+ finish()
+ """
+ self.optimize_loop(ops, expected)
+
class TestLLtype(BaseTestOptimizeBasic, LLtypeMixin):
pass
diff --git a/rpython/jit/metainterp/pyjitpl.py b/rpython/jit/metainterp/pyjitpl.py
--- a/rpython/jit/metainterp/pyjitpl.py
+++ b/rpython/jit/metainterp/pyjitpl.py
@@ -275,12 +275,18 @@
def opimpl_ptr_iszero(self, box):
return self.execute(rop.PTR_EQ, box, history.CONST_NULL)
+ @arguments("box")
+ def opimpl_assert_not_none(self, box):
+ if self.metainterp.heapcache.is_nullity_known(box):
+ return
+ self.execute(rop.ASSERT_NOT_NONE, box)
+ self.metainterp.heapcache.nullity_now_known(box)
+
@arguments("box", "box")
def opimpl_record_exact_class(self, box, clsbox):
from rpython.rtyper.lltypesystem import llmemory
if self.metainterp.heapcache.is_class_known(box):
return
- adr = clsbox.getaddr()
self.execute(rop.RECORD_EXACT_CLASS, box, clsbox)
self.metainterp.heapcache.class_now_known(box)
diff --git a/rpython/jit/metainterp/resoperation.py b/rpython/jit/metainterp/resoperation.py
--- a/rpython/jit/metainterp/resoperation.py
+++ b/rpython/jit/metainterp/resoperation.py
@@ -1143,6 +1143,7 @@
'COPYSTRCONTENT/5/n', # src, dst, srcstart, dststart, length
'COPYUNICODECONTENT/5/n',
'QUASIIMMUT_FIELD/1d/n', # [objptr], descr=SlowMutateDescr
+ 'ASSERT_NOT_NONE/1/n', # [objptr]
'RECORD_EXACT_CLASS/2/n', # [objptr, clsptr]
'KEEPALIVE/1/n',
'SAVE_EXCEPTION/0/r',
diff --git a/rpython/jit/metainterp/test/test_ajit.py b/rpython/jit/metainterp/test/test_ajit.py
--- a/rpython/jit/metainterp/test/test_ajit.py
+++ b/rpython/jit/metainterp/test/test_ajit.py
@@ -3510,6 +3510,7 @@
self.check_resops(call_f=1)
def test_look_inside_iff_virtual(self):
+ from rpython.rlib.debug import ll_assert_not_none
# There's no good reason for this to be look_inside_iff, but it's a test!
@look_inside_iff(lambda arg, n: isvirtual(arg))
def f(arg, n):
@@ -3529,7 +3530,7 @@
if n == 0:
i += f(a, n)
else:
- i += f(A(2), n)
+ i += f(ll_assert_not_none(A(2)), n)
res = self.meta_interp(main, [0], enable_opts='')
assert res == main(0)
self.check_resops(call_i=1, getfield_gc_i=0)
@@ -4585,3 +4586,30 @@
assert res == -42
res = self.interp_operations(f, [0, 200])
assert res == 205
+
+ def test_ll_assert_not_none(self):
+ # the presence of ll_assert_not_none(), even in cases where it
+ # doesn't influence the annotation, is a hint for the JIT
+ from rpython.rlib.debug import ll_assert_not_none
+ class X:
+ pass
+ class Y(X):
+ pass
+ def g(x, check):
+ if check:
+ x = ll_assert_not_none(x)
+ return isinstance(x, Y)
+ @dont_look_inside
+ def make(i):
+ if i == 1:
+ return X()
+ if i == 2:
+ return Y()
+ return None
+ def f(a, b, check):
+ return g(make(a), check) + g(make(b), check) * 10
+ res = self.interp_operations(f, [1, 2, 1])
+ assert res == 10
+ self.check_operations_history(guard_nonnull=0, guard_nonnull_class=0,
+ guard_class=2,
+ assert_not_none=2) # before optimization
diff --git a/rpython/jit/metainterp/test/test_heapcache.py b/rpython/jit/metainterp/test/test_heapcache.py
--- a/rpython/jit/metainterp/test/test_heapcache.py
+++ b/rpython/jit/metainterp/test/test_heapcache.py
@@ -83,6 +83,19 @@
assert not h.is_nullity_known(box1)
assert not h.is_nullity_known(box2)
+ def test_known_nullity_more_cases(self):
+ h = HeapCache()
+ box1 = RefFrontendOp(1)
+ box2 = RefFrontendOp(2)
+ h.class_now_known(box1)
+ assert h.is_nullity_known(box1)
+
+ h.new(box2)
+ assert h.is_nullity_known(box2)
+
+ h.reset()
+ assert not h.is_nullity_known(box1)
+ assert not h.is_nullity_known(box2)
def test_nonstandard_virtualizable(self):
h = HeapCache()
diff --git a/rpython/rlib/debug.py b/rpython/rlib/debug.py
--- a/rpython/rlib/debug.py
+++ b/rpython/rlib/debug.py
@@ -11,7 +11,8 @@
# Expose these here (public interface)
from rpython.rtyper.debug import (
- ll_assert, FatalError, fatalerror, fatalerror_notb, debug_print_traceback)
+ ll_assert, FatalError, fatalerror, fatalerror_notb, debug_print_traceback,
+ ll_assert_not_none)
class DebugLog(list):
diff --git a/rpython/rlib/jit.py b/rpython/rlib/jit.py
--- a/rpython/rlib/jit.py
+++ b/rpython/rlib/jit.py
@@ -1141,6 +1141,9 @@
"""
Assure the JIT that value is an instance of cls. This is a precise
class check, like a guard_class.
+
+ See also debug.ll_assert_not_none(x), which asserts that x is not None
+ and also assures the JIT that it is the case.
"""
assert type(value) is cls
diff --git a/rpython/rtyper/debug.py b/rpython/rtyper/debug.py
--- a/rpython/rtyper/debug.py
+++ b/rpython/rtyper/debug.py
@@ -20,6 +20,23 @@
hop.exception_cannot_occur()
hop.genop('debug_assert', vlist)
+def ll_assert_not_none(x):
+ """assert x is not None"""
+ assert x is not None, "ll_assert_not_none(%r)" % (x,)
+ return x
+
+class Entry(ExtRegistryEntry):
+ _about_ = ll_assert_not_none
+
+ def compute_result_annotation(self, s_x):
+ return s_x.nonnoneify()
+
+ def specialize_call(self, hop):
+ [v0] = hop.inputargs(hop.args_r[0])
+ hop.exception_cannot_occur()
+ hop.genop('debug_assert_not_none', [v0])
+ return v0
+
class FatalError(Exception):
pass
diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py
--- a/rpython/rtyper/llinterp.py
+++ b/rpython/rtyper/llinterp.py
@@ -521,6 +521,10 @@
if not x:
raise LLAssertFailure(msg)
+ def op_debug_assert_not_none(self, x):
+ if not x:
+ raise LLAssertFailure("ll_assert_not_none() failed")
+
def op_debug_fatalerror(self, ll_msg, ll_exc=None):
msg = ''.join(ll_msg.chars)
if ll_exc is None:
diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -78,7 +78,8 @@
def is_pure(self, args_v):
if self.canfold: # canfold => pure operation
return True
- if self is llop.debug_assert: # debug_assert is pure enough
+ if (self is llop.debug_assert or # debug_assert is pure enough
+ self is llop.debug_assert_not_none):
return True
# reading from immutable
if self is llop.getfield or self is llop.getarrayitem:
@@ -552,6 +553,7 @@
'debug_offset': LLOp(canrun=True),
'debug_flush': LLOp(canrun=True),
'debug_assert': LLOp(tryfold=True),
+ 'debug_assert_not_none': LLOp(tryfold=True),
'debug_fatalerror': LLOp(canrun=True),
'debug_llinterpcall': LLOp(canraise=(Exception,)),
# Python func call 'res=arg[0](*arg[1:])'
diff --git a/rpython/translator/backendopt/mallocv.py b/rpython/translator/backendopt/mallocv.py
deleted file mode 100644
--- a/rpython/translator/backendopt/mallocv.py
+++ /dev/null
@@ -1,1055 +0,0 @@
-from rpython.flowspace.model import Variable, Constant, Block, Link
-from rpython.flowspace.model import SpaceOperation, copygraph
-from rpython.flowspace.model import checkgraph
-from rpython.translator.backendopt.support import log
-from rpython.translator.simplify import join_blocks
-from rpython.translator.unsimplify import varoftype
-from rpython.rtyper.lltypesystem.lltype import getfunctionptr
-from rpython.rtyper.lltypesystem import lltype
-from rpython.rtyper.lltypesystem.lloperation import llop
-
-
-def virtualize_mallocs(translator, graphs, verbose=False):
- newgraphs = graphs[:]
- mallocv = MallocVirtualizer(newgraphs, translator.rtyper, verbose)
- while mallocv.remove_mallocs_once():
- pass
- for graph in newgraphs:
- checkgraph(graph)
- join_blocks(graph)
- assert newgraphs[:len(graphs)] == graphs
- del newgraphs[:len(graphs)]
- translator.graphs.extend(newgraphs)
-
-# ____________________________________________________________
-
-
-class MallocTypeDesc(object):
-
- def __init__(self, MALLOCTYPE):
- if not isinstance(MALLOCTYPE, lltype.GcStruct):
- raise CannotRemoveThisType
- self.MALLOCTYPE = MALLOCTYPE
- self.check_no_destructor()
- self.names_and_types = []
- self.name2index = {}
- self.name2subtype = {}
- self.initialize_type(MALLOCTYPE)
- #self.immutable_struct = MALLOCTYPE._hints.get('immutable')
-
- def check_no_destructor(self):
- STRUCT = self.MALLOCTYPE
- try:
- rttiptr = lltype.getRuntimeTypeInfo(STRUCT)
- except ValueError:
- return # ok
- destr_ptr = getattr(rttiptr._obj, 'destructor_funcptr', None)
- if destr_ptr:
- raise CannotRemoveThisType
-
- def initialize_type(self, TYPE):
- fieldnames = TYPE._names
- firstname, FIRSTTYPE = TYPE._first_struct()
- if FIRSTTYPE is not None:
- self.initialize_type(FIRSTTYPE)
- fieldnames = fieldnames[1:]
- for name in fieldnames:
- FIELDTYPE = TYPE._flds[name]
- if isinstance(FIELDTYPE, lltype.ContainerType):
- raise CannotRemoveThisType("inlined substructure")
- self.name2index[name] = len(self.names_and_types)
- self.names_and_types.append((name, FIELDTYPE))
- self.name2subtype[name] = TYPE
-
-
-class SpecNode(object):
- pass
-
-
-class RuntimeSpecNode(SpecNode):
-
- def __init__(self, name, TYPE):
- self.name = name
- self.TYPE = TYPE
-
- def newvar(self):
- v = Variable(self.name)
- v.concretetype = self.TYPE
- return v
-
- def getfrozenkey(self, memo):
- return 'R'
-
- def accumulate_nodes(self, rtnodes, vtnodes):
- rtnodes.append(self)
-
- def copy(self, memo, flagreadonly):
- return RuntimeSpecNode(self.name, self.TYPE)
-
- def bind_rt_nodes(self, memo, newnodes_iter):
- return newnodes_iter.next()
-
-
-class VirtualSpecNode(SpecNode):
-
- def __init__(self, typedesc, fields, readonly=False):
- self.typedesc = typedesc
- self.fields = fields # list of SpecNodes
- self.readonly = readonly
-
- def getfrozenkey(self, memo):
- if self in memo:
- return memo[self]
- else:
- memo[self] = len(memo)
- result = [self.typedesc, self.readonly]
- for subnode in self.fields:
- result.append(subnode.getfrozenkey(memo))
- return tuple(result)
-
- def accumulate_nodes(self, rtnodes, vtnodes):
- if self in vtnodes:
- return
- vtnodes[self] = True
- for subnode in self.fields:
- subnode.accumulate_nodes(rtnodes, vtnodes)
-
- def copy(self, memo, flagreadonly):
- if self in memo:
- return memo[self]
- readonly = self.readonly or self in flagreadonly
- newnode = VirtualSpecNode(self.typedesc, [], readonly)
- memo[self] = newnode
- for subnode in self.fields:
- newnode.fields.append(subnode.copy(memo, flagreadonly))
- return newnode
-
- def bind_rt_nodes(self, memo, newnodes_iter):
- if self in memo:
- return memo[self]
- newnode = VirtualSpecNode(self.typedesc, [], self.readonly)
- memo[self] = newnode
- for subnode in self.fields:
- newnode.fields.append(subnode.bind_rt_nodes(memo, newnodes_iter))
- return newnode
-
-
-class VirtualFrame(object):
-
- def __init__(self, sourceblock, nextopindex,
- allnodes, callerframe=None, calledgraphs={}):
- if isinstance(allnodes, dict):
- self.varlist = vars_alive_through_op(sourceblock, nextopindex)
- self.nodelist = [allnodes[v] for v in self.varlist]
- else:
- assert nextopindex == 0
- self.varlist = sourceblock.inputargs
- self.nodelist = allnodes[:]
- self.sourceblock = sourceblock
- self.nextopindex = nextopindex
- self.callerframe = callerframe
- self.calledgraphs = calledgraphs
-
- def get_nodes_in_use(self):
- return dict(zip(self.varlist, self.nodelist))
-
- def shallowcopy(self):
- newframe = VirtualFrame.__new__(VirtualFrame)
- newframe.varlist = self.varlist
- newframe.nodelist = self.nodelist
- newframe.sourceblock = self.sourceblock
- newframe.nextopindex = self.nextopindex
- newframe.callerframe = self.callerframe
- newframe.calledgraphs = self.calledgraphs
- return newframe
-
- def copy(self, memo, flagreadonly={}):
- newframe = self.shallowcopy()
- newframe.nodelist = [node.copy(memo, flagreadonly)
- for node in newframe.nodelist]
- if newframe.callerframe is not None:
- newframe.callerframe = newframe.callerframe.copy(memo,
- flagreadonly)
- return newframe
-
- def enum_call_stack(self):
- frame = self
- while frame is not None:
- yield frame
- frame = frame.callerframe
-
- def getfrozenkey(self):
- memo = {}
- key = []
- for frame in self.enum_call_stack():
- key.append(frame.sourceblock)
- key.append(frame.nextopindex)
- for node in frame.nodelist:
- key.append(node.getfrozenkey(memo))
- return tuple(key)
-
- def find_all_nodes(self):
- rtnodes = []
- vtnodes = {}
- for frame in self.enum_call_stack():
- for node in frame.nodelist:
- node.accumulate_nodes(rtnodes, vtnodes)
- return rtnodes, vtnodes
-
- def find_rt_nodes(self):
- rtnodes, vtnodes = self.find_all_nodes()
- return rtnodes
-
- def find_vt_nodes(self):
- rtnodes, vtnodes = self.find_all_nodes()
- return vtnodes
-
-
-def copynodes(nodelist, flagreadonly={}):
- memo = {}
- return [node.copy(memo, flagreadonly) for node in nodelist]
-
-def find_all_nodes(nodelist):
- rtnodes = []
- vtnodes = {}
- for node in nodelist:
- node.accumulate_nodes(rtnodes, vtnodes)
- return rtnodes, vtnodes
-
-def is_trivial_nodelist(nodelist):
- for node in nodelist:
- if not isinstance(node, RuntimeSpecNode):
- return False
- return True
-
-def bind_rt_nodes(srcnodelist, newnodes_list):
- """Return srcnodelist with all RuntimeNodes replaced by nodes
- coming from newnodes_list.
- """
- memo = {}
- newnodes_iter = iter(newnodes_list)
- result = [node.bind_rt_nodes(memo, newnodes_iter) for node in srcnodelist]
- rest = list(newnodes_iter)
- assert rest == [], "too many nodes in newnodes_list"
- return result
-
-
-class CannotVirtualize(Exception):
- pass
-
-class ForcedInline(Exception):
- pass
-
-class CannotRemoveThisType(Exception):
- pass
-
-# ____________________________________________________________
-
-
-class MallocVirtualizer(object):
-
- def __init__(self, graphs, rtyper, verbose=False):
- self.graphs = graphs
- self.rtyper = rtyper
- self.excdata = rtyper.exceptiondata
- self.graphbuilders = {}
- self.specialized_graphs = {}
- self.specgraphorigin = {}
- self.inline_and_remove = {} # {graph: op_to_remove}
- self.inline_and_remove_seen = {} # set of (graph, op_to_remove)
- self.malloctypedescs = {}
- self.count_virtualized = 0
- self.verbose = verbose
- self.EXCTYPE_to_vtable = self.build_obscure_mapping()
-
- def build_obscure_mapping(self):
- result = {}
- for rinstance in self.rtyper.instance_reprs.values():
- result[rinstance.lowleveltype.TO] = rinstance.rclass.getvtable()
- return result
-
- def report_result(self, progress):
- if progress:
- log.mallocv('removed %d mallocs so far' % self.count_virtualized)
- else:
- log.mallocv('done')
-
- def enum_all_mallocs(self, graph):
- for block in graph.iterblocks():
- for op in block.operations:
- if op.opname == 'malloc':
- MALLOCTYPE = op.result.concretetype.TO
- try:
- self.getmalloctypedesc(MALLOCTYPE)
- except CannotRemoveThisType:
- pass
- else:
- yield (block, op)
- elif op.opname == 'direct_call':
- graph = graph_called_by(op)
- if graph in self.inline_and_remove:
- yield (block, op)
-
- def remove_mallocs_once(self):
- self.flush_failed_specializations()
- prev = self.count_virtualized
- count_inline_and_remove = len(self.inline_and_remove)
- for graph in self.graphs:
- seen = {}
- while True:
- for block, op in self.enum_all_mallocs(graph):
- if op.result not in seen:
- seen[op.result] = True
- if self.try_remove_malloc(graph, block, op):
- break # graph mutated, restart enum_all_mallocs()
- else:
- break # enum_all_mallocs() exhausted, graph finished
- progress1 = self.count_virtualized - prev
- progress2 = len(self.inline_and_remove) - count_inline_and_remove
- progress = progress1 or bool(progress2)
- self.report_result(progress)
- return progress
-
- def flush_failed_specializations(self):
- for key, (mode, specgraph) in self.specialized_graphs.items():
- if mode == 'fail':
- del self.specialized_graphs[key]
-
- def fixup_except_block(self, exceptblock):
- # hack: this block's inputargs may be missing concretetypes...
- e1, v1 = exceptblock.inputargs
- e1.concretetype = self.excdata.lltype_of_exception_type
- v1.concretetype = self.excdata.lltype_of_exception_value
-
- def getmalloctypedesc(self, MALLOCTYPE):
- try:
- dsc = self.malloctypedescs[MALLOCTYPE]
- except KeyError:
- dsc = self.malloctypedescs[MALLOCTYPE] = MallocTypeDesc(MALLOCTYPE)
- return dsc
-
- def try_remove_malloc(self, graph, block, op):
- if (graph, op) in self.inline_and_remove_seen:
- return False # no point in trying again
- graphbuilder = GraphBuilder(self, graph)
- if graph in self.graphbuilders:
- graphbuilder.initialize_from_old_builder(self.graphbuilders[graph])
- graphbuilder.start_from_a_malloc(graph, block, op.result)
- try:
- graphbuilder.propagate_specializations()
- except CannotVirtualize as e:
- self.logresult(op, 'failed', e)
- return False
- except ForcedInline as e:
- self.logresult(op, 'forces inlining', e)
- self.inline_and_remove[graph] = op
- self.inline_and_remove_seen[graph, op] = True
- return False
- else:
- self.logresult(op, 'removed')
- graphbuilder.finished_removing_malloc()
- self.graphbuilders[graph] = graphbuilder
- self.count_virtualized += 1
- return True
-
- def logresult(self, op, msg, exc=None): # only for nice log outputs
- if self.verbose:
- if exc is None:
- exc = ''
- else:
- exc = ': %s' % (exc,)
- chain = []
- while True:
- chain.append(str(op.result))
- if op.opname != 'direct_call':
- break
- fobj = op.args[0].value._obj
- op = self.inline_and_remove[fobj.graph]
- log.mallocv('%s %s%s' % ('->'.join(chain), msg, exc))
- elif exc is None:
- log.dot()
-
- def get_specialized_graph(self, graph, nodelist):
- assert len(graph.getargs()) == len(nodelist)
- if is_trivial_nodelist(nodelist):
- return 'trivial', graph
- if graph in self.specgraphorigin:
- orggraph, orgnodelist = self.specgraphorigin[graph]
- nodelist = bind_rt_nodes(orgnodelist, nodelist)
- graph = orggraph
- virtualframe = VirtualFrame(graph.startblock, 0, nodelist)
- key = virtualframe.getfrozenkey()
- try:
- return self.specialized_graphs[key]
- except KeyError:
- self.build_specialized_graph(graph, key, nodelist)
- return self.specialized_graphs[key]
-
- def build_specialized_graph(self, graph, key, nodelist):
- graph2 = copygraph(graph)
- virtualframe = VirtualFrame(graph2.startblock, 0, nodelist)
- graphbuilder = GraphBuilder(self, graph2)
- specblock = graphbuilder.start_from_virtualframe(virtualframe)
- specgraph = graph2
- specgraph.name += '_mallocv'
- specgraph.startblock = specblock
- self.specialized_graphs[key] = ('call', specgraph)
- try:
- graphbuilder.propagate_specializations()
- except ForcedInline as e:
- if self.verbose:
- log.mallocv('%s inlined: %s' % (graph.name, e))
- self.specialized_graphs[key] = ('inline', None)
- except CannotVirtualize as e:
- if self.verbose:
- log.mallocv('%s failing: %s' % (graph.name, e))
- self.specialized_graphs[key] = ('fail', None)
- else:
- self.graphbuilders[specgraph] = graphbuilder
- self.specgraphorigin[specgraph] = graph, nodelist
- self.graphs.append(specgraph)
-
-
-class GraphBuilder(object):
-
- def __init__(self, mallocv, graph):
- self.mallocv = mallocv
- self.graph = graph
- self.specialized_blocks = {}
- self.pending_specializations = []
-
- def initialize_from_old_builder(self, oldbuilder):
- self.specialized_blocks.update(oldbuilder.specialized_blocks)
-
- def start_from_virtualframe(self, startframe):
- spec = BlockSpecializer(self)
- spec.initialize_renamings(startframe)
- self.pending_specializations.append(spec)
- return spec.specblock
-
- def start_from_a_malloc(self, graph, block, v_result):
- assert v_result in [op.result for op in block.operations]
- nodelist = []
- for v in block.inputargs:
- nodelist.append(RuntimeSpecNode(v, v.concretetype))
- trivialframe = VirtualFrame(block, 0, nodelist)
- spec = BlockSpecializer(self, v_result)
- spec.initialize_renamings(trivialframe, keep_inputargs=True)
- self.pending_specializations.append(spec)
- self.pending_patch = (block, spec.specblock)
-
- def finished_removing_malloc(self):
- (srcblock, specblock) = self.pending_patch
- srcblock.inputargs = specblock.inputargs
- srcblock.operations = specblock.operations
- srcblock.exitswitch = specblock.exitswitch
- srcblock.recloseblock(*specblock.exits)
-
- def create_outgoing_link(self, currentframe, targetblock,
- nodelist, renamings, v_expand_malloc=None):
- assert len(nodelist) == len(targetblock.inputargs)
- #
- if is_except(targetblock):
- v_expand_malloc = None
- while currentframe.callerframe is not None:
- currentframe = currentframe.callerframe
- newlink = self.handle_catch(currentframe, nodelist, renamings)
- if newlink:
- return newlink
- else:
- targetblock = self.exception_escapes(nodelist, renamings)
- assert len(nodelist) == len(targetblock.inputargs)
-
- if (currentframe.callerframe is None and
- is_trivial_nodelist(nodelist)):
- # there is no more VirtualSpecNodes being passed around,
- # so we can stop specializing
- rtnodes = nodelist
- specblock = targetblock
- else:
- if is_return(targetblock):
- v_expand_malloc = None
- newframe = self.return_to_caller(currentframe, nodelist[0])
- else:
- targetnodes = dict(zip(targetblock.inputargs, nodelist))
- newframe = VirtualFrame(targetblock, 0, targetnodes,
- callerframe=currentframe.callerframe,
- calledgraphs=currentframe.calledgraphs)
- rtnodes = newframe.find_rt_nodes()
- specblock = self.get_specialized_block(newframe, v_expand_malloc)
-
- linkargs = [renamings[rtnode] for rtnode in rtnodes]
- return Link(linkargs, specblock)
-
- def return_to_caller(self, currentframe, retnode):
- callerframe = currentframe.callerframe
- if callerframe is None:
- raise ForcedInline("return block")
- nodelist = callerframe.nodelist
- callerframe = callerframe.shallowcopy()
- callerframe.nodelist = []
- for node in nodelist:
- if isinstance(node, FutureReturnValue):
- node = retnode
- callerframe.nodelist.append(node)
- return callerframe
-
- def handle_catch(self, catchingframe, nodelist, renamings):
- if not self.has_exception_catching(catchingframe):
- return None
- [exc_node, exc_value_node] = nodelist
- v_exc_type = renamings.get(exc_node)
- if isinstance(v_exc_type, Constant):
- exc_type = v_exc_type.value
- elif isinstance(exc_value_node, VirtualSpecNode):
- EXCTYPE = exc_value_node.typedesc.MALLOCTYPE
- exc_type = self.mallocv.EXCTYPE_to_vtable[EXCTYPE]
- else:
- raise CannotVirtualize("raising non-constant exc type")
- excdata = self.mallocv.excdata
- assert catchingframe.sourceblock.exits[0].exitcase is None
- for catchlink in catchingframe.sourceblock.exits[1:]:
- if excdata.fn_exception_match(exc_type, catchlink.llexitcase):
- # Match found. Follow this link.
- mynodes = catchingframe.get_nodes_in_use()
- for node, attr in zip(nodelist,
- ['last_exception', 'last_exc_value']):
- v = getattr(catchlink, attr)
- if isinstance(v, Variable):
- mynodes[v] = node
- #
- nodelist = []
- for v in catchlink.args:
- if isinstance(v, Variable):
- node = mynodes[v]
- else:
- node = getconstnode(v, renamings)
- nodelist.append(node)
- return self.create_outgoing_link(catchingframe,
- catchlink.target,
- nodelist, renamings)
- else:
- # No match at all, propagate the exception to the caller
- return None
-
- def has_exception_catching(self, catchingframe):
- if not catchingframe.sourceblock.canraise:
- return False
- else:
- operations = catchingframe.sourceblock.operations
- assert 1 <= catchingframe.nextopindex <= len(operations)
- return catchingframe.nextopindex == len(operations)
-
- def exception_escapes(self, nodelist, renamings):
- # the exception escapes
- if not is_trivial_nodelist(nodelist):
- # start of hacks to help handle_catch()
- [exc_node, exc_value_node] = nodelist
- v_exc_type = renamings.get(exc_node)
- if isinstance(v_exc_type, Constant):
- # cannot improve: handle_catch() would already be happy
- # by seeing the exc_type as a constant
- pass
- elif isinstance(exc_value_node, VirtualSpecNode):
- # can improve with a strange hack: we pretend that
- # the source code jumps to a block that itself allocates
- # the exception, sets all fields, and raises it by
- # passing a constant type.
- typedesc = exc_value_node.typedesc
- return self.get_exc_reconstruction_block(typedesc)
- else:
- # cannot improve: handle_catch() will have no clue about
- # the exception type
- pass
- raise CannotVirtualize("except block")
- targetblock = self.graph.exceptblock
- self.mallocv.fixup_except_block(targetblock)
- return targetblock
-
- def get_exc_reconstruction_block(self, typedesc):
- exceptblock = self.graph.exceptblock
- self.mallocv.fixup_except_block(exceptblock)
- TEXC = exceptblock.inputargs[0].concretetype
- TVAL = exceptblock.inputargs[1].concretetype
- #
- v_ignored_type = varoftype(TEXC)
- v_incoming_value = varoftype(TVAL)
- block = Block([v_ignored_type, v_incoming_value])
- #
- c_EXCTYPE = Constant(typedesc.MALLOCTYPE, lltype.Void)
- v = varoftype(lltype.Ptr(typedesc.MALLOCTYPE))
- c_flavor = Constant({'flavor': 'gc'}, lltype.Void)
- op = SpaceOperation('malloc', [c_EXCTYPE, c_flavor], v)
- block.operations.append(op)
- #
- for name, FIELDTYPE in typedesc.names_and_types:
- EXACTPTR = lltype.Ptr(typedesc.name2subtype[name])
- c_name = Constant(name)
- c_name.concretetype = lltype.Void
- #
- v_in = varoftype(EXACTPTR)
- op = SpaceOperation('cast_pointer', [v_incoming_value], v_in)
- block.operations.append(op)
- #
- v_field = varoftype(FIELDTYPE)
- op = SpaceOperation('getfield', [v_in, c_name], v_field)
- block.operations.append(op)
- #
- v_out = varoftype(EXACTPTR)
- op = SpaceOperation('cast_pointer', [v], v_out)
- block.operations.append(op)
- #
- v0 = varoftype(lltype.Void)
- op = SpaceOperation('setfield', [v_out, c_name, v_field], v0)
- block.operations.append(op)
- #
- v_exc_value = varoftype(TVAL)
- op = SpaceOperation('cast_pointer', [v], v_exc_value)
- block.operations.append(op)
- #
- exc_type = self.mallocv.EXCTYPE_to_vtable[typedesc.MALLOCTYPE]
- c_exc_type = Constant(exc_type, TEXC)
- block.closeblock(Link([c_exc_type, v_exc_value], exceptblock))
- return block
-
- def get_specialized_block(self, virtualframe, v_expand_malloc=None):
- key = virtualframe.getfrozenkey()
- specblock = self.specialized_blocks.get(key)
- if specblock is None:
- orgblock = virtualframe.sourceblock
- assert len(orgblock.exits) != 0
- spec = BlockSpecializer(self, v_expand_malloc)
- spec.initialize_renamings(virtualframe)
- self.pending_specializations.append(spec)
- specblock = spec.specblock
- self.specialized_blocks[key] = specblock
- return specblock
-
- def propagate_specializations(self):
- while self.pending_specializations:
- spec = self.pending_specializations.pop()
- spec.specialize_operations()
- spec.follow_exits()
-
-
-class BlockSpecializer(object):
-
- def __init__(self, graphbuilder, v_expand_malloc=None):
- self.graphbuilder = graphbuilder
- self.v_expand_malloc = v_expand_malloc
- self.specblock = Block([])
-
- def initialize_renamings(self, virtualframe, keep_inputargs=False):
- # we make a copy of the original 'virtualframe' because the
- # specialize_operations() will mutate some of its content.
- virtualframe = virtualframe.copy({})
- self.virtualframe = virtualframe
- self.nodes = virtualframe.get_nodes_in_use()
- self.renamings = {} # {RuntimeSpecNode(): Variable()}
- if keep_inputargs:
- assert virtualframe.varlist == virtualframe.sourceblock.inputargs
- specinputargs = []
- for i, rtnode in enumerate(virtualframe.find_rt_nodes()):
- if keep_inputargs:
- v = virtualframe.varlist[i]
- assert v.concretetype == rtnode.TYPE
- else:
- v = rtnode.newvar()
- self.renamings[rtnode] = v
- specinputargs.append(v)
- self.specblock.inputargs = specinputargs
-
- def setnode(self, v, node):
- assert v not in self.nodes
- self.nodes[v] = node
-
- def getnode(self, v):
- if isinstance(v, Variable):
- return self.nodes[v]
More information about the pypy-commit
mailing list