[pypy-commit] pypy reflex-support: merge default into branch
wlav
noreply at buildbot.pypy.org
Tue Aug 14 02:18:30 CEST 2012
Author: Wim Lavrijsen <WLavrijsen at lbl.gov>
Branch: reflex-support
Changeset: r56724:be67c06ffac9
Date: 2012-08-13 15:25 -0700
http://bitbucket.org/pypy/pypy/changeset/be67c06ffac9/
Log: merge default into branch
diff too long, truncating to 10000 out of 12084 lines
diff --git a/lib_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py
--- a/lib_pypy/_ctypes/basics.py
+++ b/lib_pypy/_ctypes/basics.py
@@ -59,7 +59,8 @@
'resbuffer' is a _rawffi array of length 1 containing the value,
and this returns a general Python object that corresponds.
"""
- res = self.__new__(self)
+ res = object.__new__(self)
+ res.__class__ = self
res.__dict__['_buffer'] = resbuffer
res.__dict__['_base'] = base
res.__dict__['_index'] = index
diff --git a/lib_pypy/_marshal.py b/lib_pypy/_marshal.py
--- a/lib_pypy/_marshal.py
+++ b/lib_pypy/_marshal.py
@@ -430,6 +430,7 @@
def _read(self, n):
pos = self.bufpos
newpos = pos + n
+ if newpos > len(self.bufstr): raise EOFError
ret = self.bufstr[pos : newpos]
self.bufpos = newpos
return ret
diff --git a/lib_pypy/greenlet.py b/lib_pypy/greenlet.py
--- a/lib_pypy/greenlet.py
+++ b/lib_pypy/greenlet.py
@@ -77,8 +77,6 @@
try:
unbound_method = getattr(_continulet, methodname)
args = unbound_method(current, *args, to=target)
- except GreenletExit, e:
- args = (e,)
finally:
_tls.current = current
#
@@ -132,6 +130,8 @@
_tls.current = greenlet
try:
res = greenlet.run(*args)
+ except GreenletExit, e:
+ res = e
finally:
_continuation.permute(greenlet, greenlet.parent)
return (res,)
diff --git a/lib_pypy/pypy_test/test_marshal_extra.py b/lib_pypy/pypy_test/test_marshal_extra.py
--- a/lib_pypy/pypy_test/test_marshal_extra.py
+++ b/lib_pypy/pypy_test/test_marshal_extra.py
@@ -142,4 +142,6 @@
f2.close()
assert obj == case
-
+def test_load_truncated_string():
+ s = '(\x02\x00\x00\x00i\x03\x00\x00\x00sB\xf9\x00\x00\nabcd'
+ py.test.raises(EOFError, marshal.loads, s)
diff --git a/pypy/annotation/description.py b/pypy/annotation/description.py
--- a/pypy/annotation/description.py
+++ b/pypy/annotation/description.py
@@ -450,6 +450,12 @@
attrs.update(self.basedesc.all_enforced_attrs)
self.all_enforced_attrs = attrs
+ if (self.is_builtin_exception_class() and
+ self.all_enforced_attrs is None):
+ from pypy.annotation import classdef
+ if self.pyobj not in classdef.FORCE_ATTRIBUTES_INTO_CLASSES:
+ self.all_enforced_attrs = [] # no attribute allowed
+
def add_source_attribute(self, name, value, mixin=False):
if isinstance(value, types.FunctionType):
# for debugging
diff --git a/pypy/annotation/test/test_annrpython.py b/pypy/annotation/test/test_annrpython.py
--- a/pypy/annotation/test/test_annrpython.py
+++ b/pypy/annotation/test/test_annrpython.py
@@ -3841,6 +3841,14 @@
assert len(a.translator.graphs) == 3 # fn, __iter__, next
assert isinstance(s, annmodel.SomeInteger)
+ def test_no_attr_on_common_exception_classes(self):
+ for cls in [ValueError, Exception]:
+ def fn():
+ e = cls()
+ e.foo = "bar"
+ a = self.RPythonAnnotator()
+ py.test.raises(Exception, a.build_types, fn, [])
+
def g(n):
return [0,1,2,n]
diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -34,7 +34,7 @@
"thread", "itertools", "pyexpat", "_ssl", "cpyext", "array",
"_bisect", "binascii", "_multiprocessing", '_warnings',
"_collections", "_multibytecodec", "micronumpy", "_ffi",
- "_continuation"]
+ "_continuation", "_cffi_backend"]
))
translation_modules = default_modules.copy()
@@ -89,7 +89,6 @@
"_rawffi": [("objspace.usemodules.struct", True)],
"cpyext": [("translation.secondaryentrypoints", "cpyext"),
("translation.shared", sys.platform == "win32")],
- "_ffi": [("translation.jit_ffi", True)],
}
module_import_dependencies = {
diff --git a/pypy/config/test/test_pypyoption.py b/pypy/config/test/test_pypyoption.py
--- a/pypy/config/test/test_pypyoption.py
+++ b/pypy/config/test/test_pypyoption.py
@@ -72,8 +72,3 @@
for path in c.getpaths(include_groups=True):
fn = prefix + "." + path + ".txt"
yield fn, check_file_exists, fn
-
-def test__ffi_opt():
- config = get_pypy_config(translating=True)
- config.objspace.usemodules._ffi = True
- assert config.translation.jit_ffi
diff --git a/pypy/config/translationoption.py b/pypy/config/translationoption.py
--- a/pypy/config/translationoption.py
+++ b/pypy/config/translationoption.py
@@ -122,8 +122,6 @@
ChoiceOption("jit_profiler", "integrate profiler support into the JIT",
["off", "oprofile"],
default="off"),
- # jit_ffi is automatically turned on by withmod-_ffi (which is enabled by default)
- BoolOption("jit_ffi", "optimize libffi calls", default=False, cmdline=None),
BoolOption("check_str_without_nul",
"Forbid NUL chars in strings in some external function calls",
default=False, cmdline=None),
diff --git a/pypy/conftest.py b/pypy/conftest.py
--- a/pypy/conftest.py
+++ b/pypy/conftest.py
@@ -186,6 +186,9 @@
def delslice(self, obj, *args):
obj.__delslice__(*args)
+ def is_w(self, obj1, obj2):
+ return obj1 is obj2
+
def translation_test_so_skip_if_appdirect():
if option.runappdirect:
py.test.skip("translation test, skipped for appdirect")
diff --git a/pypy/doc/config/objspace.usemodules._cffi_backend.txt b/pypy/doc/config/objspace.usemodules._cffi_backend.txt
new file mode 100644
--- /dev/null
+++ b/pypy/doc/config/objspace.usemodules._cffi_backend.txt
@@ -0,0 +1,1 @@
+Core of CFFI (http://cffi.readthedocs.org)
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -1033,6 +1033,10 @@
w_meth = self.getattr(w_obj, self.wrap(methname))
return self.call_function(w_meth, *arg_w)
+ def raise_key_error(self, w_key):
+ e = self.call_function(self.w_KeyError, w_key)
+ raise OperationError(self.w_KeyError, e)
+
def lookup(self, w_obj, name):
w_type = self.type(w_obj)
w_mro = self.getattr(w_type, self.wrap("__mro__"))
diff --git a/pypy/interpreter/test/test_function.py b/pypy/interpreter/test/test_function.py
--- a/pypy/interpreter/test/test_function.py
+++ b/pypy/interpreter/test/test_function.py
@@ -16,6 +16,7 @@
assert f.func_defaults == None
assert f.func_dict == {}
assert type(f.func_globals) == dict
+ assert f.func_globals is f.__globals__
assert f.func_closure is None
assert f.func_doc == None
assert f.func_name == 'f'
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -37,7 +37,7 @@
assert __total_ordering__ in (None, 'auto'), "Unknown value for __total_ordering"
if __total_ordering__ == 'auto':
self.auto_total_ordering()
-
+
def add_entries(self, **rawdict):
# xxx fix the names of the methods to match what app-level expects
for key, value in rawdict.items():
@@ -228,7 +228,7 @@
def add(Proto):
for key, value in Proto.__dict__.items():
- if (not key.startswith('__') and not key.startswith('_mixin_')
+ if (not key.startswith('__') and not key.startswith('_mixin_')
or key == '__del__'):
if hasattr(value, "func_name"):
value = func_with_new_name(value, value.func_name)
@@ -315,10 +315,10 @@
class Proto(object):
def getdict(self, space):
return self.w__dict__
-
+
def setdict(self, space, w_dict):
self.w__dict__ = check_new_dictionary(space, w_dict)
-
+
def user_setup(self, space, w_subtype):
self.w__dict__ = space.newdict(
instance=True)
@@ -383,7 +383,7 @@
return %(name)s(%(args)s, %(extra)s)
"""
miniglobals[cls_name] = cls
-
+
name = func.__name__
extra = ', '.join(extraargs)
from pypy.interpreter import pycode
@@ -503,7 +503,7 @@
space, '__delattr__',
self.reqcls, Arguments(space, [w_obj,
space.wrap(self.name)]))
-
+
def descr_get_objclass(space, property):
return property.objclass_getter(space)
@@ -521,7 +521,7 @@
return space.w_None
else:
return w_value
-
+
return GetSetProperty(fget, cls=cls, doc=doc)
GetSetProperty.typedef = TypeDef(
@@ -543,7 +543,7 @@
self.index = index
self.name = name
self.w_cls = w_cls
-
+
def typecheck(self, space, w_obj):
if not space.is_true(space.isinstance(w_obj, self.w_cls)):
raise operationerrfmt(space.w_TypeError,
@@ -552,7 +552,7 @@
self.name,
self.w_cls.name,
space.type(w_obj).getname(space))
-
+
def descr_member_get(self, space, w_obj, w_w_cls=None):
"""member.__get__(obj[, type]) -> value
Read the slot 'member' of the given 'obj'."""
@@ -565,13 +565,13 @@
raise OperationError(space.w_AttributeError,
space.wrap(self.name)) # XXX better message
return w_result
-
+
def descr_member_set(self, space, w_obj, w_value):
"""member.__set__(obj, value)
Write into the slot 'member' of the given 'obj'."""
self.typecheck(space, w_obj)
w_obj.setslotvalue(self.index, w_value)
-
+
def descr_member_del(self, space, w_obj):
"""member.__delete__(obj)
Delete the value of the slot 'member' from the given 'obj'."""
@@ -803,15 +803,16 @@
func_dict = getset_func_dict,
func_defaults = getset_func_defaults,
func_globals = interp_attrproperty_w('w_func_globals', cls=Function),
- func_closure = GetSetProperty( Function.fget_func_closure ),
+ func_closure = GetSetProperty(Function.fget_func_closure),
__code__ = getset_func_code,
__doc__ = getset_func_doc,
__name__ = getset_func_name,
__dict__ = getset_func_dict,
__defaults__ = getset_func_defaults,
+ __globals__ = interp_attrproperty_w('w_func_globals', cls=Function),
__module__ = getset___module__,
__weakref__ = make_weakref_descr(Function),
- )
+)
Function.typedef.acceptable_as_base_class = False
Method.typedef = TypeDef(
diff --git a/pypy/jit/backend/llgraph/llimpl.py b/pypy/jit/backend/llgraph/llimpl.py
--- a/pypy/jit/backend/llgraph/llimpl.py
+++ b/pypy/jit/backend/llgraph/llimpl.py
@@ -21,7 +21,6 @@
from pypy.jit.backend.llgraph import symbolic
from pypy.jit.codewriter import longlong
-from pypy.rlib import libffi, clibffi
from pypy.rlib.objectmodel import ComputedIntSymbolic, we_are_translated
from pypy.rlib.rarithmetic import ovfcheck
from pypy.rlib.rarithmetic import r_longlong, r_ulonglong, r_uint
@@ -64,7 +63,8 @@
FLOAT_ARRAY_TP = lltype.Ptr(lltype.Array(lltype.Float, hints={"nolength": True}))
def maybe_uncast(TP, array):
- if array._TYPE.TO._hints.get("uncast_on_llgraph"):
+ if array._TYPE.TO.OF != lltype.Float:
+ # array._TYPE.TO._hints.get("uncast_on_llgraph"):
array = rffi.cast(TP, array)
return array
@@ -803,7 +803,7 @@
if arraydescr.typeinfo == REF:
raise NotImplementedError("getarrayitem_raw -> gcref")
elif arraydescr.typeinfo == INT:
- return do_getarrayitem_raw_int(array, index)
+ return do_getarrayitem_raw_int(array, index, arraydescr.ofs)
elif arraydescr.typeinfo == FLOAT:
return do_getarrayitem_raw_float(array, index)
else:
@@ -824,9 +824,7 @@
op_getfield_gc_pure = op_getfield_gc
def op_getfield_raw(self, fielddescr, struct):
- if fielddescr.arg_types == 'dynamic': # abuse of .arg_types
- return do_getfield_raw_dynamic(struct, fielddescr)
- elif fielddescr.typeinfo == REF:
+ if fielddescr.typeinfo == REF:
return do_getfield_raw_ptr(struct, fielddescr.ofs)
elif fielddescr.typeinfo == INT:
return do_getfield_raw_int(struct, fielddescr.ofs)
@@ -837,6 +835,26 @@
op_getfield_raw_pure = op_getfield_raw
+ def op_raw_store(self, arraydescr, addr, offset, value):
+ if arraydescr.typeinfo == REF:
+ raise AssertionError("cannot store GC pointer in raw storage")
+ elif arraydescr.typeinfo == INT:
+ do_raw_store_int(addr, offset, arraydescr.ofs, value)
+ elif arraydescr.typeinfo == FLOAT:
+ do_raw_store_float(addr, offset, value)
+ else:
+ raise NotImplementedError
+
+ def op_raw_load(self, arraydescr, addr, offset):
+ if arraydescr.typeinfo == REF:
+ raise AssertionError("cannot store GC pointer in raw storage")
+ elif arraydescr.typeinfo == INT:
+ return do_raw_load_int(addr, offset, arraydescr.ofs)
+ elif arraydescr.typeinfo == FLOAT:
+ return do_raw_load_float(addr, offset)
+ else:
+ raise NotImplementedError
+
def op_new(self, size):
return do_new(size.ofs)
@@ -862,7 +880,7 @@
if arraydescr.typeinfo == REF:
raise NotImplementedError("setarrayitem_raw <- gcref")
elif arraydescr.typeinfo == INT:
- do_setarrayitem_raw_int(array, index, newvalue)
+ do_setarrayitem_raw_int(array, index, newvalue, arraydescr.ofs)
elif arraydescr.typeinfo == FLOAT:
do_setarrayitem_raw_float(array, index, newvalue)
else:
@@ -922,9 +940,7 @@
raise NotImplementedError
def op_setfield_raw(self, fielddescr, struct, newvalue):
- if fielddescr.arg_types == 'dynamic': # abuse of .arg_types
- do_setfield_raw_dynamic(struct, fielddescr, newvalue)
- elif fielddescr.typeinfo == REF:
+ if fielddescr.typeinfo == REF:
do_setfield_raw_ptr(struct, fielddescr.ofs, newvalue)
elif fielddescr.typeinfo == INT:
do_setfield_raw_int(struct, fielddescr.ofs, newvalue)
@@ -1433,9 +1449,13 @@
array = array._obj.container
return cast_to_int(array.getitem(index))
-def do_getarrayitem_raw_int(array, index):
- array = array.adr.ptr._obj
- return cast_to_int(array.getitem(index))
+def do_getarrayitem_raw_int(array, index, itemsize):
+ array = array.adr.ptr
+ ITEMTYPE = lltype.typeOf(array).TO.OF
+ TYPE = symbolic.Size2Type[itemsize]
+ if TYPE.OF != ITEMTYPE:
+ array = rffi.cast(lltype.Ptr(TYPE), array)
+ return cast_to_int(array._obj.getitem(index))
def do_getarrayitem_gc_float(array, index):
array = array._obj.container
@@ -1479,18 +1499,6 @@
struct = array._obj.container.getitem(index)
return cast_to_ptr(_getinteriorfield_gc(struct, fieldnum))
-def _getinteriorfield_raw(ffitype, array, index, width, ofs):
- addr = rffi.cast(rffi.VOIDP, array)
- return libffi.array_getitem(ffitype, width, addr, index, ofs)
-
-def do_getinteriorfield_raw_int(array, index, width, ofs):
- res = _getinteriorfield_raw(libffi.types.slong, array, index, width, ofs)
- return res
-
-def do_getinteriorfield_raw_float(array, index, width, ofs):
- res = _getinteriorfield_raw(libffi.types.double, array, index, width, ofs)
- return res
-
def _getfield_raw(struct, fieldnum):
STRUCT, fieldname = symbolic.TokenToField[fieldnum]
ptr = cast_from_int(lltype.Ptr(STRUCT), struct)
@@ -1505,16 +1513,31 @@
def do_getfield_raw_ptr(struct, fieldnum):
return cast_to_ptr(_getfield_raw(struct, fieldnum))
-def do_getfield_raw_dynamic(struct, fielddescr):
- from pypy.rlib import libffi
- addr = cast_from_int(rffi.VOIDP, struct)
- ofs = fielddescr.ofs
- if fielddescr.is_pointer_field():
- assert False, 'fixme'
- elif fielddescr.is_float_field():
- assert False, 'fixme'
- else:
- return libffi._struct_getfield(lltype.Signed, addr, ofs)
+def do_raw_load_int(struct, offset, descrofs):
+ TYPE = symbolic.Size2Type[descrofs]
+ ll_p = rffi.cast(rffi.CCHARP, struct)
+ ll_p = rffi.cast(lltype.Ptr(TYPE), rffi.ptradd(ll_p, offset))
+ value = ll_p[0]
+ return rffi.cast(lltype.Signed, value)
+
+def do_raw_load_float(struct, offset):
+ ll_p = rffi.cast(rffi.CCHARP, struct)
+ ll_p = rffi.cast(rffi.CArrayPtr(longlong.FLOATSTORAGE),
+ rffi.ptradd(ll_p, offset))
+ value = ll_p[0]
+ return value
+
+def do_raw_store_int(struct, offset, descrofs, value):
+ TYPE = symbolic.Size2Type[descrofs]
+ ll_p = rffi.cast(rffi.CCHARP, struct)
+ ll_p = rffi.cast(lltype.Ptr(TYPE), rffi.ptradd(ll_p, offset))
+ ll_p[0] = rffi.cast(TYPE.OF, value)
+
+def do_raw_store_float(struct, offset, value):
+ ll_p = rffi.cast(rffi.CCHARP, struct)
+ ll_p = rffi.cast(rffi.CArrayPtr(longlong.FLOATSTORAGE),
+ rffi.ptradd(ll_p, offset))
+ ll_p[0] = value
def do_new(size):
TYPE = symbolic.Size2Type[size]
@@ -1533,10 +1556,13 @@
newvalue = cast_from_int(ITEMTYPE, newvalue)
array.setitem(index, newvalue)
-def do_setarrayitem_raw_int(array, index, newvalue):
+def do_setarrayitem_raw_int(array, index, newvalue, itemsize):
array = array.adr.ptr
ITEMTYPE = lltype.typeOf(array).TO.OF
- newvalue = cast_from_int(ITEMTYPE, newvalue)
+ TYPE = symbolic.Size2Type[itemsize]
+ if TYPE.OF != ITEMTYPE:
+ array = rffi.cast(lltype.Ptr(TYPE), array)
+ newvalue = cast_from_int(TYPE.OF, newvalue)
array._obj.setitem(index, newvalue)
def do_setarrayitem_gc_float(array, index, newvalue):
@@ -1581,18 +1607,6 @@
do_setinteriorfield_gc_float = new_setinteriorfield_gc(cast_from_floatstorage)
do_setinteriorfield_gc_ptr = new_setinteriorfield_gc(cast_from_ptr)
-def new_setinteriorfield_raw(cast_func, ffitype):
- def do_setinteriorfield_raw(array, index, newvalue, width, ofs):
- addr = rffi.cast(rffi.VOIDP, array)
- for TYPE, ffitype2 in clibffi.ffitype_map:
- if ffitype2 is ffitype:
- newvalue = cast_func(TYPE, newvalue)
- break
- return libffi.array_setitem(ffitype, width, addr, index, ofs, newvalue)
- return do_setinteriorfield_raw
-do_setinteriorfield_raw_int = new_setinteriorfield_raw(cast_from_int, libffi.types.slong)
-do_setinteriorfield_raw_float = new_setinteriorfield_raw(cast_from_floatstorage, libffi.types.double)
-
def do_setfield_raw_int(struct, fieldnum, newvalue):
STRUCT, fieldname = symbolic.TokenToField[fieldnum]
ptr = cast_from_int(lltype.Ptr(STRUCT), struct)
@@ -1614,17 +1628,6 @@
newvalue = cast_from_ptr(FIELDTYPE, newvalue)
setattr(ptr, fieldname, newvalue)
-def do_setfield_raw_dynamic(struct, fielddescr, newvalue):
- from pypy.rlib import libffi
- addr = cast_from_int(rffi.VOIDP, struct)
- ofs = fielddescr.ofs
- if fielddescr.is_pointer_field():
- assert False, 'fixme'
- elif fielddescr.is_float_field():
- assert False, 'fixme'
- else:
- libffi._struct_setfield(lltype.Signed, addr, ofs, newvalue)
-
def do_newstr(length):
x = rstr.mallocstr(length)
return cast_to_ptr(x)
@@ -1923,6 +1926,7 @@
setannotation(do_getinteriorfield_gc_int, annmodel.SomeInteger())
setannotation(do_getinteriorfield_gc_ptr, annmodel.SomePtr(llmemory.GCREF))
setannotation(do_getinteriorfield_gc_float, s_FloatStorage)
+setannotation(do_raw_load_int, annmodel.SomeInteger())
setannotation(do_new, annmodel.SomePtr(llmemory.GCREF))
setannotation(do_new_array, annmodel.SomePtr(llmemory.GCREF))
setannotation(do_setarrayitem_gc_int, annmodel.s_None)
@@ -1939,6 +1943,7 @@
setannotation(do_setinteriorfield_gc_int, annmodel.s_None)
setannotation(do_setinteriorfield_gc_ptr, annmodel.s_None)
setannotation(do_setinteriorfield_gc_float, annmodel.s_None)
+setannotation(do_raw_store_int, annmodel.s_None)
setannotation(do_newstr, annmodel.SomePtr(llmemory.GCREF))
setannotation(do_strsetitem, annmodel.s_None)
setannotation(do_newunicode, annmodel.SomePtr(llmemory.GCREF))
diff --git a/pypy/jit/backend/llgraph/runner.py b/pypy/jit/backend/llgraph/runner.py
--- a/pypy/jit/backend/llgraph/runner.py
+++ b/pypy/jit/backend/llgraph/runner.py
@@ -339,16 +339,6 @@
token = history.getkind(getattr(S, fieldname))
return self.getdescr(ofs, token[0], name=fieldname)
- def fielddescrof_dynamic(self, offset, fieldsize, is_pointer, is_float, is_signed):
- if is_pointer:
- typeinfo = REF
- elif is_float:
- typeinfo = FLOAT
- else:
- typeinfo = INT
- # we abuse the arg_types field to distinguish dynamic and static descrs
- return self.getdescr(offset, typeinfo, arg_types='dynamic', name='<dynamic field>')
-
def interiorfielddescrof(self, A, fieldname):
S = A.OF
width = symbolic.get_size(A)
@@ -356,18 +346,6 @@
token = history.getkind(getattr(S, fieldname))
return self.getdescr(ofs, token[0], name=fieldname, width=width)
- def interiorfielddescrof_dynamic(self, offset, width, fieldsize,
- is_pointer, is_float, is_signed):
-
- if is_pointer:
- typeinfo = REF
- elif is_float:
- typeinfo = FLOAT
- else:
- typeinfo = INT
- # we abuse the arg_types field to distinguish dynamic and static descrs
- return Descr(offset, typeinfo, arg_types='dynamic', name='<dynamic interior field>', width=width)
-
def calldescrof(self, FUNC, ARGS, RESULT, extrainfo):
arg_types = []
for ARG in ARGS:
@@ -382,22 +360,27 @@
return self.getdescr(0, token[0], extrainfo=extrainfo,
arg_types=''.join(arg_types))
- def calldescrof_dynamic(self, ffi_args, ffi_result, extrainfo, ffi_flags):
+ def calldescrof_dynamic(self, cif_description, extrainfo):
from pypy.jit.backend.llsupport.ffisupport import get_ffi_type_kind
from pypy.jit.backend.llsupport.ffisupport import UnsupportedKind
arg_types = []
try:
- for arg in ffi_args:
+ for arg in cif_description.atypes:
kind = get_ffi_type_kind(self, arg)
if kind != history.VOID:
arg_types.append(kind)
- reskind = get_ffi_type_kind(self, ffi_result)
+ reskind = get_ffi_type_kind(self, cif_description.rtype)
except UnsupportedKind:
return None
return self.getdescr(0, reskind, extrainfo=extrainfo,
arg_types=''.join(arg_types),
- ffi_flags=ffi_flags)
+ ffi_flags=cif_description.abi)
+ def _calldescr_dynamic_for_tests(self, atypes, rtype,
+ abiname='FFI_DEFAULT_ABI'):
+ from pypy.jit.backend.llsupport import ffisupport
+ return ffisupport.calldescr_dynamic_for_tests(self, atypes, rtype,
+ abiname)
def grab_exc_value(self):
return llimpl.grab_exc_value()
@@ -433,7 +416,7 @@
return llimpl.do_getarrayitem_gc_int(array, index)
def bh_getarrayitem_raw_i(self, arraydescr, array, index):
assert isinstance(arraydescr, Descr)
- return llimpl.do_getarrayitem_raw_int(array, index)
+ return llimpl.do_getarrayitem_raw_int(array, index, arraydescr.ofs)
def bh_getarrayitem_gc_r(self, arraydescr, array, index):
assert isinstance(arraydescr, Descr)
return llimpl.do_getarrayitem_gc_ptr(array, index)
@@ -487,6 +470,19 @@
return llimpl.do_setinteriorfield_gc_float(array, index, descr.ofs,
value)
+ def bh_raw_store_i(self, struct, offset, descr, newvalue):
+ assert isinstance(descr, Descr)
+ return llimpl.do_raw_store_int(struct, offset, descr.ofs, newvalue)
+ def bh_raw_store_f(self, struct, offset, descr, newvalue):
+ assert isinstance(descr, Descr)
+ return llimpl.do_raw_store_float(struct, offset, newvalue)
+ def bh_raw_load_i(self, struct, offset, descr):
+ assert isinstance(descr, Descr)
+ return llimpl.do_raw_load_int(struct, offset, descr.ofs)
+ def bh_raw_load_f(self, struct, offset, descr):
+ assert isinstance(descr, Descr)
+ return llimpl.do_raw_load_float(struct, offset)
+
def bh_new(self, sizedescr):
assert isinstance(sizedescr, Descr)
return llimpl.do_new(sizedescr.ofs)
@@ -516,7 +512,7 @@
def bh_setarrayitem_raw_i(self, arraydescr, array, index, newvalue):
assert isinstance(arraydescr, Descr)
- llimpl.do_setarrayitem_raw_int(array, index, newvalue)
+ llimpl.do_setarrayitem_raw_int(array, index, newvalue, arraydescr.ofs)
def bh_setarrayitem_gc_r(self, arraydescr, array, index, newvalue):
assert isinstance(arraydescr, Descr)
diff --git a/pypy/jit/backend/llgraph/symbolic.py b/pypy/jit/backend/llgraph/symbolic.py
--- a/pypy/jit/backend/llgraph/symbolic.py
+++ b/pypy/jit/backend/llgraph/symbolic.py
@@ -1,8 +1,7 @@
-import ctypes
from pypy.rpython.lltypesystem import lltype, rffi, rclass
-Size2Type = [None]
+Size2Type = [None] * 100
Type2Size = {}
def get_size(TYPE):
@@ -14,7 +13,7 @@
Type2Size[TYPE] = size
return size
-TokenToField = [None]
+TokenToField = [None] * 100
FieldToToken = {}
def get_field_token(STRUCT, fieldname):
@@ -26,21 +25,3 @@
FieldToToken[STRUCT, fieldname] = token
return token
get_field_token(rclass.OBJECT, 'typeptr') # force the index 1 for this
-
-def get_array_token(T):
- # T can be an array or a var-sized structure
- if isinstance(T, lltype.Struct):
- assert T._arrayfld is not None, "%r is not variable-sized" % (T,)
- cstruct = ll2ctypes.get_ctypes_type(T)
- cfield = getattr(cstruct, T._arrayfld)
- before_array_part = cfield.offset
- T = getattr(T, T._arrayfld)
- else:
- before_array_part = 0
- carray = ll2ctypes.get_ctypes_type(T)
- assert carray.length.size == 4
- ofs_length = before_array_part + carray.length.offset
- basesize = before_array_part + carray.items.offset
- carrayitem = ll2ctypes.get_ctypes_type(T.OF)
- itemsize = ctypes.sizeof(carrayitem)
- return basesize, itemsize, ofs_length
diff --git a/pypy/jit/backend/llsupport/descr.py b/pypy/jit/backend/llsupport/descr.py
--- a/pypy/jit/backend/llsupport/descr.py
+++ b/pypy/jit/backend/llsupport/descr.py
@@ -237,29 +237,6 @@
cache[(ARRAY, name)] = descr
return descr
-def compute_flag(is_pointer, is_float, is_signed):
- if is_pointer:
- assert not is_float
- return FLAG_POINTER
- elif is_float:
- return FLAG_FLOAT
- elif is_signed:
- return FLAG_SIGNED
- else:
- return FLAG_UNSIGNED
-
-def get_dynamic_field_descr(offset, fieldsize, is_pointer, is_float, is_signed):
- flag = compute_flag(is_pointer, is_float, is_signed)
- return FieldDescr('dynamic', offset, fieldsize, flag)
-
-def get_dynamic_interiorfield_descr(gc_ll_descr, offset, width, fieldsize,
- is_pointer, is_float, is_signed):
- arraydescr = ArrayDescr(0, width, None, FLAG_STRUCT)
- flag = compute_flag(is_pointer, is_float, is_signed)
- fielddescr = FieldDescr('dynamic', offset, fieldsize, flag)
- return InteriorFieldDescr(arraydescr, fielddescr)
-
-
# ____________________________________________________________
# CallDescrs
diff --git a/pypy/jit/backend/llsupport/ffisupport.py b/pypy/jit/backend/llsupport/ffisupport.py
--- a/pypy/jit/backend/llsupport/ffisupport.py
+++ b/pypy/jit/backend/llsupport/ffisupport.py
@@ -1,43 +1,97 @@
from pypy.rlib.rarithmetic import intmask
-from pypy.jit.metainterp import history
-from pypy.rpython.lltypesystem import rffi
+from pypy.rlib.objectmodel import specialize
+from pypy.rpython.lltypesystem import lltype, rffi
from pypy.jit.backend.llsupport.descr import CallDescr
class UnsupportedKind(Exception):
pass
-def get_call_descr_dynamic(cpu, ffi_args, ffi_result, extrainfo, ffi_flags):
- """Get a call descr: the types of result and args are represented by
- rlib.libffi.types.*"""
+def get_call_descr_dynamic(cpu, cif_description, extrainfo):
+ """Get a call descr from the given CIF_DESCRIPTION"""
+ ffi_result = cif_description.rtype
try:
reskind = get_ffi_type_kind(cpu, ffi_result)
- argkinds = [get_ffi_type_kind(cpu, arg) for arg in ffi_args]
+ argkinds = [get_ffi_type_kind(cpu, cif_description.atypes[i])
+ for i in range(cif_description.nargs)]
except UnsupportedKind:
return None
- if reskind == history.VOID:
+ if reskind == 'v':
result_size = 0
else:
result_size = intmask(ffi_result.c_size)
argkinds = ''.join(argkinds)
return CallDescr(argkinds, reskind, is_ffi_type_signed(ffi_result),
- result_size, extrainfo, ffi_flags=ffi_flags)
+ result_size, extrainfo, ffi_flags=cif_description.abi)
def get_ffi_type_kind(cpu, ffi_type):
- from pypy.rlib.libffi import types
+ from pypy.rlib.jit_libffi import types
+ kind = types.getkind(ffi_type)
+ if ((not cpu.supports_floats and kind == 'f') or
+ (not cpu.supports_longlong and kind == 'L') or
+ (not cpu.supports_singlefloats and kind == 'S') or
+ kind == '*' or kind == '?'):
+ raise UnsupportedKind("Unsupported kind '%s'" % kind)
+ if kind == 'u':
+ kind = 'i'
+ return kind
+
+def is_ffi_type_signed(ffi_type):
+ from pypy.rlib.jit_libffi import types
+ kind = types.getkind(ffi_type)
+ return kind != 'u'
+
+ at specialize.memo()
+def _get_ffi2descr_dict(cpu):
+ d = {('v', 0): ('v', None)}
+ if cpu.supports_floats:
+ d[('f', 0)] = ('f', cpu.arraydescrof(rffi.CArray(lltype.Float)))
+ if cpu.supports_singlefloats:
+ d[('S', 0)] = ('i', cpu.arraydescrof(rffi.CArray(lltype.SingleFloat)))
+ for SIGNED_TYPE in [rffi.SIGNEDCHAR,
+ rffi.SHORT,
+ rffi.INT,
+ rffi.LONG,
+ rffi.LONGLONG]:
+ key = ('i', rffi.sizeof(SIGNED_TYPE))
+ kind = 'i'
+ if key[1] > rffi.sizeof(lltype.Signed):
+ if not cpu.supports_longlong:
+ continue
+ key = ('L', 0)
+ kind = 'f'
+ d[key] = (kind, cpu.arraydescrof(rffi.CArray(SIGNED_TYPE)))
+ for UNSIGNED_TYPE in [rffi.UCHAR,
+ rffi.USHORT,
+ rffi.UINT,
+ rffi.ULONG,
+ rffi.ULONGLONG]:
+ key = ('u', rffi.sizeof(UNSIGNED_TYPE))
+ if key[1] > rffi.sizeof(lltype.Signed):
+ continue
+ d[key] = ('i', cpu.arraydescrof(rffi.CArray(UNSIGNED_TYPE)))
+ return d
+
+def get_arg_descr(cpu, ffi_type):
+ from pypy.rlib.jit_libffi import types
kind = types.getkind(ffi_type)
if kind == 'i' or kind == 'u':
- return history.INT
- elif cpu.supports_floats and kind == 'f':
- return history.FLOAT
- elif kind == 'v':
- return history.VOID
- elif cpu.supports_longlong and (kind == 'I' or kind == 'U'): # longlong
- return 'L'
- elif cpu.supports_singlefloats and kind == 's': # singlefloat
- return 'S'
- raise UnsupportedKind("Unsupported kind '%s'" % kind)
+ size = rffi.getintfield(ffi_type, 'c_size')
+ else:
+ size = 0
+ return _get_ffi2descr_dict(cpu)[kind, size]
-def is_ffi_type_signed(ffi_type):
- from pypy.rlib.libffi import types
- kind = types.getkind(ffi_type)
- return kind != 'u'
+def calldescr_dynamic_for_tests(cpu, atypes, rtype, abiname='FFI_DEFAULT_ABI'):
+ from pypy.rlib import clibffi
+ from pypy.rlib.jit_libffi import CIF_DESCRIPTION, FFI_TYPE_PP
+ from pypy.jit.codewriter.effectinfo import EffectInfo
+ #
+ p = lltype.malloc(CIF_DESCRIPTION, len(atypes),
+ flavor='raw', immortal=True)
+ p.abi = getattr(clibffi, abiname)
+ p.nargs = len(atypes)
+ p.rtype = rtype
+ p.atypes = lltype.malloc(FFI_TYPE_PP.TO, len(atypes),
+ flavor='raw', immortal=True)
+ for i in range(len(atypes)):
+ p.atypes[i] = atypes[i]
+ return cpu.calldescrof_dynamic(p, EffectInfo.MOST_GENERAL)
diff --git a/pypy/jit/backend/llsupport/llmodel.py b/pypy/jit/backend/llsupport/llmodel.py
--- a/pypy/jit/backend/llsupport/llmodel.py
+++ b/pypy/jit/backend/llsupport/llmodel.py
@@ -10,8 +10,8 @@
from pypy.jit.backend.llsupport.symbolic import WORD, unroll_basic_sizes
from pypy.jit.backend.llsupport.descr import (
get_size_descr, get_field_descr, get_array_descr,
- get_call_descr, get_interiorfield_descr, get_dynamic_interiorfield_descr,
- FieldDescr, ArrayDescr, CallDescr, InteriorFieldDescr, get_dynamic_field_descr)
+ get_call_descr, get_interiorfield_descr,
+ FieldDescr, ArrayDescr, CallDescr, InteriorFieldDescr)
from pypy.jit.backend.llsupport.asmmemmgr import AsmMemoryManager
@@ -245,9 +245,6 @@
def fielddescrof(self, STRUCT, fieldname):
return get_field_descr(self.gc_ll_descr, STRUCT, fieldname)
- def fielddescrof_dynamic(self, offset, fieldsize, is_pointer, is_float, is_signed):
- return get_dynamic_field_descr(offset, fieldsize, is_pointer, is_float, is_signed)
-
def unpack_fielddescr(self, fielddescr):
assert isinstance(fielddescr, FieldDescr)
return fielddescr.offset
@@ -267,12 +264,6 @@
def interiorfielddescrof(self, A, fieldname):
return get_interiorfield_descr(self.gc_ll_descr, A, fieldname)
- def interiorfielddescrof_dynamic(self, offset, width, fieldsize,
- is_pointer, is_float, is_signed):
- return get_dynamic_interiorfield_descr(self.gc_ll_descr,
- offset, width, fieldsize,
- is_pointer, is_float, is_signed)
-
def unpack_arraydescr(self, arraydescr):
assert isinstance(arraydescr, ArrayDescr)
return arraydescr.basesize
@@ -289,10 +280,16 @@
def calldescrof(self, FUNC, ARGS, RESULT, extrainfo):
return get_call_descr(self.gc_ll_descr, ARGS, RESULT, extrainfo)
- def calldescrof_dynamic(self, ffi_args, ffi_result, extrainfo, ffi_flags):
+ def calldescrof_dynamic(self, cif_description, extrainfo):
from pypy.jit.backend.llsupport import ffisupport
- return ffisupport.get_call_descr_dynamic(self, ffi_args, ffi_result,
- extrainfo, ffi_flags)
+ return ffisupport.get_call_descr_dynamic(self, cif_description,
+ extrainfo)
+
+ def _calldescr_dynamic_for_tests(self, atypes, rtype,
+ abiname='FFI_DEFAULT_ABI'):
+ from pypy.jit.backend.llsupport import ffisupport
+ return ffisupport.calldescr_dynamic_for_tests(self, atypes, rtype,
+ abiname)
def get_overflow_error(self):
ovf_vtable = self.cast_adr_to_int(self._ovf_error_vtable)
@@ -589,6 +586,32 @@
bh_setfield_raw_r = _base_do_setfield_r
bh_setfield_raw_f = _base_do_setfield_f
+ def bh_raw_store_i(self, addr, offset, descr, newvalue):
+ ofs, size, sign = self.unpack_arraydescr_size(descr)
+ items = addr + offset
+ for TYPE, _, itemsize in unroll_basic_sizes:
+ if size == itemsize:
+ items = rffi.cast(rffi.CArrayPtr(TYPE), items)
+ items[0] = rffi.cast(TYPE, newvalue)
+ break
+
+ def bh_raw_store_f(self, addr, offset, descr, newvalue):
+ items = rffi.cast(rffi.CArrayPtr(longlong.FLOATSTORAGE), addr + offset)
+ items[0] = newvalue
+
+ def bh_raw_load_i(self, addr, offset, descr):
+ ofs, size, sign = self.unpack_arraydescr_size(descr)
+ items = addr + offset
+ for TYPE, _, itemsize in unroll_basic_sizes:
+ if size == itemsize:
+ items = rffi.cast(rffi.CArrayPtr(TYPE), items)
+ return rffi.cast(lltype.Signed, items[0])
+ assert False # unreachable code
+
+ def bh_raw_load_f(self, addr, offset, descr):
+ items = rffi.cast(rffi.CArrayPtr(longlong.FLOATSTORAGE), addr + offset)
+ return items[0]
+
def bh_new(self, sizedescr):
return self.gc_ll_descr.gc_malloc(sizedescr)
diff --git a/pypy/jit/backend/llsupport/test/test_ffisupport.py b/pypy/jit/backend/llsupport/test/test_ffisupport.py
--- a/pypy/jit/backend/llsupport/test/test_ffisupport.py
+++ b/pypy/jit/backend/llsupport/test/test_ffisupport.py
@@ -1,4 +1,6 @@
-from pypy.rlib.libffi import types
+from pypy.rlib.jit_libffi import types, CIF_DESCRIPTION, FFI_TYPE_PP
+from pypy.rlib.clibffi import FFI_DEFAULT_ABI
+from pypy.rpython.lltypesystem import lltype, rffi
from pypy.jit.codewriter.longlong import is_64_bit
from pypy.jit.backend.llsupport.descr import *
from pypy.jit.backend.llsupport.ffisupport import *
@@ -11,56 +13,55 @@
self.supports_floats = supports_floats
self.supports_longlong = supports_longlong
self.supports_singlefloats = supports_singlefloats
-
+ def calldescrof_dynamic(self, cif_descr, effectinfo):
+ return get_call_descr_dynamic(self, cif_descr, effectinfo)
def test_call_descr_dynamic():
args = [types.sint, types.pointer]
- descr = get_call_descr_dynamic(FakeCPU(), args, types.sint, None,
- ffi_flags=42)
+ descr = calldescr_dynamic_for_tests(FakeCPU(), args, types.sint)
assert isinstance(descr, CallDescr)
assert descr.result_type == 'i'
assert descr.result_flag == FLAG_SIGNED
assert descr.arg_classes == 'ii'
- assert descr.get_ffi_flags() == 42
+ assert descr.get_ffi_flags() == FFI_DEFAULT_ABI
args = [types.sint, types.double, types.pointer]
- descr = get_call_descr_dynamic(FakeCPU(), args, types.void, None, 42)
+ descr = calldescr_dynamic_for_tests(FakeCPU(), args, types.void)
assert descr is None # missing floats
- descr = get_call_descr_dynamic(FakeCPU(supports_floats=True),
- args, types.void, None, ffi_flags=43)
+ descr = calldescr_dynamic_for_tests(FakeCPU(supports_floats=True),
+ args, types.void)
assert descr.result_type == 'v'
assert descr.result_flag == FLAG_VOID
assert descr.arg_classes == 'ifi'
- assert descr.get_ffi_flags() == 43
+ assert descr.get_ffi_flags() == FFI_DEFAULT_ABI
- descr = get_call_descr_dynamic(FakeCPU(), [], types.sint8, None, 42)
+ descr = calldescr_dynamic_for_tests(FakeCPU(), [], types.sint8)
assert descr.get_result_size() == 1
assert descr.result_flag == FLAG_SIGNED
assert descr.is_result_signed() == True
- descr = get_call_descr_dynamic(FakeCPU(), [], types.uint8, None, 42)
+ descr = calldescr_dynamic_for_tests(FakeCPU(), [], types.uint8)
assert isinstance(descr, CallDescr)
assert descr.get_result_size() == 1
assert descr.result_flag == FLAG_UNSIGNED
assert descr.is_result_signed() == False
if not is_64_bit or is_emulated_long:
- descr = get_call_descr_dynamic(FakeCPU(), [], types.slonglong,
- None, 42)
+ descr = calldescr_dynamic_for_tests(FakeCPU(), [], types.slonglong)
assert descr is None # missing longlongs
- descr = get_call_descr_dynamic(FakeCPU(supports_longlong=True),
- [], types.slonglong, None, ffi_flags=43)
+ descr = calldescr_dynamic_for_tests(FakeCPU(supports_longlong=True),
+ [], types.slonglong)
assert isinstance(descr, CallDescr)
assert descr.result_flag == FLAG_FLOAT
assert descr.result_type == 'L'
- assert descr.get_ffi_flags() == 43
+ assert descr.get_ffi_flags() == FFI_DEFAULT_ABI
else:
assert types.slonglong is types.slong
- descr = get_call_descr_dynamic(FakeCPU(), [], types.float, None, 42)
+ descr = calldescr_dynamic_for_tests(FakeCPU(), [], types.float)
assert descr is None # missing singlefloats
- descr = get_call_descr_dynamic(FakeCPU(supports_singlefloats=True),
- [], types.float, None, ffi_flags=44)
+ descr = calldescr_dynamic_for_tests(FakeCPU(supports_singlefloats=True),
+ [], types.float)
assert descr.result_flag == FLAG_UNSIGNED
assert descr.result_type == 'S'
- assert descr.get_ffi_flags() == 44
+ assert descr.get_ffi_flags() == FFI_DEFAULT_ABI
diff --git a/pypy/jit/backend/model.py b/pypy/jit/backend/model.py
--- a/pypy/jit/backend/model.py
+++ b/pypy/jit/backend/model.py
@@ -208,10 +208,6 @@
def interiorfielddescrof(self, A, fieldname):
raise NotImplementedError
- def interiorfielddescrof_dynamic(self, offset, width, fieldsize, is_pointer,
- is_float, is_signed):
- raise NotImplementedError
-
def arraydescrof(self, A):
raise NotImplementedError
diff --git a/pypy/jit/backend/test/calling_convention_test.py b/pypy/jit/backend/test/calling_convention_test.py
--- a/pypy/jit/backend/test/calling_convention_test.py
+++ b/pypy/jit/backend/test/calling_convention_test.py
@@ -59,7 +59,6 @@
return ConstInt(heaptracker.adr2int(addr))
def test_call_aligned_with_spilled_values(self):
- from pypy.rlib.libffi import types
cpu = self.cpu
if not cpu.supports_floats:
py.test.skip('requires floats')
@@ -118,7 +117,6 @@
assert abs(x - expected_result) < 0.0001
def test_call_aligned_with_imm_values(self):
- from pypy.rlib.libffi import types
cpu = self.cpu
if not cpu.supports_floats:
py.test.skip('requires floats')
@@ -161,7 +159,6 @@
assert abs(res.getfloat() - result) < 0.0001
def test_call_aligned_with_args_on_the_stack(self):
- from pypy.rlib.libffi import types
cpu = self.cpu
if not cpu.supports_floats:
py.test.skip('requires floats')
@@ -204,7 +201,6 @@
assert abs(res.getfloat() - result) < 0.0001
def test_call_alignment_call_assembler(self):
- from pypy.rlib.libffi import types
cpu = self.cpu
if not cpu.supports_floats:
py.test.skip('requires floats')
@@ -303,7 +299,6 @@
py.test.skip('requires floats and singlefloats')
import random
- from pypy.rlib.libffi import types
from pypy.rlib.rarithmetic import r_singlefloat
def func(*args):
diff --git a/pypy/jit/backend/test/runner_test.py b/pypy/jit/backend/test/runner_test.py
--- a/pypy/jit/backend/test/runner_test.py
+++ b/pypy/jit/backend/test/runner_test.py
@@ -515,7 +515,7 @@
assert longlong.getrealfloat(x) == 3.5 - 42
def test_call(self):
- from pypy.rlib.libffi import types, FUNCFLAG_CDECL
+ from pypy.rlib.jit_libffi import types
def func_int(a, b):
return a + b
@@ -543,9 +543,8 @@
'int', descr=calldescr)
assert res.value == 2 * num
# then, try it with the dynamic calldescr
- dyn_calldescr = cpu.calldescrof_dynamic([ffi_type, ffi_type], ffi_type,
- EffectInfo.MOST_GENERAL,
- ffi_flags=FUNCFLAG_CDECL)
+ dyn_calldescr = cpu._calldescr_dynamic_for_tests(
+ [ffi_type, ffi_type], ffi_type)
res = self.execute_operation(rop.CALL,
[funcbox, BoxInt(num), BoxInt(num)],
'int', descr=dyn_calldescr)
@@ -1733,39 +1732,6 @@
assert s.x == chr(190)
assert s.y == chr(150)
- def test_fielddescrof_dynamic(self):
- S = lltype.Struct('S',
- ('x', lltype.Signed),
- ('y', lltype.Signed),
- )
- longsize = rffi.sizeof(lltype.Signed)
- y_ofs = longsize
- s = lltype.malloc(S, flavor='raw')
- sa = llmemory.cast_ptr_to_adr(s)
- s_box = BoxInt(heaptracker.adr2int(sa))
- #
- field = self.cpu.fielddescrof(S, 'y')
- field_dyn = self.cpu.fielddescrof_dynamic(offset=y_ofs,
- fieldsize=longsize,
- is_pointer=False,
- is_float=False,
- is_signed=True)
- assert field.is_pointer_field() == field_dyn.is_pointer_field()
- assert field.is_float_field() == field_dyn.is_float_field()
- if 'llgraph' not in str(self.cpu):
- assert field.is_field_signed() == field_dyn.is_field_signed()
-
- #
- for get_op, set_op in ((rop.GETFIELD_RAW, rop.SETFIELD_RAW),
- (rop.GETFIELD_RAW_PURE, rop.SETFIELD_RAW)):
- for descr in (field, field_dyn):
- self.execute_operation(set_op, [s_box, BoxInt(32)], 'void',
- descr=descr)
- res = self.execute_operation(get_op, [s_box], 'int', descr=descr)
- assert res.getint() == 32
-
- lltype.free(s, flavor='raw')
-
def test_new_with_vtable(self):
cpu = self.cpu
t_box, T_box = self.alloc_instance(self.T)
@@ -2200,9 +2166,7 @@
cpu = self.cpu
func_adr = llmemory.cast_ptr_to_adr(c_tolower.funcsym)
funcbox = ConstInt(heaptracker.adr2int(func_adr))
- calldescr = cpu.calldescrof_dynamic([types.uchar], types.sint,
- EffectInfo.MOST_GENERAL,
- ffi_flags=FUNCFLAG_CDECL)
+ calldescr = cpu._calldescr_dynamic_for_tests([types.uchar], types.sint)
i1 = BoxInt()
i2 = BoxInt()
tok = BoxInt()
@@ -2255,11 +2219,9 @@
cpu = self.cpu
func_adr = llmemory.cast_ptr_to_adr(c_qsort.funcsym)
funcbox = ConstInt(heaptracker.adr2int(func_adr))
- calldescr = cpu.calldescrof_dynamic([types.pointer, types_size_t,
- types_size_t, types.pointer],
- types.void,
- EffectInfo.MOST_GENERAL,
- ffi_flags=clibffi.FUNCFLAG_CDECL)
+ calldescr = cpu._calldescr_dynamic_for_tests(
+ [types.pointer, types_size_t, types_size_t, types.pointer],
+ types.void)
i0 = BoxInt()
i1 = BoxInt()
i2 = BoxInt()
@@ -2308,10 +2270,10 @@
cpu = self.cpu
func_adr = llmemory.cast_ptr_to_adr(c_GetCurrentDir.funcsym)
funcbox = ConstInt(heaptracker.adr2int(func_adr))
- calldescr = cpu.calldescrof_dynamic([types.ulong, types.pointer],
- types.ulong,
- EffectInfo.MOST_GENERAL,
- ffi_flags=FUNCFLAG_STDCALL)
+ calldescr = cpu._calldescr_dynamic_for_tests(
+ [types.ulong, types.pointer],
+ types.ulong,
+ abiname='FFI_STDCALL')
i1 = BoxInt()
i2 = BoxInt()
faildescr = BasicFailDescr(1)
@@ -2565,13 +2527,14 @@
assert str.chars[4] == '/'
def test_sorting_of_fields(self):
- S = self.S
+ S = lltype.GcStruct('S', ('parent', rclass.OBJECT),
+ ('value', lltype.Signed),
+ ('chr1', lltype.Char),
+ ('chr2', lltype.Char))
+ chr1 = self.cpu.fielddescrof(S, 'chr1').sort_key()
value = self.cpu.fielddescrof(S, 'value').sort_key()
- chr1 = self.cpu.fielddescrof(S, 'chr1').sort_key()
chr2 = self.cpu.fielddescrof(S, 'chr2').sort_key()
- assert (sorted([chr2, chr1, value]) ==
- [value, chr1, chr2])
- assert len(dict.fromkeys([value, chr1, chr2]).keys()) == 3
+ assert len(set([value, chr1, chr2])) == 3
def test_guards_nongc(self):
x = lltype.malloc(lltype.Struct('x'), flavor='raw')
@@ -3206,6 +3169,20 @@
res = self.cpu.get_latest_value_int(0)
assert res == -10
+ def test_int_force_ge_zero(self):
+ ops = """
+ [i0]
+ i1 = int_force_ge_zero(i0) # but forced to be in a register
+ finish(i1, descr=1)
+ """
+ loop = parse(ops, self.cpu, namespace=locals())
+ descr = loop.operations[-1].getdescr()
+ looptoken = JitCellToken()
+ self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken)
+ for inp, outp in [(2,2), (-3, 0)]:
+ self.cpu.execute_token(looptoken, inp)
+ assert outp == self.cpu.get_latest_value_int(0)
+
def test_compile_asmlen(self):
from pypy.jit.backend.llsupport.llmodel import AbstractLLCPU
if not isinstance(self.cpu, AbstractLLCPU):
@@ -3340,6 +3317,107 @@
fail = self.cpu.execute_token(looptoken2, -9)
assert fail.identifier == 42
+ def test_raw_load_int(self):
+ from pypy.rlib import rawstorage
+ for T in [rffi.UCHAR, rffi.SIGNEDCHAR,
+ rffi.USHORT, rffi.SHORT,
+ rffi.UINT, rffi.INT,
+ rffi.ULONG, rffi.LONG]:
+ ops = """
+ [i0, i1]
+ i2 = raw_load(i0, i1, descr=arraydescr)
+ finish(i2)
+ """
+ arraydescr = self.cpu.arraydescrof(rffi.CArray(T))
+ p = rawstorage.alloc_raw_storage(31)
+ for i in range(31):
+ p[i] = '\xDD'
+ value = rffi.cast(T, 0x4243444546474849)
+ rawstorage.raw_storage_setitem(p, 16, value)
+ loop = parse(ops, self.cpu, namespace=locals())
+ looptoken = JitCellToken()
+ self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken)
+ self.cpu.execute_token(looptoken,
+ rffi.cast(lltype.Signed, p), 16)
+ result = self.cpu.get_latest_value_int(0)
+ assert result == rffi.cast(lltype.Signed, value)
+ rawstorage.free_raw_storage(p)
+
+ def test_raw_load_float(self):
+ if not self.cpu.supports_floats:
+ py.test.skip("requires floats")
+ from pypy.rlib import rawstorage
+ for T in [rffi.DOUBLE]:
+ ops = """
+ [i0, i1]
+ f2 = raw_load(i0, i1, descr=arraydescr)
+ finish(f2)
+ """
+ arraydescr = self.cpu.arraydescrof(rffi.CArray(T))
+ p = rawstorage.alloc_raw_storage(31)
+ for i in range(31):
+ p[i] = '\xDD'
+ value = rffi.cast(T, 1.12e20)
+ rawstorage.raw_storage_setitem(p, 16, value)
+ loop = parse(ops, self.cpu, namespace=locals())
+ looptoken = JitCellToken()
+ self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken)
+ self.cpu.execute_token(looptoken,
+ rffi.cast(lltype.Signed, p), 16)
+ result = self.cpu.get_latest_value_float(0)
+ result = longlong.getrealfloat(result)
+ assert result == rffi.cast(lltype.Float, value)
+ rawstorage.free_raw_storage(p)
+
+ def test_raw_store_int(self):
+ from pypy.rlib import rawstorage
+ for T in [rffi.UCHAR, rffi.SIGNEDCHAR,
+ rffi.USHORT, rffi.SHORT,
+ rffi.UINT, rffi.INT,
+ rffi.ULONG, rffi.LONG]:
+ ops = """
+ [i0, i1, i2]
+ raw_store(i0, i1, i2, descr=arraydescr)
+ finish()
+ """
+ arraydescr = self.cpu.arraydescrof(rffi.CArray(T))
+ p = rawstorage.alloc_raw_storage(31)
+ for i in range(31):
+ p[i] = '\xDD'
+ value = 0x4243444546474849 & sys.maxint
+ loop = parse(ops, self.cpu, namespace=locals())
+ looptoken = JitCellToken()
+ self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken)
+ self.cpu.execute_token(looptoken,
+ rffi.cast(lltype.Signed, p), 16, value)
+ result = rawstorage.raw_storage_getitem(T, p, 16)
+ assert result == rffi.cast(T, value)
+ rawstorage.free_raw_storage(p)
+
+ def test_raw_store_float(self):
+ if not self.cpu.supports_floats:
+ py.test.skip("requires floats")
+ from pypy.rlib import rawstorage
+ for T in [rffi.DOUBLE]:
+ ops = """
+ [i0, i1, f2]
+ raw_store(i0, i1, f2, descr=arraydescr)
+ finish()
+ """
+ arraydescr = self.cpu.arraydescrof(rffi.CArray(T))
+ p = rawstorage.alloc_raw_storage(31)
+ for i in range(31):
+ p[i] = '\xDD'
+ value = 1.23e20
+ loop = parse(ops, self.cpu, namespace=locals())
+ looptoken = JitCellToken()
+ self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken)
+ self.cpu.execute_token(looptoken,
+ rffi.cast(lltype.Signed, p), 16,
+ longlong.getfloatstorage(value))
+ result = rawstorage.raw_storage_getitem(T, p, 16)
+ assert result == rffi.cast(T, value)
+ rawstorage.free_raw_storage(p)
class OOtypeBackendTest(BaseBackendTest):
diff --git a/pypy/jit/backend/x86/assembler.py b/pypy/jit/backend/x86/assembler.py
--- a/pypy/jit/backend/x86/assembler.py
+++ b/pypy/jit/backend/x86/assembler.py
@@ -127,9 +127,13 @@
self._build_stack_check_slowpath()
if gc_ll_descr.gcrootmap:
self._build_release_gil(gc_ll_descr.gcrootmap)
- debug_start('jit-backend-counts')
- self.set_debug(have_debug_prints())
- debug_stop('jit-backend-counts')
+ if not self._debug:
+ # if self._debug is already set it means that someone called
+ # set_debug by hand before initializing the assembler. Leave it
+ # as it is
+ debug_start('jit-backend-counts')
+ self.set_debug(have_debug_prints())
+ debug_stop('jit-backend-counts')
def setup(self, looptoken):
assert self.memcpy_addr != 0, "setup_once() not called?"
@@ -998,6 +1002,24 @@
getattr(self.mc, asmop)(arglocs[0], arglocs[1])
return genop_binary
+ def _binaryop_or_lea(asmop, is_add):
+ def genop_binary_or_lea(self, op, arglocs, result_loc):
+ # use a regular ADD or SUB if result_loc is arglocs[0],
+ # and a LEA only if different.
+ if result_loc is arglocs[0]:
+ getattr(self.mc, asmop)(arglocs[0], arglocs[1])
+ else:
+ loc = arglocs[0]
+ argloc = arglocs[1]
+ assert isinstance(loc, RegLoc)
+ assert isinstance(argloc, ImmedLoc)
+ assert isinstance(result_loc, RegLoc)
+ delta = argloc.value
+ if not is_add: # subtraction
+ delta = -delta
+ self.mc.LEA_rm(result_loc.value, (loc.value, delta))
+ return genop_binary_or_lea
+
def _cmpop(cond, rev_cond):
def genop_cmp(self, op, arglocs, result_loc):
rl = result_loc.lowest8bits()
@@ -1224,8 +1246,8 @@
genop_int_neg = _unaryop("NEG")
genop_int_invert = _unaryop("NOT")
- genop_int_add = _binaryop("ADD", True)
- genop_int_sub = _binaryop("SUB")
+ genop_int_add = _binaryop_or_lea("ADD", True)
+ genop_int_sub = _binaryop_or_lea("SUB", False)
genop_int_mul = _binaryop("IMUL", True)
genop_int_and = _binaryop("AND", True)
genop_int_or = _binaryop("OR", True)
@@ -1378,7 +1400,7 @@
def genop_int_force_ge_zero(self, op, arglocs, resloc):
self.mc.TEST(arglocs[0], arglocs[0])
self.mov(imm0, resloc)
- self.mc.CMOVNS(arglocs[0], resloc)
+ self.mc.CMOVNS(resloc, arglocs[0])
def genop_int_mod(self, op, arglocs, resloc):
if IS_X86_32:
@@ -1550,6 +1572,13 @@
genop_getarrayitem_gc_pure = genop_getarrayitem_gc
genop_getarrayitem_raw = genop_getarrayitem_gc
+ genop_getarrayitem_raw_pure = genop_getarrayitem_gc
+
+ def genop_raw_load(self, op, arglocs, resloc):
+ base_loc, ofs_loc, size_loc, ofs, sign_loc = arglocs
+ assert isinstance(ofs, ImmedLoc)
+ src_addr = addr_add(base_loc, ofs_loc, ofs.value, 0)
+ self.load_from_mem(resloc, src_addr, size_loc, sign_loc)
def _get_interiorfield_addr(self, temp_loc, index_loc, itemsize_loc,
base_loc, ofs_loc):
@@ -1576,9 +1605,6 @@
ofs_loc)
self.load_from_mem(resloc, src_addr, fieldsize_loc, sign_loc)
- genop_getinteriorfield_raw = genop_getinteriorfield_gc
-
-
def genop_discard_setfield_gc(self, op, arglocs):
base_loc, ofs_loc, size_loc, value_loc = arglocs
assert isinstance(size_loc, ImmedLoc)
@@ -1603,6 +1629,12 @@
dest_addr = AddressLoc(base_loc, ofs_loc, scale, baseofs.value)
self.save_into_mem(dest_addr, value_loc, size_loc)
+ def genop_discard_raw_store(self, op, arglocs):
+ base_loc, ofs_loc, value_loc, size_loc, baseofs = arglocs
+ assert isinstance(baseofs, ImmedLoc)
+ dest_addr = AddressLoc(base_loc, ofs_loc, 0, baseofs.value)
+ self.save_into_mem(dest_addr, value_loc, size_loc)
+
def genop_discard_strsetitem(self, op, arglocs):
base_loc, ofs_loc, val_loc = arglocs
basesize, itemsize, ofs_length = symbolic.get_array_token(rstr.STR,
@@ -1711,15 +1743,15 @@
guard_op.getopname())
def genop_guard_int_add_ovf(self, op, guard_op, guard_token, arglocs, result_loc):
- self.genop_int_add(op, arglocs, result_loc)
+ self.mc.ADD(arglocs[0], arglocs[1])
return self._gen_guard_overflow(guard_op, guard_token)
def genop_guard_int_sub_ovf(self, op, guard_op, guard_token, arglocs, result_loc):
- self.genop_int_sub(op, arglocs, result_loc)
+ self.mc.SUB(arglocs[0], arglocs[1])
return self._gen_guard_overflow(guard_op, guard_token)
def genop_guard_int_mul_ovf(self, op, guard_op, guard_token, arglocs, result_loc):
- self.genop_int_mul(op, arglocs, result_loc)
+ self.mc.IMUL(arglocs[0], arglocs[1])
return self._gen_guard_overflow(guard_op, guard_token)
def genop_guard_guard_false(self, ign_1, guard_op, guard_token, locs, ign_2):
@@ -2635,13 +2667,13 @@
return AddressLoc(reg_or_imm1, reg_or_imm2, scale, offset)
def addr_add_const(reg_or_imm1, offset):
- return AddressLoc(reg_or_imm1, ImmedLoc(0), 0, offset)
+ return AddressLoc(reg_or_imm1, imm0, 0, offset)
def mem(loc, offset):
- return AddressLoc(loc, ImmedLoc(0), 0, offset)
+ return AddressLoc(loc, imm0, 0, offset)
def heap(addr):
- return AddressLoc(ImmedLoc(addr), ImmedLoc(0), 0, 0)
+ return AddressLoc(ImmedLoc(addr), imm0, 0, 0)
def not_implemented(msg):
os.write(2, '[x86/asm] %s\n' % msg)
diff --git a/pypy/jit/backend/x86/regalloc.py b/pypy/jit/backend/x86/regalloc.py
--- a/pypy/jit/backend/x86/regalloc.py
+++ b/pypy/jit/backend/x86/regalloc.py
@@ -23,6 +23,7 @@
TempBox
from pypy.jit.backend.x86.arch import WORD, FRAME_FIXED_SIZE
from pypy.jit.backend.x86.arch import IS_X86_32, IS_X86_64, MY_COPY_OF_REGS
+from pypy.jit.backend.x86 import rx86
from pypy.rlib.rarithmetic import r_longlong
class X86RegisterManager(RegisterManager):
@@ -610,9 +611,31 @@
loc, argloc = self._consider_binop_part(op)
self.Perform(op, [loc, argloc], loc)
- consider_int_add = _consider_binop
+ def _consider_lea(self, op, loc):
+ argloc = self.loc(op.getarg(1))
+ self.rm.possibly_free_var(op.getarg(0))
+ resloc = self.force_allocate_reg(op.result)
+ self.Perform(op, [loc, argloc], resloc)
+
+ def consider_int_add(self, op):
+ loc = self.loc(op.getarg(0))
+ y = op.getarg(1)
+ if (isinstance(loc, RegLoc) and
+ isinstance(y, ConstInt) and rx86.fits_in_32bits(y.value)):
+ self._consider_lea(op, loc)
+ else:
+ self._consider_binop(op)
+
+ def consider_int_sub(self, op):
+ loc = self.loc(op.getarg(0))
+ y = op.getarg(1)
+ if (isinstance(loc, RegLoc) and
+ isinstance(y, ConstInt) and rx86.fits_in_32bits(-y.value)):
+ self._consider_lea(op, loc)
+ else:
+ self._consider_binop(op)
+
consider_int_mul = _consider_binop
- consider_int_sub = _consider_binop
consider_int_and = _consider_binop
consider_int_or = _consider_binop
consider_int_xor = _consider_binop
@@ -1102,6 +1125,7 @@
imm(itemsize), imm(ofs)])
consider_setarrayitem_raw = consider_setarrayitem_gc
+ consider_raw_store = consider_setarrayitem_gc
def consider_getfield_gc(self, op):
ofs_loc, size_loc, sign = self._unpack_fielddescr(op.getdescr())
@@ -1135,6 +1159,8 @@
consider_getarrayitem_raw = consider_getarrayitem_gc
consider_getarrayitem_gc_pure = consider_getarrayitem_gc
+ consider_getarrayitem_raw_pure = consider_getarrayitem_gc
+ consider_raw_load = consider_getarrayitem_gc
def consider_getinteriorfield_gc(self, op):
t = self._unpack_interiorfielddescr(op.getdescr())
@@ -1166,8 +1192,6 @@
self.Perform(op, [base_loc, ofs, itemsize, fieldsize,
index_loc, temp_loc, sign_loc], result_loc)
- consider_getinteriorfield_raw = consider_getinteriorfield_gc
-
def consider_int_is_true(self, op, guard_op):
# doesn't need arg to be in a register
argloc = self.loc(op.getarg(0))
diff --git a/pypy/jit/backend/x86/rx86.py b/pypy/jit/backend/x86/rx86.py
--- a/pypy/jit/backend/x86/rx86.py
+++ b/pypy/jit/backend/x86/rx86.py
@@ -530,7 +530,7 @@
NOT_r = insn(rex_w, '\xF7', register(1), '\xD0')
NOT_b = insn(rex_w, '\xF7', orbyte(2<<3), stack_bp(1))
- CMOVNS_rr = insn(rex_w, '\x0F\x49', register(2, 8), register(1), '\xC0')
+ CMOVNS_rr = insn(rex_w, '\x0F\x49', register(1, 8), register(2), '\xC0')
# ------------------------------ Misc stuff ------------------------------
diff --git a/pypy/jit/backend/x86/test/test_fficall.py b/pypy/jit/backend/x86/test/test_fficall.py
--- a/pypy/jit/backend/x86/test/test_fficall.py
+++ b/pypy/jit/backend/x86/test/test_fficall.py
@@ -2,7 +2,7 @@
from pypy.jit.metainterp.test import test_fficall
from pypy.jit.backend.x86.test.test_basic import Jit386Mixin
-class TestFfiLookups(Jit386Mixin, test_fficall.FfiLookupTests):
+class TestFfiCall(Jit386Mixin, test_fficall.FfiCallTests):
# for the individual tests see
# ====> ../../../metainterp/test/test_fficall.py
- supports_all = True
+ pass
diff --git a/pypy/jit/backend/x86/test/test_rawmem.py b/pypy/jit/backend/x86/test/test_rawmem.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/backend/x86/test/test_rawmem.py
@@ -0,0 +1,9 @@
+
+from pypy.jit.backend.x86.test.test_basic import Jit386Mixin
+from pypy.jit.metainterp.test.test_rawmem import RawMemTests
+
+
+class TestRawMem(Jit386Mixin, RawMemTests):
+ # for the individual tests see
+ # ====> ../../../metainterp/test/test_rawmem.py
+ pass
diff --git a/pypy/jit/backend/x86/test/test_runner.py b/pypy/jit/backend/x86/test/test_runner.py
--- a/pypy/jit/backend/x86/test/test_runner.py
+++ b/pypy/jit/backend/x86/test/test_runner.py
@@ -458,10 +458,8 @@
mc.RET16_i(40)
rawstart = mc.materialize(cpu.asmmemmgr, [])
#
- calldescr = cpu.calldescrof_dynamic([types.slong] * 10,
- types.slong,
- EffectInfo.MOST_GENERAL,
- ffi_flags=-1)
+ calldescr = cpu._calldescr_dynamic_for_tests([types.slong] * 10,
+ types.slong)
calldescr.get_call_conv = lambda: ffi # <==== hack
# ^^^ we patch get_call_conv() so that the test also makes sense
# on Linux, because clibffi.get_call_conv() would always
diff --git a/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py b/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py
--- a/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py
+++ b/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py
@@ -317,9 +317,7 @@
# CALL_j is actually relative, so tricky to test
(instrname == 'CALL' and argmodes == 'j') or
# SET_ir must be tested manually
- (instrname == 'SET' and argmodes == 'ir') or
- # asm gets CMOVNS args the wrong way
- (instrname.startswith('CMOV'))
+ (instrname == 'SET' and argmodes == 'ir')
)
diff --git a/pypy/jit/backend/x86/test/test_ztranslation.py b/pypy/jit/backend/x86/test/test_ztranslation.py
--- a/pypy/jit/backend/x86/test/test_ztranslation.py
+++ b/pypy/jit/backend/x86/test/test_ztranslation.py
@@ -187,7 +187,8 @@
return len(ll_times)
res = self.meta_interp(main, [])
- assert res == 1
+ assert res == 3
+ # one for loop, one for entry point and one for the prologue
class TestTranslationRemoveTypePtrX86(CCompiledMixin):
CPUClass = getcpuclass()
diff --git a/pypy/jit/backend/x86/tool/test/test_viewcode.py b/pypy/jit/backend/x86/tool/test/test_viewcode.py
--- a/pypy/jit/backend/x86/tool/test/test_viewcode.py
+++ b/pypy/jit/backend/x86/tool/test/test_viewcode.py
@@ -1,5 +1,10 @@
from cStringIO import StringIO
from pypy.jit.backend.x86.tool.viewcode import format_code_dump_with_labels
+from pypy.jit.backend.x86.tool.viewcode import find_objdump
+import os
+import py
+import tempfile
+from pypy.tool.udir import udir
def test_format_code_dump_with_labels():
lines = StringIO("""
@@ -53,3 +58,16 @@
lines = format_code_dump_with_labels(0xAA00, lines, label_list=None)
out = ''.join(lines)
assert out.strip() == input
+
+def test_find_objdump():
+ old = os.environ['PATH']
+ os.environ['PATH'] = ''
+ py.test.raises(find_objdump)
+
+ #
+ path = udir.join('objdump')
+ print >>path, 'hello world'
+ os.environ['PATH'] = path.dirname
+ assert find_objdump() == 'objdump'
+ #
+ os.environ['PATH'] = old
diff --git a/pypy/jit/backend/x86/tool/viewcode.py b/pypy/jit/backend/x86/tool/viewcode.py
--- a/pypy/jit/backend/x86/tool/viewcode.py
+++ b/pypy/jit/backend/x86/tool/viewcode.py
@@ -8,9 +8,9 @@
./viewcode.py log # also includes a pygame viewer
"""
-import autopath
import new
import operator
+import os
import py
import re
import sys
@@ -36,6 +36,17 @@
if sys.platform == "win32":
pass # lots more in Psyco
+def find_objdump():
+ exe = ('objdump', 'gobjdump')
+ path = os.environ['PATH'].split(os.pathsep)
+ for e in exe:
+ for p in path:
+ path_to = os.path.join(p, e)
+ if not os.path.exists(path_to):
+ continue
+ return e
+ raise AssertionError('(g)objdump was not found in PATH')
+
def machine_code_dump(data, originaddr, backend_name, label_list=None):
objdump_backend_option = {
'x86': 'i386',
@@ -43,7 +54,8 @@
'x86_64': 'x86-64',
'i386': 'i386',
}
- objdump = ('objdump -M %(backend)s -b binary -m i386 '
+ cmd = find_objdump()
+ objdump = ('%(command)s -M %(backend)s -b binary -m i386 '
'--disassembler-options=intel-mnemonics '
'--adjust-vma=%(origin)d -D %(file)s')
#
@@ -51,6 +63,7 @@
f.write(data)
f.close()
p = subprocess.Popen(objdump % {
+ 'command': cmd,
'file': tmpfile,
'origin': originaddr,
'backend': objdump_backend_option[backend_name],
diff --git a/pypy/jit/codewriter/call.py b/pypy/jit/codewriter/call.py
--- a/pypy/jit/codewriter/call.py
+++ b/pypy/jit/codewriter/call.py
@@ -16,6 +16,7 @@
class CallControl(object):
virtualref_info = None # optionally set from outside
+ has_libffi_call = False # default value
def __init__(self, cpu=None, jitdrivers_sd=[]):
assert isinstance(jitdrivers_sd, list) # debugging
diff --git a/pypy/jit/codewriter/effectinfo.py b/pypy/jit/codewriter/effectinfo.py
--- a/pypy/jit/codewriter/effectinfo.py
+++ b/pypy/jit/codewriter/effectinfo.py
@@ -45,13 +45,7 @@
OS_UNIEQ_LENGTHOK = 51 #
_OS_offset_uni = OS_UNI_CONCAT - OS_STR_CONCAT
#
- OS_LIBFFI_PREPARE = 60
- OS_LIBFFI_PUSH_ARG = 61
OS_LIBFFI_CALL = 62
- OS_LIBFFI_STRUCT_GETFIELD = 63
- OS_LIBFFI_STRUCT_SETFIELD = 64
- OS_LIBFFI_GETARRAYITEM = 65
- OS_LIBFFI_SETARRAYITEM = 66
#
OS_LLONG_INVERT = 69
OS_LLONG_ADD = 70
@@ -81,9 +75,13 @@
OS_LLONG_U_TO_FLOAT = 94
#
OS_MATH_SQRT = 100
+ #
+ OS_RAW_MALLOC_VARSIZE = 110
+ OS_RAW_FREE = 111
# for debugging:
- _OS_CANRAISE = set([OS_NONE, OS_STR2UNICODE, OS_LIBFFI_CALL])
+ _OS_CANRAISE = set([OS_NONE, OS_STR2UNICODE, OS_LIBFFI_CALL,
+ OS_RAW_MALLOC_VARSIZE])
def __new__(cls, readonly_descrs_fields, readonly_descrs_arrays,
write_descrs_fields, write_descrs_arrays,
diff --git a/pypy/jit/codewriter/jtransform.py b/pypy/jit/codewriter/jtransform.py
--- a/pypy/jit/codewriter/jtransform.py
+++ b/pypy/jit/codewriter/jtransform.py
@@ -11,6 +11,7 @@
from pypy.objspace.flow.model import SpaceOperation, Variable, Constant, c_last_exception
from pypy.rlib import objectmodel
from pypy.rlib.jit import _we_are_jitted
+from pypy.rlib.rgc import lltype_is_gc
from pypy.rpython.lltypesystem import lltype, llmemory, rstr, rclass, rffi
from pypy.rpython.rclass import IR_QUASIIMMUTABLE, IR_QUASIIMMUTABLE_ARRAY
from pypy.translator.simplify import get_funcobj
@@ -208,6 +209,10 @@
if op.args[0] in self.vable_array_vars:
self.vable_array_vars[op.result]= self.vable_array_vars[op.args[0]]
+ def rewrite_op_cast_ptr_to_adr(self, op):
+ if lltype_is_gc(op.args[0].concretetype):
+ raise Exception("cast_ptr_to_adr for GC types unsupported")
+
def rewrite_op_cast_pointer(self, op):
newop = self.rewrite_op_same_as(op)
assert newop is None
@@ -223,6 +228,9 @@
return [None, # hack, do the right renaming from op.args[0] to op.result
SpaceOperation("record_known_class", [op.args[0], const_vtable], None)]
+ def rewrite_op_raw_malloc_usage(self, op):
+ pass
+
def rewrite_op_jit_record_known_class(self, op):
return SpaceOperation("record_known_class", [op.args[0], op.args[1]], None)
@@ -520,9 +528,12 @@
name += '_add_memory_pressure'
if not track_allocation:
name += '_no_track_allocation'
- return self._do_builtin_call(op, name, args,
- extra = (TYPE,),
- extrakey = TYPE)
+ op1 = self.prepare_builtin_call(op, name, args, (TYPE,), TYPE)
+ if name == 'raw_malloc_varsize':
+ return self._handle_oopspec_call(op1, args,
+ EffectInfo.OS_RAW_MALLOC_VARSIZE,
+ EffectInfo.EF_CAN_RAISE)
+ return self.rewrite_op_direct_call(op1)
def rewrite_op_malloc_varsize(self, op):
if op.args[1].value['flavor'] == 'raw':
@@ -550,8 +561,13 @@
name = 'raw_free'
if not track_allocation:
name += '_no_track_allocation'
- return self._do_builtin_call(op, name, [op.args[0]],
- extra = (STRUCT,), extrakey = STRUCT)
+ op1 = self.prepare_builtin_call(op, name, [op.args[0]], (STRUCT,),
+ STRUCT)
+ if name == 'raw_free':
+ return self._handle_oopspec_call(op1, [op.args[0]],
+ EffectInfo.OS_RAW_FREE,
+ EffectInfo.EF_CANNOT_RAISE)
+ return self.rewrite_op_direct_call(op1)
def rewrite_op_getarrayitem(self, op):
ARRAY = op.args[0].concretetype.TO
@@ -566,9 +582,14 @@
[v_base, arrayfielddescr, arraydescr,
op.args[1]], op.result)]
# normal case follows
+ pure = ''
+ immut = ARRAY._immutable_field(None)
+ if immut:
+ pure = '_pure'
arraydescr = self.cpu.arraydescrof(ARRAY)
kind = getkind(op.result.concretetype)
- return SpaceOperation('getarrayitem_%s_%s' % (ARRAY._gckind, kind[0]),
+ return SpaceOperation('getarrayitem_%s_%s%s' % (ARRAY._gckind,
+ kind[0], pure),
[op.args[0], arraydescr, op.args[1]],
op.result)
@@ -691,6 +712,16 @@
[v_inst, descr, v_value],
None)
+ def rewrite_op_getsubstruct(self, op):
+ STRUCT = op.args[0].concretetype.TO
+ argname = getattr(STRUCT, '_gckind', 'gc')
+ if argname != 'raw':
+ raise Exception("%r: only supported for gckind=raw" % (op,))
+ ofs = llmemory.offsetof(STRUCT, op.args[1].value)
+ return SpaceOperation('int_add',
+ [op.args[0], Constant(ofs, lltype.Signed)],
+ op.result)
+
def is_typeptr_getset(self, op):
return (op.args[1].value == 'typeptr' and
op.args[0].concretetype.TO._hints.get('typeptr'))
@@ -840,6 +871,23 @@
return SpaceOperation('setinteriorfield_gc_%s' % kind, args,
op.result)
+ def rewrite_op_raw_store(self, op):
+ T = op.args[2].concretetype
+ kind = getkind(T)[0]
+ assert kind != 'r'
+ descr = self.cpu.arraydescrof(rffi.CArray(T))
+ return SpaceOperation('raw_store_%s' % kind,
+ [op.args[0], op.args[1], descr, op.args[2]],
+ None)
+
+ def rewrite_op_raw_load(self, op):
+ T = op.result.concretetype
+ kind = getkind(T)[0]
+ assert kind != 'r'
+ descr = self.cpu.arraydescrof(rffi.CArray(T))
+ return SpaceOperation('raw_load_%s' % kind,
+ [op.args[0], op.args[1], descr], op.result)
+
def _rewrite_equality(self, op, opname):
arg0, arg1 = op.args
if isinstance(arg0, Constant) and not arg0.value:
@@ -850,7 +898,7 @@
return self._rewrite_symmetric(op)
def _is_gc(self, v):
- return getattr(getattr(v.concretetype, "TO", None), "_gckind", "?") == 'gc'
+ return lltype_is_gc(v.concretetype)
def _is_rclass_instance(self, v):
return lltype._castdepth(v.concretetype.TO, rclass.OBJECT) >= 0
@@ -1228,6 +1276,8 @@
('uint_or', 'int_or'),
('uint_lshift', 'int_lshift'),
('uint_xor', 'int_xor'),
+
+ ('adr_add', 'int_add'),
]:
assert _old not in locals()
exec py.code.Source('''
@@ -1469,7 +1519,7 @@
'check_neg_index')
extra = getkind(op.result.concretetype)[0]
if pure:
- extra = 'pure_' + extra
+ extra += '_pure'
op = SpaceOperation('getarrayitem_gc_%s' % extra,
[args[0], arraydescr, v_index], op.result)
return extraop + [op]
@@ -1678,27 +1728,10 @@
# rlib.libffi
def _handle_libffi_call(self, op, oopspec_name, args):
- if oopspec_name == 'libffi_prepare_call':
- oopspecindex = EffectInfo.OS_LIBFFI_PREPARE
- extraeffect = EffectInfo.EF_CANNOT_RAISE
- elif oopspec_name.startswith('libffi_push_'):
- oopspecindex = EffectInfo.OS_LIBFFI_PUSH_ARG
- extraeffect = EffectInfo.EF_CANNOT_RAISE
- elif oopspec_name.startswith('libffi_call_'):
+ if oopspec_name == 'libffi_call':
oopspecindex = EffectInfo.OS_LIBFFI_CALL
extraeffect = EffectInfo.EF_RANDOM_EFFECTS
- elif oopspec_name == 'libffi_struct_getfield':
- oopspecindex = EffectInfo.OS_LIBFFI_STRUCT_GETFIELD
- extraeffect = EffectInfo.EF_CANNOT_RAISE
- elif oopspec_name == 'libffi_struct_setfield':
- oopspecindex = EffectInfo.OS_LIBFFI_STRUCT_SETFIELD
- extraeffect = EffectInfo.EF_CANNOT_RAISE
- elif oopspec_name == 'libffi_array_getitem':
- oopspecindex = EffectInfo.OS_LIBFFI_GETARRAYITEM
- extraeffect = EffectInfo.EF_CANNOT_RAISE
- elif oopspec_name == 'libffi_array_setitem':
- oopspecindex = EffectInfo.OS_LIBFFI_SETARRAYITEM
- extraeffect = EffectInfo.EF_CANNOT_RAISE
+ self.callcontrol.has_libffi_call = True
else:
assert False, 'unsupported oopspec: %s' % oopspec_name
return self._handle_oopspec_call(op, args, oopspecindex, extraeffect)
diff --git a/pypy/jit/codewriter/policy.py b/pypy/jit/codewriter/policy.py
--- a/pypy/jit/codewriter/policy.py
+++ b/pypy/jit/codewriter/policy.py
@@ -63,11 +63,10 @@
contains_loop = contains_loop and not getattr(
func, '_jit_unroll_safe_', False)
- unsupported = contains_unsupported_variable_type(graph,
+ res = see_function and not contains_unsupported_variable_type(graph,
self.supports_floats,
self.supports_longlong,
self.supports_singlefloats)
- res = see_function and not unsupported
if res and contains_loop:
self.unsafe_loopy_graphs.add(graph)
res = res and not contains_loop
diff --git a/pypy/jit/codewriter/support.py b/pypy/jit/codewriter/support.py
--- a/pypy/jit/codewriter/support.py
+++ b/pypy/jit/codewriter/support.py
@@ -431,31 +431,6 @@
return llop.uint_mod(lltype.Unsigned, xll, yll)
-# libffi support
-# --------------
-
-def func(llfunc):
- from pypy.rlib.libffi import Func
- return cast_base_ptr_to_instance(Func, llfunc)
-
-def _ll_1_libffi_prepare_call(llfunc):
- return func(llfunc)._prepare()
-
-def _ll_4_libffi_push_int(llfunc, value, ll_args, i):
- return func(llfunc)._push_int(value, ll_args, i)
-
-def _ll_4_libffi_push_float(llfunc, value, ll_args, i):
- return func(llfunc)._push_float(value, ll_args, i)
-
-def _ll_3_libffi_call_int(llfunc, funcsym, ll_args):
- return func(llfunc)._do_call(funcsym, ll_args, rffi.LONG)
-
-def _ll_3_libffi_call_float(llfunc, funcsym, ll_args):
- return func(llfunc)._do_call(funcsym, ll_args, rffi.DOUBLE)
-
-def _ll_3_libffi_call_void(llfunc, funcsym, ll_args):
- return func(llfunc)._do_call(funcsym, ll_args, lltype.Void)
-
# in the following calls to builtins, the JIT is allowed to look inside:
inline_calls_to = [
('int_floordiv_ovf_zer', [lltype.Signed, lltype.Signed], lltype.Signed),
diff --git a/pypy/jit/codewriter/test/test_jtransform.py b/pypy/jit/codewriter/test/test_jtransform.py
--- a/pypy/jit/codewriter/test/test_jtransform.py
+++ b/pypy/jit/codewriter/test/test_jtransform.py
@@ -123,6 +123,7 @@
INT = lltype.Signed
UNICHAR = lltype.UniChar
FLOAT = lltype.Float
+ ARRAYPTR = rffi.CArrayPtr(lltype.Signed)
argtypes = {
EI.OS_MATH_SQRT: ([FLOAT], FLOAT),
EI.OS_STR2UNICODE:([PSTR], PUNICODE),
@@ -139,16 +140,26 @@
EI.OS_UNIEQ_NONNULL_CHAR: ([PUNICODE, UNICHAR], INT),
EI.OS_UNIEQ_CHECKNULL_CHAR: ([PUNICODE, UNICHAR], INT),
EI.OS_UNIEQ_LENGTHOK: ([PUNICODE, PUNICODE], INT),
+ EI.OS_RAW_MALLOC_VARSIZE: ([INT], ARRAYPTR),
+ EI.OS_RAW_FREE: ([ARRAYPTR], lltype.Void),
}
argtypes = argtypes[oopspecindex]
assert argtypes[0] == [v.concretetype for v in op.args[1:]]
assert argtypes[1] == op.result.concretetype
if oopspecindex == EI.OS_STR2UNICODE:
assert extraeffect == EI.EF_ELIDABLE_CAN_RAISE
+ elif oopspecindex == EI.OS_RAW_MALLOC_VARSIZE:
+ assert extraeffect == EI.EF_CAN_RAISE
+ elif oopspecindex == EI.OS_RAW_FREE:
+ assert extraeffect == EI.EF_CANNOT_RAISE
else:
assert extraeffect == EI.EF_ELIDABLE_CANNOT_RAISE
return 'calldescr-%d' % oopspecindex
+
def calldescr_canraise(self, calldescr):
+ EI = effectinfo.EffectInfo
+ if calldescr == 'calldescr-%d' % EI.OS_RAW_MALLOC_VARSIZE:
+ return True
return False
@@ -547,10 +558,13 @@
flags = Constant({'flavor': 'raw'}, lltype.Void)
op = SpaceOperation('malloc_varsize', [Constant(S, lltype.Void), flags,
v1], v)
- tr = Transformer(FakeCPU(), FakeResidualCallControl())
+ tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
op0, op1 = tr.rewrite_operation(op)
assert op0.opname == 'residual_call_ir_i'
assert op0.args[0].value == 'raw_malloc_varsize' # pseudo-function as a str
+ assert (op0.args[1] == 'calldescr-%d' %
+ effectinfo.EffectInfo.OS_RAW_MALLOC_VARSIZE)
+
assert op1.opname == '-live-'
assert op1.args == []
@@ -591,21 +605,28 @@
assert op1.args == []
def test_raw_free():
- S = lltype.Struct('dummy', ('x', lltype.Signed))
- for flag in [True, False]:
- flags = Constant({'flavor': 'raw', 'track_allocation': flag},
- lltype.Void)
- op = SpaceOperation('free', [varoftype(lltype.Ptr(S)), flags],
- varoftype(lltype.Void))
- tr = Transformer(FakeCPU(), FakeResidualCallControl())
- op0, op1 = tr.rewrite_operation(op)
- assert op0.opname == 'residual_call_ir_v'
- if flag:
- pseudo_op_name = 'raw_free'
- else:
- pseudo_op_name = 'raw_free_no_track_allocation'
- assert op0.args[0].value == pseudo_op_name # pseudo-function as a str
- assert op1.opname == '-live-'
+ S = rffi.CArray(lltype.Signed)
+ flags = Constant({'flavor': 'raw', 'track_allocation': True},
+ lltype.Void)
+ op = SpaceOperation('free', [varoftype(lltype.Ptr(S)), flags],
+ varoftype(lltype.Void))
+ tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
+ op0 = tr.rewrite_operation(op)
+ assert op0.opname == 'residual_call_ir_v'
+ assert op0.args[0].value == 'raw_free'
+ assert op0.args[1] == 'calldescr-%d' % effectinfo.EffectInfo.OS_RAW_FREE
+
+def test_raw_free_no_track_allocation():
+ S = rffi.CArray(lltype.Signed)
+ flags = Constant({'flavor': 'raw', 'track_allocation': False},
+ lltype.Void)
+ op = SpaceOperation('free', [varoftype(lltype.Ptr(S)), flags],
+ varoftype(lltype.Void))
+ tr = Transformer(FakeCPU(), FakeResidualCallControl())
+ op0, op1 = tr.rewrite_operation(op)
+ assert op0.opname == 'residual_call_ir_v'
+ assert op0.args[0].value == 'raw_free_no_track_allocation'
+ assert op1.opname == '-live-'
def test_rename_on_links():
v1 = Variable()
@@ -621,6 +642,13 @@
assert block.exits[0].target is block2
assert block.exits[0].args == [v1]
+def test_cast_ptr_to_adr():
+ t = Transformer(FakeCPU(), None)
+ v = varoftype(lltype.Ptr(lltype.Array()))
+ v2 = varoftype(llmemory.Address)
+ op1 = t.rewrite_operation(SpaceOperation('cast_ptr_to_adr', [v], v2))
+ assert op1 is None
+
def test_int_eq():
v1 = varoftype(lltype.Signed)
v2 = varoftype(lltype.Signed)
@@ -830,6 +858,30 @@
op1 = Transformer(FakeCPU()).rewrite_operation(op)
assert not op1
+def test_raw_store():
+ v_storage = varoftype(llmemory.Address)
+ v_index = varoftype(lltype.Signed)
+ v_item = varoftype(lltype.Signed) # for example
+ op = SpaceOperation('raw_store', [v_storage, v_index, v_item], None)
+ op1 = Transformer(FakeCPU()).rewrite_operation(op)
+ assert op1.opname == 'raw_store_i'
+ assert op1.args[0] == v_storage
+ assert op1.args[1] == v_index
+ assert op1.args[2] == ('arraydescr', rffi.CArray(lltype.Signed))
+ assert op1.args[3] == v_item
+
+def test_raw_load():
+ v_storage = varoftype(llmemory.Address)
+ v_index = varoftype(lltype.Signed)
+ v_res = varoftype(lltype.Signed) # for example
+ op = SpaceOperation('raw_load', [v_storage, v_index], v_res)
+ op1 = Transformer(FakeCPU()).rewrite_operation(op)
+ assert op1.opname == 'raw_load_i'
+ assert op1.args[0] == v_storage
+ assert op1.args[1] == v_index
+ assert op1.args[2] == ('arraydescr', rffi.CArray(lltype.Signed))
+ assert op1.result == v_res
+
def test_promote_1():
v1 = varoftype(lltype.Signed)
v2 = varoftype(lltype.Signed)
diff --git a/pypy/jit/codewriter/test/test_list.py b/pypy/jit/codewriter/test/test_list.py
--- a/pypy/jit/codewriter/test/test_list.py
+++ b/pypy/jit/codewriter/test/test_list.py
@@ -129,14 +129,14 @@
builtin_test('list.getitem_foldable/NONNEG',
[varoftype(FIXEDLIST), varoftype(lltype.Signed)],
lltype.Signed, """
- getarrayitem_gc_pure_i %r0, <ArrayDescr>, %i0 -> %i1
+ getarrayitem_gc_i_pure %r0, <ArrayDescr>, %i0 -> %i1
""")
builtin_test('list.getitem_foldable/NEG',
[varoftype(FIXEDLIST), varoftype(lltype.Signed)],
lltype.Signed, """
-live-
check_neg_index %r0, <ArrayDescr>, %i0 -> %i1
- getarrayitem_gc_pure_i %r0, <ArrayDescr>, %i1 -> %i2
+ getarrayitem_gc_i_pure %r0, <ArrayDescr>, %i1 -> %i2
""")
def test_fixed_setitem():
diff --git a/pypy/jit/metainterp/blackhole.py b/pypy/jit/metainterp/blackhole.py
--- a/pypy/jit/metainterp/blackhole.py
+++ b/pypy/jit/metainterp/blackhole.py
@@ -1129,9 +1129,9 @@
def bhimpl_getarrayitem_gc_f(cpu, array, arraydescr, index):
return cpu.bh_getarrayitem_gc_f(arraydescr, array, index)
- bhimpl_getarrayitem_gc_pure_i = bhimpl_getarrayitem_gc_i
- bhimpl_getarrayitem_gc_pure_r = bhimpl_getarrayitem_gc_r
- bhimpl_getarrayitem_gc_pure_f = bhimpl_getarrayitem_gc_f
+ bhimpl_getarrayitem_gc_i_pure = bhimpl_getarrayitem_gc_i
+ bhimpl_getarrayitem_gc_r_pure = bhimpl_getarrayitem_gc_r
+ bhimpl_getarrayitem_gc_f_pure = bhimpl_getarrayitem_gc_f
@arguments("cpu", "i", "d", "i", returns="i")
def bhimpl_getarrayitem_raw_i(cpu, array, arraydescr, index):
@@ -1140,6 +1140,9 @@
def bhimpl_getarrayitem_raw_f(cpu, array, arraydescr, index):
return cpu.bh_getarrayitem_raw_f(arraydescr, array, index)
+ bhimpl_getarrayitem_raw_i_pure = bhimpl_getarrayitem_raw_i
+ bhimpl_getarrayitem_raw_f_pure = bhimpl_getarrayitem_raw_f
+
@arguments("cpu", "r", "d", "i", "i")
def bhimpl_setarrayitem_gc_i(cpu, array, arraydescr, index, newvalue):
cpu.bh_setarrayitem_gc_i(arraydescr, array, index, newvalue)
@@ -1274,6 +1277,20 @@
def bhimpl_setfield_raw_f(cpu, struct, fielddescr, newvalue):
cpu.bh_setfield_raw_f(struct, fielddescr, newvalue)
+ @arguments("cpu", "i", "i", "d", "i")
+ def bhimpl_raw_store_i(cpu, addr, offset, arraydescr, newvalue):
+ cpu.bh_raw_store_i(addr, offset, arraydescr, newvalue)
+ @arguments("cpu", "i", "i", "d", "f")
+ def bhimpl_raw_store_f(cpu, addr, offset, arraydescr, newvalue):
+ cpu.bh_raw_store_f(addr, offset, arraydescr, newvalue)
+
+ @arguments("cpu", "i", "i", "d", returns="i")
+ def bhimpl_raw_load_i(cpu, addr, offset, arraydescr):
+ return cpu.bh_raw_load_i(addr, offset, arraydescr)
+ @arguments("cpu", "i", "i", "d", returns="f")
+ def bhimpl_raw_load_f(cpu, addr, offset, arraydescr):
+ return cpu.bh_raw_load_f(addr, offset, arraydescr)
+
@arguments("r", "d", "d")
def bhimpl_record_quasiimmut_field(struct, fielddescr, mutatefielddescr):
pass
diff --git a/pypy/jit/metainterp/executor.py b/pypy/jit/metainterp/executor.py
--- a/pypy/jit/metainterp/executor.py
+++ b/pypy/jit/metainterp/executor.py
@@ -180,6 +180,26 @@
else:
cpu.bh_setfield_raw_i(struct, fielddescr, itembox.getint())
+def do_raw_store(cpu, _, addrbox, offsetbox, valuebox, arraydescr):
+ addr = addrbox.getint()
+ offset = offsetbox.getint()
+ if arraydescr.is_array_of_pointers():
+ raise AssertionError("cannot store GC pointers in raw store")
+ elif arraydescr.is_array_of_floats():
+ cpu.bh_raw_store_f(addr, offset, arraydescr,valuebox.getfloatstorage())
+ else:
+ cpu.bh_raw_store_i(addr, offset, arraydescr, valuebox.getint())
+
+def do_raw_load(cpu, _, addrbox, offsetbox, arraydescr):
+ addr = addrbox.getint()
+ offset = offsetbox.getint()
+ if arraydescr.is_array_of_pointers():
+ raise AssertionError("cannot store GC pointers in raw store")
+ elif arraydescr.is_array_of_floats():
+ return BoxFloat(cpu.bh_raw_load_f(addr, offset, arraydescr))
+ else:
+ return BoxInt(cpu.bh_raw_load_i(addr, offset, arraydescr))
+
def exec_new_with_vtable(cpu, clsbox):
from pypy.jit.codewriter import heaptracker
vtable = clsbox.getint()
@@ -277,19 +297,6 @@
def _make_execute_list():
- if 0: # enable this to trace calls to do_xxx
- def wrap(fn):
- def myfn(*args):
- print '<<<', fn.__name__
- try:
- return fn(*args)
- finally:
- print fn.__name__, '>>>'
- return myfn
- else:
- def wrap(fn):
- return fn
- #
execute_by_num_args = {}
for key, value in rop.__dict__.items():
if not key.startswith('_'):
@@ -343,7 +350,6 @@
rop.DEBUG_MERGE_POINT,
rop.JIT_DEBUG,
rop.SETARRAYITEM_RAW,
- rop.GETINTERIORFIELD_RAW,
rop.SETINTERIORFIELD_RAW,
rop.CALL_RELEASE_GIL,
rop.QUASIIMMUT_FIELD,
diff --git a/pypy/jit/metainterp/history.py b/pypy/jit/metainterp/history.py
--- a/pypy/jit/metainterp/history.py
+++ b/pypy/jit/metainterp/history.py
@@ -39,7 +39,7 @@
# XXX fix this for oo...
if (TYPE != llmemory.Address and
rffi.sizeof(TYPE) > rffi.sizeof(lltype.Signed)):
- if supports_longlong:
+ if supports_longlong and TYPE is not lltype.LongFloat:
assert rffi.sizeof(TYPE) == 8
return 'float'
raise NotImplementedError("type %s is too large" % TYPE)
diff --git a/pypy/jit/metainterp/optimizeopt/__init__.py b/pypy/jit/metainterp/optimizeopt/__init__.py
--- a/pypy/jit/metainterp/optimizeopt/__init__.py
+++ b/pypy/jit/metainterp/optimizeopt/__init__.py
@@ -5,7 +5,6 @@
from pypy.jit.metainterp.optimizeopt.heap import OptHeap
from pypy.jit.metainterp.optimizeopt.vstring import OptString
from pypy.jit.metainterp.optimizeopt.unroll import optimize_unroll
-from pypy.jit.metainterp.optimizeopt.fficall import OptFfiCall
from pypy.jit.metainterp.optimizeopt.simplify import OptSimplify
from pypy.jit.metainterp.optimizeopt.pure import OptPure
from pypy.jit.metainterp.optimizeopt.earlyforce import OptEarlyForce
@@ -21,7 +20,6 @@
('earlyforce', OptEarlyForce),
('pure', OptPure),
('heap', OptHeap),
- ('ffi', None),
('unroll', None)]
# no direct instantiation of unroll
unroll_all_opts = unrolling_iterable(ALL_OPTS)
@@ -42,11 +40,6 @@
if opt is not None:
o = opt()
optimizations.append(o)
- elif name == 'ffi' and config.translation.jit_ffi:
- # we cannot put the class directly in the unrolling_iterable,
- # because we do not want it to be seen at all (to avoid to
- # introduce a dependency on libffi in case we do not need it)
- optimizations.append(OptFfiCall())
if ('rewrite' not in enable_opts or 'virtualize' not in enable_opts
or 'heap' not in enable_opts or 'unroll' not in enable_opts
diff --git a/pypy/jit/metainterp/optimizeopt/fficall.py b/pypy/jit/metainterp/optimizeopt/fficall.py
deleted file mode 100644
--- a/pypy/jit/metainterp/optimizeopt/fficall.py
+++ /dev/null
@@ -1,307 +0,0 @@
-from pypy.jit.codewriter.effectinfo import EffectInfo
-from pypy.jit.metainterp.optimizeopt.optimizer import Optimization
-from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
-from pypy.jit.metainterp.resoperation import rop, ResOperation
-from pypy.rlib import clibffi, libffi
-from pypy.rlib.debug import debug_print
-from pypy.rlib.libffi import Func
-from pypy.rlib.objectmodel import we_are_translated
-from pypy.rpython.annlowlevel import cast_base_ptr_to_instance
-from pypy.rpython.lltypesystem import lltype, llmemory, rffi
-from pypy.rlib.objectmodel import we_are_translated
-from pypy.rlib.rarithmetic import intmask
-
-
-class FuncInfo(object):
-
- argtypes = None
- restype = None
- descr = None
- prepare_op = None
-
- def __init__(self, funcval, cpu, prepare_op):
- self.funcval = funcval
- self.opargs = []
- argtypes, restype, flags = self._get_signature(funcval)
- self.descr = cpu.calldescrof_dynamic(argtypes, restype,
- EffectInfo.MOST_GENERAL,
- ffi_flags=flags)
- # ^^^ may be None if unsupported
- self.prepare_op = prepare_op
- self.delayed_ops = []
-
- def _get_signature(self, funcval):
- """
- given the funcval, return a tuple (argtypes, restype, flags), where
- the actuall types are libffi.types.*
-
- The implementation is tricky because we have three possible cases:
-
- - translated: the easiest case, we can just cast back the pointer to
- the original Func instance and read .argtypes, .restype and .flags
-
- - completely untranslated: this is what we get from test_optimizeopt
- tests. funcval contains a FakeLLObject whose _fake_class is Func,
- and we can just get .argtypes, .restype and .flags
-
- - partially translated: this happens when running metainterp tests:
- funcval contains the low-level equivalent of a Func, and thus we
- have to fish inst_argtypes and inst_restype by hand. Note that
- inst_argtypes is actually a low-level array, but we can use it
- directly since the only thing we do with it is to read its items
- """
-
- llfunc = funcval.box.getref_base()
- if we_are_translated():
- func = cast_base_ptr_to_instance(Func, llfunc)
- return func.argtypes, func.restype, func.flags
- elif getattr(llfunc, '_fake_class', None) is Func:
- # untranslated
- return llfunc.argtypes, llfunc.restype, llfunc.flags
- else:
- # partially translated
- # llfunc contains an opaque pointer to something like the following:
- # <GcStruct pypy.rlib.libffi.Func { super, inst_argtypes, inst_funcptr,
- # inst_funcsym, inst_restype }>
- #
- # Unfortunately, we cannot use the proper lltype.cast_opaque_ptr,
- # because we don't have the exact TYPE to cast to. Instead, we
- # just fish it manually :-(
- f = llfunc._obj.container
- return f.inst_argtypes, f.inst_restype, f.inst_flags
-
-
-class OptFfiCall(Optimization):
-
- def setup(self):
- self.funcinfo = None
- if self.optimizer.loop is not None:
- self.logops = self.optimizer.loop.logops
- else:
- self.logops = None
-
- def new(self):
- return OptFfiCall()
-
- def begin_optimization(self, funcval, op):
- self.rollback_maybe('begin_optimization', op)
- self.funcinfo = FuncInfo(funcval, self.optimizer.cpu, op)
-
- def commit_optimization(self):
- self.funcinfo = None
-
- def rollback_maybe(self, msg, op):
- if self.funcinfo is None:
- return # nothing to rollback
- #
- # we immediately set funcinfo to None to prevent recursion when
- # calling emit_op
- if self.logops is not None:
- debug_print('rollback: ' + msg + ': ', self.logops.repr_of_resop(op))
- funcinfo = self.funcinfo
- self.funcinfo = None
- self.emit_operation(funcinfo.prepare_op)
- for op in funcinfo.opargs:
- self.emit_operation(op)
- for delayed_op in funcinfo.delayed_ops:
- self.emit_operation(delayed_op)
-
- def emit_operation(self, op):
- # we cannot emit any operation during the optimization
- self.rollback_maybe('invalid op', op)
- Optimization.emit_operation(self, op)
-
- def optimize_CALL(self, op):
- oopspec = self._get_oopspec(op)
- ops = [op]
- if oopspec == EffectInfo.OS_LIBFFI_PREPARE:
- ops = self.do_prepare_call(op)
- elif oopspec == EffectInfo.OS_LIBFFI_PUSH_ARG:
- ops = self.do_push_arg(op)
- elif oopspec == EffectInfo.OS_LIBFFI_CALL:
- ops = self.do_call(op)
- elif (oopspec == EffectInfo.OS_LIBFFI_STRUCT_GETFIELD or
- oopspec == EffectInfo.OS_LIBFFI_STRUCT_SETFIELD):
- ops = self.do_struct_getsetfield(op, oopspec)
- elif (oopspec == EffectInfo.OS_LIBFFI_GETARRAYITEM or
- oopspec == EffectInfo.OS_LIBFFI_SETARRAYITEM):
- ops = self.do_getsetarrayitem(op, oopspec)
- #
- for op in ops:
- self.emit_operation(op)
-
- optimize_CALL_MAY_FORCE = optimize_CALL
-
- def optimize_FORCE_TOKEN(self, op):
- # The handling of force_token needs a bit of explanation.
- # The original trace which is getting optimized looks like this:
- # i1 = force_token()
- # setfield_gc(p0, i1, ...)
- # call_may_force(...)
- #
- # In theory, fficall should take care of both force_token and
- # setfield_gc. However, the lazy setfield optimization in heap.py
- # delays the setfield_gc, with the effect that fficall.py sees them in
- # this order:
- # i1 = force_token()
- # call_may_force(...)
- # setfield_gc(p0, i1, ...)
- #
- # This means that see the setfield_gc only the call_may_force, when
- # the optimization has already been done, and thus we need to take
- # special care just of force_token.
- #
- # Finally, the method force_lazy_setfield in heap.py reorders the
- # call_may_force and the setfield_gc, so the final result we get is
- # again force_token/setfield_gc/call_may_force.
- #
- # However, note that nowadays we also allow to have any setfield_gc
- # between libffi_prepare and libffi_call, so while the comment above
- # it's a bit superfluous, it has been left there for future reference.
- if self.funcinfo is None:
- self.emit_operation(op)
- else:
- self.funcinfo.delayed_ops.append(op)
-
- optimize_SETFIELD_GC = optimize_FORCE_TOKEN
-
- def do_prepare_call(self, op):
- self.rollback_maybe('prepare call', op)
- funcval = self._get_funcval(op)
- if not funcval.is_constant():
- return [op] # cannot optimize
- self.begin_optimization(funcval, op)
- return []
-
- def do_push_arg(self, op):
- funcval = self._get_funcval(op)
- if not self.funcinfo or self.funcinfo.funcval is not funcval:
- return [op] # cannot optimize
- self.funcinfo.opargs.append(op)
- return []
-
- def do_call(self, op):
- funcval = self._get_funcval(op)
- funcinfo = self.funcinfo
- if (not funcinfo or funcinfo.funcval is not funcval or
- funcinfo.descr is None):
- return [op] # cannot optimize
- funcsymval = self.getvalue(op.getarg(2))
- arglist = [funcsymval.get_key_box()]
- for push_op in funcinfo.opargs:
- argval = self.getvalue(push_op.getarg(2))
- arglist.append(argval.get_key_box())
- newop = ResOperation(rop.CALL_RELEASE_GIL, arglist, op.result,
- descr=funcinfo.descr)
- self.commit_optimization()
- ops = []
- for delayed_op in funcinfo.delayed_ops:
- ops.append(delayed_op)
- ops.append(newop)
- return ops
-
- def do_struct_getsetfield(self, op, oopspec):
- ffitypeval = self.getvalue(op.getarg(1))
- addrval = self.getvalue(op.getarg(2))
- offsetval = self.getvalue(op.getarg(3))
- if not ffitypeval.is_constant() or not offsetval.is_constant():
- return [op]
- #
- ffitypeaddr = ffitypeval.box.getaddr()
- ffitype = llmemory.cast_adr_to_ptr(ffitypeaddr, clibffi.FFI_TYPE_P)
- offset = offsetval.box.getint()
- descr = self._get_field_descr(ffitype, offset)
- #
- arglist = [addrval.force_box(self.optimizer)]
- if oopspec == EffectInfo.OS_LIBFFI_STRUCT_GETFIELD:
- opnum = rop.GETFIELD_RAW
- else:
- opnum = rop.SETFIELD_RAW
- newval = self.getvalue(op.getarg(4))
- arglist.append(newval.force_box(self.optimizer))
- #
- newop = ResOperation(opnum, arglist, op.result, descr=descr)
- return [newop]
-
- def _get_field_descr(self, ffitype, offset):
- kind = libffi.types.getkind(ffitype)
- is_pointer = is_float = is_signed = False
- if ffitype is libffi.types.pointer:
- is_pointer = True
- elif kind == 'i':
- is_signed = True
- elif kind == 'f' or kind == 'I' or kind == 'U':
- # longlongs are treated as floats, see e.g. llsupport/descr.py:getDescrClass
- is_float = True
- else:
- assert False, "unsupported ffitype or kind"
- #
- fieldsize = intmask(ffitype.c_size)
- return self.optimizer.cpu.fielddescrof_dynamic(offset, fieldsize,
- is_pointer, is_float, is_signed)
-
- def do_getsetarrayitem(self, op, oopspec):
- ffitypeval = self.getvalue(op.getarg(1))
- widthval = self.getvalue(op.getarg(2))
- offsetval = self.getvalue(op.getarg(5))
- if not ffitypeval.is_constant() or not widthval.is_constant() or not offsetval.is_constant():
- return [op]
-
- ffitypeaddr = ffitypeval.box.getaddr()
- ffitype = llmemory.cast_adr_to_ptr(ffitypeaddr, clibffi.FFI_TYPE_P)
- offset = offsetval.box.getint()
- width = widthval.box.getint()
- descr = self._get_interior_descr(ffitype, width, offset)
-
- arglist = [
- self.getvalue(op.getarg(3)).force_box(self.optimizer),
- self.getvalue(op.getarg(4)).force_box(self.optimizer),
- ]
- if oopspec == EffectInfo.OS_LIBFFI_GETARRAYITEM:
- opnum = rop.GETINTERIORFIELD_RAW
- elif oopspec == EffectInfo.OS_LIBFFI_SETARRAYITEM:
- opnum = rop.SETINTERIORFIELD_RAW
- arglist.append(self.getvalue(op.getarg(6)).force_box(self.optimizer))
- else:
- assert False
- return [
- ResOperation(opnum, arglist, op.result, descr=descr),
- ]
-
- def _get_interior_descr(self, ffitype, width, offset):
- kind = libffi.types.getkind(ffitype)
- is_pointer = is_float = is_signed = False
- if ffitype is libffi.types.pointer:
- is_pointer = True
- elif kind == 'i':
- is_signed = True
- elif kind == 'f' or kind == 'I' or kind == 'U':
- # longlongs are treated as floats, see
- # e.g. llsupport/descr.py:getDescrClass
- is_float = True
- elif kind == 'u' or kind == 's':
- # they're all False
- pass
- else:
- raise NotImplementedError("unsupported ffitype or kind: %s" % kind)
- #
- fieldsize = rffi.getintfield(ffitype, 'c_size')
- return self.optimizer.cpu.interiorfielddescrof_dynamic(
- offset, width, fieldsize, is_pointer, is_float, is_signed
- )
-
-
- def propagate_forward(self, op):
- if self.logops is not None:
- debug_print(self.logops.repr_of_resop(op))
- dispatch_opt(self, op)
-
- def _get_oopspec(self, op):
- effectinfo = op.getdescr().get_extra_info()
- return effectinfo.oopspecindex
-
- def _get_funcval(self, op):
- return self.getvalue(op.getarg(1))
-
-dispatch_opt = make_dispatcher_method(OptFfiCall, 'optimize_',
- default=OptFfiCall.emit_operation)
diff --git a/pypy/jit/metainterp/optimizeopt/heap.py b/pypy/jit/metainterp/optimizeopt/heap.py
--- a/pypy/jit/metainterp/optimizeopt/heap.py
+++ b/pypy/jit/metainterp/optimizeopt/heap.py
@@ -255,6 +255,7 @@
opnum == rop.SETARRAYITEM_GC or # handled specially
opnum == rop.SETARRAYITEM_RAW or # no effect on GC struct
opnum == rop.SETINTERIORFIELD_RAW or # no effect on GC struct
+ opnum == rop.RAW_STORE or # no effect on GC struct
opnum == rop.STRSETITEM or # no effect on GC struct/array
opnum == rop.UNICODESETITEM or # no effect on GC struct/array
opnum == rop.DEBUG_MERGE_POINT or # no effect whatsoever
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
deleted file mode 100644
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
+++ /dev/null
@@ -1,315 +0,0 @@
-from pypy.rpython.lltypesystem import llmemory
-from pypy.rlib.libffi import Func, types
-from pypy.jit.metainterp.history import AbstractDescr
-from pypy.jit.codewriter.effectinfo import EffectInfo
-from pypy.jit.metainterp.optimizeopt.test.test_optimizebasic import BaseTestBasic
-from pypy.jit.metainterp.optimizeopt.test.test_optimizebasic import LLtypeMixin
-
-class MyCallDescr(AbstractDescr):
- """
- Fake calldescr to be used inside the tests.
-
- The particularity is that it provides an __eq__ method, so that it
- comparses by value by comparing the arg_types and typeinfo fields, so you
- can check that the signature of a call is really what you want.
- """
-
- def __init__(self, arg_types, typeinfo, flags):
- self.arg_types = arg_types
- self.typeinfo = typeinfo # return type
- self.flags = flags
-
- def __eq__(self, other):
- return (self.arg_types == other.arg_types and
- self.typeinfo == other.typeinfo and
- self.flags == other.get_ffi_flags())
-
-class FakeLLObject(object):
-
- def __init__(self, **kwds):
- self.__dict__.update(kwds)
- self._TYPE = llmemory.GCREF
-
- def _identityhash(self):
- return id(self)
-
-
-class TestFfiCall(BaseTestBasic, LLtypeMixin):
-
- enable_opts = "intbounds:rewrite:virtualize:string:pure:earlyforce:heap:ffi"
-
- class namespace:
- cpu = LLtypeMixin.cpu
- FUNC = LLtypeMixin.FUNC
- vable_token_descr = LLtypeMixin.valuedescr
- valuedescr = LLtypeMixin.valuedescr
-
- int_float__int_42 = MyCallDescr('if', 'i', 42)
- int_float__int_43 = MyCallDescr('if', 'i', 43)
- funcptr = FakeLLObject()
- func = FakeLLObject(_fake_class=Func,
- argtypes=[types.sint, types.double],
- restype=types.sint,
- flags=42)
- func2 = FakeLLObject(_fake_class=Func,
- argtypes=[types.sint, types.double],
- restype=types.sint,
- flags=43)
- #
- ffi_slong = types.slong
- dyn_123_field = cpu.fielddescrof_dynamic(offset=123,
- fieldsize=types.slong.c_size,
- is_pointer=False,
- is_float=False,
- is_signed=True)
- #
- def calldescr(cpu, FUNC, oopspecindex, extraeffect=None):
- if extraeffect == EffectInfo.EF_RANDOM_EFFECTS:
- f = None # means "can force all" really
- else:
- f = []
- einfo = EffectInfo(f, f, f, f, oopspecindex=oopspecindex,
- extraeffect=extraeffect)
- return cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, einfo)
- #
- libffi_prepare = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_PREPARE)
- libffi_push_arg = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_PUSH_ARG)
- libffi_call = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_CALL,
- EffectInfo.EF_RANDOM_EFFECTS)
- libffi_struct_getfield = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_STRUCT_GETFIELD)
- libffi_struct_setfield = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_STRUCT_SETFIELD)
-
- namespace = namespace.__dict__
-
- # ----------------------------------------------------------------------
- # this group of tests is the most important, as they represent the "real"
- # cases you actually get when using rlib.libffi
-
- def test_ffi_call_opt(self):
- ops = """
- [i0, f1]
- call(0, ConstPtr(func), descr=libffi_prepare)
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, f1)
- """
- expected = """
- [i0, f1]
- i3 = call_release_gil(12345, i0, f1, descr=int_float__int_42)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, f1)
- """
- loop = self.optimize_loop(ops, expected)
-
- def test_ffi_call_nonconst(self):
- ops = """
- [i0, f1, p2]
- call(0, p2, descr=libffi_prepare)
- call(0, p2, i0, descr=libffi_push_arg)
- call(0, p2, f1, descr=libffi_push_arg)
- i3 = call_may_force(0, p2, 12345, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, f1, p2)
- """
- expected = ops
- loop = self.optimize_loop(ops, expected)
-
- def test_handle_virtualizables(self):
- # this test needs an explanation to understand what goes on: see the
- # comment in optimize_FORCE_TOKEN
- ops = """
- [i0, f1, p2]
- call(0, ConstPtr(func), descr=libffi_prepare)
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- i4 = force_token()
- setfield_gc(p2, i4, descr=vable_token_descr)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() [p2]
- guard_no_exception() [p2]
- jump(i3, f1, p2)
- """
- expected = """
- [i0, f1, p2]
- i4 = force_token()
- setfield_gc(p2, i4, descr=vable_token_descr)
- i3 = call_release_gil(12345, i0, f1, descr=int_float__int_42)
- guard_not_forced() [p2]
- guard_no_exception() [p2]
- jump(i3, f1, p2)
- """
- loop = self.optimize_loop(ops, expected)
-
- # ----------------------------------------------------------------------
- # in pratice, the situations described in these tests should never happen,
- # but we still want to ensure correctness
-
- def test_rollback_if_op_in_between(self):
- ops = """
- [i0, f1]
- call(0, ConstPtr(func), descr=libffi_prepare)
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- i1 = int_add(i0, 1)
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, f1)
- """
- expected = ops
- loop = self.optimize_loop(ops, expected)
-
- def test_rollback_multiple_calls(self):
- ops = """
- [i0, i2, f1]
- call(0, ConstPtr(func), descr=libffi_prepare)
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- #
- # this is the culprit!
- call(0, ConstPtr(func2), descr=libffi_prepare)
- #
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- call(0, ConstPtr(func2), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func2), f1, descr=libffi_push_arg)
- i4 = call_may_force(0, ConstPtr(func2), 67890, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, i4, f1)
- """
- expected = ops
- loop = self.optimize_loop(ops, expected)
-
- def test_rollback_multiple_prepare(self):
- ops = """
- [i0, i2, f1]
- call(0, ConstPtr(func), descr=libffi_prepare)
- #
- # this is the culprit!
- call(0, ConstPtr(func2), descr=libffi_prepare)
- #
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- call(0, ConstPtr(func2), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func2), f1, descr=libffi_push_arg)
- i4 = call_may_force(0, ConstPtr(func2), 67890, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, i4, f1)
- """
- expected = ops
- loop = self.optimize_loop(ops, expected)
-
- def test_optimize_nested_call(self):
- ops = """
- [i0, i2, f1]
- call(0, ConstPtr(func), descr=libffi_prepare)
- #
- # this "nested" call is nicely optimized
- call(0, ConstPtr(func2), descr=libffi_prepare)
- call(0, ConstPtr(func2), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func2), f1, descr=libffi_push_arg)
- i4 = call_may_force(0, ConstPtr(func2), 67890, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- #
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, i4, f1)
- """
- expected = """
- [i0, i2, f1]
- call(0, ConstPtr(func), descr=libffi_prepare)
- #
- # this "nested" call is nicely optimized
- i4 = call_release_gil(67890, i0, f1, descr=int_float__int_43)
- guard_not_forced() []
- guard_no_exception() []
- #
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, i4, f1)
- """
- loop = self.optimize_loop(ops, expected)
-
- def test_rollback_force_token(self):
- ops = """
- [i0, f1, p2]
- call(0, ConstPtr(func), descr=libffi_prepare)
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- i4 = force_token()
- i5 = int_add(i0, 1) # culprit!
- setfield_gc(p2, i4, descr=vable_token_descr)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() [p2]
- guard_no_exception() [p2]
- jump(i3, f1, p2)
- """
- expected = ops
- loop = self.optimize_loop(ops, expected)
-
- def test_allow_setfields_in_between(self):
- ops = """
- [i0, f1, p2]
- call(0, ConstPtr(func), descr=libffi_prepare)
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- setfield_gc(p2, i0, descr=valuedescr)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, f1, p2)
- """
- expected = """
- [i0, f1, p2]
- setfield_gc(p2, i0, descr=valuedescr)
- i3 = call_release_gil(12345, i0, f1, descr=int_float__int_42)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, f1, p2)
- """
- loop = self.optimize_loop(ops, expected)
-
- def test_ffi_struct_fields(self):
- ops = """
- [i0]
- i1 = call(0, ConstClass(ffi_slong), i0, 123, descr=libffi_struct_getfield)
- i2 = int_add(i1, 1)
- call(0, ConstClass(ffi_slong), i0, 123, i2, descr=libffi_struct_setfield)
- jump(i1)
- """
- expected = """
- [i0]
- i1 = getfield_raw(i0, descr=dyn_123_field)
- i2 = int_add(i1, 1)
- setfield_raw(i0, i2, descr=dyn_123_field)
- jump(i1)
- """
- loop = self.optimize_loop(ops, expected)
-
- def test_ffi_struct_fields_nonconst(self):
- ops = """
- [i0, i1]
- i2 = call(0, ConstClass(ffi_slong), i0, i1, descr=libffi_struct_getfield)
- i3 = call(0, i1 , i0, 123, descr=libffi_struct_getfield)
- jump(i1)
- """
- expected = ops
- loop = self.optimize_loop(ops, expected)
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -41,14 +41,6 @@
#
chain, _ = build_opt_chain(metainterp_sd, "aaa:bbb")
check(chain, ["OptSimplify"])
- #
- chain, _ = build_opt_chain(metainterp_sd, "ffi")
- check(chain, ["OptFfiCall", "OptSimplify"])
- #
- metainterp_sd.config = get_pypy_config(translating=True)
- assert not metainterp_sd.config.translation.jit_ffi
- chain, _ = build_opt_chain(metainterp_sd, "ffi")
- check(chain, ["OptSimplify"])
# ____________________________________________________________
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_util.py b/pypy/jit/metainterp/optimizeopt/test/test_util.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_util.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_util.py
@@ -346,7 +346,6 @@
self.options = Fake()
self.globaldata = Fake()
self.config = get_pypy_config(translating=True)
- self.config.translation.jit_ffi = True
class logger_noopt:
@classmethod
diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -451,12 +451,27 @@
opimpl_getarrayitem_raw_f = _opimpl_getarrayitem_raw_any
@arguments("box", "descr", "box")
+ def _opimpl_getarrayitem_raw_pure_any(self, arraybox,arraydescr, indexbox):
+ return self.execute_with_descr(rop.GETARRAYITEM_RAW_PURE,
+ arraydescr, arraybox, indexbox)
+
+ opimpl_getarrayitem_raw_i_pure = _opimpl_getarrayitem_raw_pure_any
+ opimpl_getarrayitem_raw_f_pure = _opimpl_getarrayitem_raw_pure_any
+
+ @arguments("box", "descr", "box")
def _opimpl_getarrayitem_gc_pure_any(self, arraybox, arraydescr, indexbox):
+ if isinstance(arraybox, ConstPtr) and isinstance(indexbox, ConstInt):
+ # if the arguments are directly constants, bypass the heapcache
+ # completely
+ resbox = executor.execute(self.metainterp.cpu, self.metainterp,
+ rop.GETARRAYITEM_GC_PURE, arraydescr,
+ arraybox, indexbox)
+ return resbox.constbox()
return self._do_getarrayitem_gc_any(rop.GETARRAYITEM_GC_PURE, arraybox, arraydescr, indexbox)
- opimpl_getarrayitem_gc_pure_i = _opimpl_getarrayitem_gc_pure_any
- opimpl_getarrayitem_gc_pure_r = _opimpl_getarrayitem_gc_pure_any
- opimpl_getarrayitem_gc_pure_f = _opimpl_getarrayitem_gc_pure_any
+ opimpl_getarrayitem_gc_i_pure = _opimpl_getarrayitem_gc_pure_any
+ opimpl_getarrayitem_gc_r_pure = _opimpl_getarrayitem_gc_pure_any
+ opimpl_getarrayitem_gc_f_pure = _opimpl_getarrayitem_gc_pure_any
@arguments("box", "descr", "box", "box")
def _opimpl_setarrayitem_gc_any(self, arraybox, arraydescr,
@@ -563,6 +578,11 @@
@arguments("box", "descr")
def _opimpl_getfield_gc_pure_any(self, box, fielddescr):
+ if isinstance(box, ConstPtr):
+ # if 'box' is directly a ConstPtr, bypass the heapcache completely
+ resbox = executor.execute(self.metainterp.cpu, self.metainterp,
+ rop.GETFIELD_GC_PURE, fielddescr, box)
+ return resbox.constbox()
return self._opimpl_getfield_gc_any_pureornot(
rop.GETFIELD_GC_PURE, box, fielddescr)
opimpl_getfield_gc_i_pure = _opimpl_getfield_gc_pure_any
@@ -647,6 +667,20 @@
opimpl_setfield_raw_r = _opimpl_setfield_raw_any
opimpl_setfield_raw_f = _opimpl_setfield_raw_any
+ @arguments("box", "box", "descr", "box")
+ def _opimpl_raw_store(self, addrbox, offsetbox, arraydescr, valuebox):
+ self.execute_with_descr(rop.RAW_STORE, arraydescr,
+ addrbox, offsetbox, valuebox)
+ opimpl_raw_store_i = _opimpl_raw_store
+ opimpl_raw_store_f = _opimpl_raw_store
+
+ @arguments("box", "box", "descr")
+ def _opimpl_raw_load(self, addrbox, offsetbox, arraydescr):
+ return self.execute_with_descr(rop.RAW_LOAD, arraydescr,
+ addrbox, offsetbox)
+ opimpl_raw_load_i = _opimpl_raw_load
+ opimpl_raw_load_f = _opimpl_raw_load
+
@arguments("box", "descr", "descr", "orgpc")
def opimpl_record_quasiimmut_field(self, box, fielddescr,
mutatefielddescr, orgpc):
@@ -1368,6 +1402,8 @@
if vablebox is not None:
self.metainterp.history.record(rop.KEEPALIVE, [vablebox], None)
self.metainterp.handle_possible_exception()
+ if effectinfo.oopspecindex == effectinfo.OS_LIBFFI_CALL:
+ self.metainterp.direct_libffi_call()
return resbox
else:
effect = effectinfo.extraeffect
@@ -1462,6 +1498,7 @@
self.jitdrivers_sd = codewriter.callcontrol.jitdrivers_sd
self.virtualref_info = codewriter.callcontrol.virtualref_info
self.callinfocollection = codewriter.callcontrol.callinfocollection
+ self.has_libffi_call = codewriter.callcontrol.has_libffi_call
#
# store this information for fastpath of call_assembler
# (only the paths that can actually be taken)
@@ -2511,6 +2548,89 @@
else:
return None
+ def direct_libffi_call(self):
+ """Generate a direct call to C code, patching the CALL_MAY_FORCE
+ to jit_ffi_call() that occurred just now.
+ """
+ # an 'assert' that constant-folds away the rest of this function
+ # if the codewriter didn't produce any OS_LIBFFI_CALL at all.
+ assert self.staticdata.has_libffi_call
+ #
+ from pypy.rpython.lltypesystem import llmemory
+ from pypy.rlib.jit_libffi import CIF_DESCRIPTION_P
+ from pypy.jit.backend.llsupport.ffisupport import get_arg_descr
+ #
+ num_extra_guards = 0
+ while True:
+ op = self.history.operations[-1-num_extra_guards]
+ if op.getopnum() == rop.CALL_MAY_FORCE:
+ break
+ assert op.is_guard()
+ num_extra_guards += 1
+ #
+ box_cif_description = op.getarg(1)
+ if not isinstance(box_cif_description, ConstInt):
+ return
+ cif_description = box_cif_description.getint()
+ cif_description = llmemory.cast_int_to_adr(cif_description)
+ cif_description = llmemory.cast_adr_to_ptr(cif_description,
+ CIF_DESCRIPTION_P)
+ extrainfo = op.getdescr().get_extra_info()
+ calldescr = self.cpu.calldescrof_dynamic(cif_description, extrainfo)
+ if calldescr is None:
+ return
+ #
+ extra_guards = []
+ for i in range(num_extra_guards):
+ extra_guards.append(self.history.operations.pop())
+ extra_guards.reverse()
+ #
+ box_exchange_buffer = op.getarg(3)
+ self.history.operations.pop()
+ arg_boxes = []
+ for i in range(cif_description.nargs):
+ kind, descr = get_arg_descr(self.cpu, cif_description.atypes[i])
+ if kind == 'i':
+ box_arg = history.BoxInt()
+ elif kind == 'f':
+ box_arg = history.BoxFloat()
+ else:
+ assert kind == 'v'
+ continue
+ ofs = cif_description.exchange_args[i]
+ box_argpos = history.BoxInt()
+ self.history.record(rop.INT_ADD,
+ [box_exchange_buffer, ConstInt(ofs)],
+ box_argpos)
+ self.history.record(rop.GETARRAYITEM_RAW,
+ [box_argpos, ConstInt(0)],
+ box_arg, descr)
+ arg_boxes.append(box_arg)
+ #
+ kind, descr = get_arg_descr(self.cpu, cif_description.rtype)
+ if kind == 'i':
+ box_result = history.BoxInt()
+ elif kind == 'f':
+ box_result = history.BoxFloat()
+ else:
+ assert kind == 'v'
+ box_result = None
+ self.history.record(rop.CALL_RELEASE_GIL,
+ [op.getarg(2)] + arg_boxes,
+ box_result, calldescr)
+ #
+ self.history.operations.extend(extra_guards)
+ #
+ if box_result is not None:
+ ofs = cif_description.exchange_result
+ box_resultpos = history.BoxInt()
+ self.history.record(rop.INT_ADD,
+ [box_exchange_buffer, ConstInt(ofs)],
+ box_resultpos)
+ self.history.record(rop.SETARRAYITEM_RAW,
+ [box_resultpos, ConstInt(0), box_result],
+ None, descr)
+
# ____________________________________________________________
class ChangeFrame(JitException):
diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py
--- a/pypy/jit/metainterp/resoperation.py
+++ b/pypy/jit/metainterp/resoperation.py
@@ -460,6 +460,7 @@
'GETFIELD_GC_PURE/1d',
'GETFIELD_RAW_PURE/1d',
'GETARRAYITEM_GC_PURE/2d',
+ 'GETARRAYITEM_RAW_PURE/2d',
'UNICODELEN/1',
'UNICODEGETITEM/2',
#
@@ -472,7 +473,7 @@
'GETARRAYITEM_GC/2d',
'GETARRAYITEM_RAW/2d',
'GETINTERIORFIELD_GC/2d',
- 'GETINTERIORFIELD_RAW/2d',
+ 'RAW_LOAD/2d',
'GETFIELD_GC/1d',
'GETFIELD_RAW/1d',
'_MALLOC_FIRST',
@@ -491,7 +492,8 @@
'SETARRAYITEM_GC/3d',
'SETARRAYITEM_RAW/3d',
'SETINTERIORFIELD_GC/3d',
- 'SETINTERIORFIELD_RAW/3d',
+ 'SETINTERIORFIELD_RAW/3d', # only used by llsupport/rewrite.py
+ 'RAW_STORE/3d',
'SETFIELD_GC/2d',
'SETFIELD_RAW/2d',
'STRSETITEM/3',
diff --git a/pypy/jit/metainterp/test/support.py b/pypy/jit/metainterp/test/support.py
--- a/pypy/jit/metainterp/test/support.py
+++ b/pypy/jit/metainterp/test/support.py
@@ -42,6 +42,9 @@
trace_limit = sys.maxint
enable_opts = ALL_OPTS_DICT
+ if kwds.pop('disable_optimizations', False):
+ FakeWarmRunnerState.enable_opts = {}
+
func._jit_unroll_safe_ = True
rtyper = support.annotate(func, values, type_system=type_system,
translationoptions=translationoptions)
diff --git a/pypy/jit/metainterp/test/test_ajit.py b/pypy/jit/metainterp/test/test_ajit.py
--- a/pypy/jit/metainterp/test/test_ajit.py
+++ b/pypy/jit/metainterp/test/test_ajit.py
@@ -3797,6 +3797,7 @@
assert res == 3
def test_float_bytes(self):
+ from pypy.rlib.rfloat import isnan
def f(n):
ll = float2longlong(n)
return longlong2float(ll)
@@ -3804,7 +3805,7 @@
for x in [2.5, float("nan"), -2.5, float("inf")]:
# There are tests elsewhere to verify the correctness of this.
res = self.interp_operations(f, [x])
- assert res == x or math.isnan(x) and math.isnan(res)
+ assert res == x or isnan(x) and isnan(res)
class TestLLtype(BaseLLtypeTests, LLJitMixin):
diff --git a/pypy/jit/metainterp/test/test_fficall.py b/pypy/jit/metainterp/test/test_fficall.py
--- a/pypy/jit/metainterp/test/test_fficall.py
+++ b/pypy/jit/metainterp/test/test_fficall.py
@@ -1,210 +1,106 @@
-from __future__ import with_statement
import py
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.jit.metainterp.test.support import LLJitMixin
+from pypy.rlib import jit
+from pypy.rlib.jit_libffi import types, CIF_DESCRIPTION, FFI_TYPE_PP
+from pypy.rlib.unroll import unrolling_iterable
-from pypy.jit.metainterp.test.support import LLJitMixin
-from pypy.rlib.jit import JitDriver, promote, dont_look_inside
-from pypy.rlib.libffi import (ArgChain, IS_32_BIT, array_getitem, array_setitem,
- types, struct_setfield_int, struct_getfield_int)
-from pypy.rlib.objectmodel import specialize
-from pypy.rlib.rarithmetic import r_singlefloat, r_longlong, r_ulonglong
-from pypy.rlib.test.test_libffi import TestLibffiCall as _TestLibffiCall
-from pypy.rlib.unroll import unrolling_iterable
-from pypy.rpython.lltypesystem import lltype, rffi
-from pypy.tool.sourcetools import func_with_new_name
+def get_description(atypes, rtype):
+ p = lltype.malloc(CIF_DESCRIPTION, len(atypes),
+ flavor='raw', immortal=True)
+ p.abi = 42
+ p.nargs = len(atypes)
+ p.rtype = rtype
+ p.atypes = lltype.malloc(FFI_TYPE_PP.TO, len(atypes),
+ flavor='raw', immortal=True)
+ for i in range(len(atypes)):
+ p.atypes[i] = atypes[i]
+ return p
-class FfiCallTests(_TestLibffiCall):
- # ===> ../../../rlib/test/test_libffi.py
- def call(self, funcspec, args, RESULT, is_struct=False, jitif=[]):
- """
- Call the function specified by funcspec in a loop, and let the jit to
- see and optimize it.
- """
- #
- lib, name, argtypes, restype = funcspec
- method_and_args = []
- for argval in args:
- if isinstance(argval, tuple):
- method_name, argval = argval
+class FfiCallTests(object):
+
+ def _run(self, atypes, rtype, avalues, rvalue):
+ cif_description = get_description(atypes, rtype)
+
+ def verify(*args):
+ assert args == tuple(avalues)
+ return rvalue
+ FUNC = lltype.FuncType([lltype.typeOf(avalue) for avalue in avalues],
+ lltype.typeOf(rvalue))
+ func = lltype.functionptr(FUNC, 'verify', _callable=verify)
+ func_addr = rffi.cast(rffi.VOIDP, func)
+
+ for i in range(len(avalues)):
+ cif_description.exchange_args[i] = (i+1) * 16
+ cif_description.exchange_result = (len(avalues)+1) * 16
+
+ unroll_avalues = unrolling_iterable(avalues)
+
+ @jit.oopspec("libffi_call(cif_description,func_addr,exchange_buffer)")
+ def fake_call(cif_description, func_addr, exchange_buffer):
+ ofs = 16
+ for avalue in unroll_avalues:
+ TYPE = rffi.CArray(lltype.typeOf(avalue))
+ data = rffi.ptradd(exchange_buffer, ofs)
+ assert rffi.cast(lltype.Ptr(TYPE), data)[0] == avalue
+ ofs += 16
+ if rvalue is not None:
+ write_rvalue = rvalue
else:
- method_name = 'arg'
- method_and_args.append((method_name, argval))
- method_and_args = unrolling_iterable(method_and_args)
- #
- reds = ['n', 'res', 'func']
- if (RESULT is rffi.DOUBLE or
- IS_32_BIT and RESULT in [rffi.LONGLONG, rffi.ULONGLONG]):
- reds = ['n', 'func', 'res'] # 'double' floats must be *after* refs
- driver = JitDriver(reds=reds, greens=[])
- init_result = rffi.cast(RESULT, 0)
- #
- def g(func):
- # a different function, which is marked as "dont_look_inside"
- # in case it uses an unsupported argument
- argchain = ArgChain()
- # this loop is unrolled
- for method_name, argval in method_and_args:
- getattr(argchain, method_name)(argval)
- return func.call(argchain, RESULT, is_struct=is_struct)
- #
- def f(n):
- func = lib.getpointer(name, argtypes, restype)
- res = init_result
- while n < 10:
- driver.jit_merge_point(n=n, res=res, func=func)
- promote(func)
- res = g(func)
- n += 1
+ write_rvalue = 12923 # ignored
+ TYPE = rffi.CArray(lltype.typeOf(write_rvalue))
+ data = rffi.ptradd(exchange_buffer, ofs)
+ rffi.cast(lltype.Ptr(TYPE), data)[0] = write_rvalue
+
+ def f():
+ exbuf = lltype.malloc(rffi.CCHARP.TO, (len(avalues)+2) * 16,
+ flavor='raw', zero=True)
+ ofs = 16
+ for avalue in unroll_avalues:
+ TYPE = rffi.CArray(lltype.typeOf(avalue))
+ data = rffi.ptradd(exbuf, ofs)
+ rffi.cast(lltype.Ptr(TYPE), data)[0] = avalue
+ ofs += 16
+
+ fake_call(cif_description, func_addr, exbuf)
+
+ if rvalue is None:
+ res = 654321
+ else:
+ TYPE = rffi.CArray(lltype.typeOf(rvalue))
+ data = rffi.ptradd(exbuf, ofs)
+ res = rffi.cast(lltype.Ptr(TYPE), data)[0]
+ lltype.free(exbuf, flavor='raw')
return res
- #
- res = self.meta_interp(f, [0], backendopt=True,
- supports_floats = self.supports_all,
- supports_longlong = self.supports_all,
- supports_singlefloats = self.supports_all)
- d = {'floats': self.supports_all,
- 'longlong': self.supports_all or not IS_32_BIT,
- 'singlefloats': self.supports_all,
- 'byval': False}
- supported = all(d[check] for check in jitif)
- if supported:
- self.check_resops(
- call_release_gil=2, # a CALL_RELEASE_GIL, and no other CALLs
- call=0,
- call_may_force=0,
- guard_no_exception=2,
- guard_not_forced=2,
- int_add=2,
- int_lt=2,
- guard_true=2,
- jump=1)
- else:
- self.check_resops(
- call_release_gil=0, # no CALL_RELEASE_GIL
- int_add=2,
- int_lt=2,
- guard_true=2,
- jump=1)
- return res
- def test_byval_result(self):
- _TestLibffiCall.test_byval_result(self)
- test_byval_result.__doc__ = _TestLibffiCall.test_byval_result.__doc__
- test_byval_result.dont_track_allocations = True
+ res = f()
+ assert res == rvalue or (res, rvalue) == (654321, None)
+ res = self.interp_operations(f, [])
+ assert res == rvalue or (res, rvalue) == (654321, None)
+ self.check_operations_history(call_may_force=0,
+ call_release_gil=1)
-class FfiLookupTests(object):
- def test_array_fields(self):
- myjitdriver = JitDriver(
- greens = [],
- reds = ["n", "i", "points", "result_point"],
- )
+ def test_simple_call(self):
+ self._run([types.signed] * 2, types.signed, [456, 789], -42)
- POINT = lltype.Struct("POINT",
- ("x", lltype.Signed),
- ("y", lltype.Signed),
- )
- def f(points, result_point, n):
- i = 0
- while i < n:
- myjitdriver.jit_merge_point(i=i, points=points, n=n,
- result_point=result_point)
- x = array_getitem(
- types.slong, rffi.sizeof(lltype.Signed) * 2, points, i, 0
- )
- y = array_getitem(
- types.slong, rffi.sizeof(lltype.Signed) * 2, points, i, rffi.sizeof(lltype.Signed)
- )
+ def test_many_arguments(self):
+ for i in [0, 6, 20]:
+ self._run([types.signed] * i, types.signed,
+ [-123456*j for j in range(i)],
+ -42434445)
- cur_x = array_getitem(
- types.slong, rffi.sizeof(lltype.Signed) * 2, result_point, 0, 0
- )
- cur_y = array_getitem(
- types.slong, rffi.sizeof(lltype.Signed) * 2, result_point, 0, rffi.sizeof(lltype.Signed)
- )
+ def test_simple_call_float(self):
+ self._run([types.double] * 2, types.double, [45.6, 78.9], -4.2)
- array_setitem(
- types.slong, rffi.sizeof(lltype.Signed) * 2, result_point, 0, 0, cur_x + x
- )
- array_setitem(
- types.slong, rffi.sizeof(lltype.Signed) * 2, result_point, 0, rffi.sizeof(lltype.Signed), cur_y + y
- )
- i += 1
+ def test_returns_none(self):
+ self._run([types.signed] * 2, types.void, [456, 789], None)
- def main(n):
- with lltype.scoped_alloc(rffi.CArray(POINT), n) as points:
- with lltype.scoped_alloc(rffi.CArray(POINT), 1) as result_point:
- for i in xrange(n):
- points[i].x = i * 2
- points[i].y = i * 2 + 1
- points = rffi.cast(rffi.CArrayPtr(lltype.Char), points)
- result_point[0].x = 0
- result_point[0].y = 0
- result_point = rffi.cast(rffi.CArrayPtr(lltype.Char), result_point)
- f(points, result_point, n)
- result_point = rffi.cast(rffi.CArrayPtr(POINT), result_point)
- return result_point[0].x * result_point[0].y
-
- assert self.meta_interp(main, [10]) == main(10) == 9000
- self.check_resops({'jump': 1, 'int_lt': 2, 'setinteriorfield_raw': 4,
- 'getinteriorfield_raw': 8, 'int_add': 6, 'guard_true': 2})
-
- def _test_getitem_type(self, TYPE, ffitype, COMPUTE_TYPE):
- reds = ["n", "i", "s", "data"]
- if COMPUTE_TYPE is lltype.Float:
- # Move the float var to the back.
- reds.remove("s")
- reds.append("s")
- myjitdriver = JitDriver(
- greens = [],
- reds = reds,
- )
- def f(data, n):
- i = 0
- s = rffi.cast(COMPUTE_TYPE, 0)
- while i < n:
- myjitdriver.jit_merge_point(n=n, i=i, s=s, data=data)
- s += rffi.cast(COMPUTE_TYPE, array_getitem(ffitype, rffi.sizeof(TYPE), data, 0, 0))
- i += 1
- return s
- def main(n):
- with lltype.scoped_alloc(rffi.CArray(TYPE), 1) as data:
- data[0] = rffi.cast(TYPE, 200)
- return f(data, n)
- assert self.meta_interp(main, [10]) == 2000
-
- def test_array_getitem_uint8(self):
- self._test_getitem_type(rffi.UCHAR, types.uchar, lltype.Signed)
- self.check_resops({'jump': 1, 'int_lt': 2, 'getinteriorfield_raw': 2,
- 'guard_true': 2, 'int_add': 4})
-
- def test_array_getitem_float(self):
- self._test_getitem_type(rffi.FLOAT, types.float, lltype.Float)
+ def test_returns_signedchar(self):
+ self._run([types.signed], types.sint8, [456],
+ rffi.cast(rffi.SIGNEDCHAR, -42))
class TestFfiCall(FfiCallTests, LLJitMixin):
- supports_all = False
-
-class TestFfiCallSupportAll(FfiCallTests, LLJitMixin):
- supports_all = True # supports_{floats,longlong,singlefloats}
-
- def test_struct_getfield(self):
- myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'addr'])
-
- def f(n):
- i = 0
- addr = lltype.malloc(rffi.VOIDP.TO, 10, flavor='raw')
- while i < n:
- myjitdriver.jit_merge_point(n=n, i=i, addr=addr)
- struct_setfield_int(types.slong, addr, 0, 1)
- i += struct_getfield_int(types.slong, addr, 0)
- lltype.free(addr, flavor='raw')
- return i
- assert self.meta_interp(f, [20]) == f(20)
- self.check_resops(
- setfield_raw=2,
- getfield_raw=2,
- call=0)
-
-
-class TestFfiLookup(FfiLookupTests, LLJitMixin):
pass
diff --git a/pypy/jit/metainterp/test/test_immutable.py b/pypy/jit/metainterp/test/test_immutable.py
--- a/pypy/jit/metainterp/test/test_immutable.py
+++ b/pypy/jit/metainterp/test/test_immutable.py
@@ -89,6 +89,92 @@
int_add=3)
+ def test_raw_field_and_array(self):
+ from pypy.rpython.lltypesystem import lltype
+ X = lltype.Struct('X',
+ ('a', lltype.Signed),
+ ('b', lltype.Array(lltype.Signed,
+ hints={'nolength': True, 'immutable': True})),
+ hints={'immutable': True})
+
+ x = lltype.malloc(X, 4, flavor='raw', immortal=True)
+ x.a = 6
+ x.b[2] = 7
+ xlist = [x, lltype.nullptr(X)]
+ def g(num):
+ if num < 0:
+ num = 0
+ return num
+ g._dont_inline_ = True
+ def f(num):
+ num = g(num)
+ x = xlist[num]
+ return x.a * x.b[2]
+ #
+ res = self.interp_operations(f, [0], disable_optimizations=True)
+ assert res == 42
+ self.check_operations_history(getfield_raw_pure=1,
+ getarrayitem_raw_pure=1,
+ int_mul=1)
+ #
+ # second try, in which we get num=0 constant-folded through f()
+ res = self.interp_operations(f, [-1], disable_optimizations=True)
+ assert res == 42
+ self.check_operations_history(getfield_raw_pure=0,
+ getarrayitem_raw_pure=0,
+ int_mul=0)
+
+ def test_read_on_promoted(self):
+ # this test used to fail because the n = f.n was staying alive
+ # in a box (not a const, as it was read before promote), and
+ # thus the second f.n was returning the same box, although it
+ # could now return a const.
+ class Foo(object):
+ _immutable_fields_ = ['n']
+ def __init__(self, n):
+ self.n = n
+ f1 = Foo(42); f2 = Foo(43)
+ @jit.dont_look_inside
+ def some(m):
+ return [f1, f2][m]
+ @jit.dont_look_inside
+ def do_stuff_with(n):
+ print n
+ def main(m):
+ f = some(m)
+ n = f.n
+ f = jit.hint(f, promote=True)
+ res = f.n * 6
+ do_stuff_with(n)
+ return res
+ res = self.interp_operations(main, [1])
+ assert res == 43 * 6
+ self.check_operations_history(int_mul=0) # constant-folded
+
+ def test_read_on_promoted_array(self):
+ class Foo(object):
+ _immutable_fields_ = ['lst[*]']
+ def __init__(self, lst):
+ self.lst = lst
+ f1 = Foo([42]); f2 = Foo([43])
+ @jit.dont_look_inside
+ def some(m):
+ return [f1, f2][m]
+ @jit.dont_look_inside
+ def do_stuff_with(n):
+ print n
+ def main(m):
+ f = some(m)
+ n = f.lst[0]
+ f = jit.hint(f, promote=True)
+ res = f.lst[0] * 6
+ do_stuff_with(n)
+ return res
+ res = self.interp_operations(main, [1])
+ assert res == 43 * 6
+ self.check_operations_history(int_mul=0) # constant-folded
+
+
class TestLLtypeImmutableFieldsTests(ImmutableFieldsTests, LLJitMixin):
pass
diff --git a/pypy/jit/metainterp/test/test_rawmem.py b/pypy/jit/metainterp/test/test_rawmem.py
--- a/pypy/jit/metainterp/test/test_rawmem.py
+++ b/pypy/jit/metainterp/test/test_rawmem.py
@@ -1,8 +1,9 @@
from pypy.jit.metainterp.test.support import LLJitMixin
from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rlib.rawstorage import (alloc_raw_storage, raw_storage_setitem,
+ free_raw_storage, raw_storage_getitem)
-
-class TestJITRawMem(LLJitMixin):
+class RawMemTests(object):
def test_cast_void_ptr(self):
TP = lltype.Array(lltype.Float, hints={"nolength": True})
VOID_TP = lltype.Array(lltype.Void, hints={"nolength": True, "uncast_on_llgraph": True})
@@ -18,7 +19,7 @@
s += rffi.cast(lltype.Ptr(TP), a.storage)[0]
lltype.free(x, flavor="raw")
return s
- res = self.interp_operations(f, [10])
+ self.interp_operations(f, [10])
def test_fixed_size_malloc(self):
TIMEVAL = lltype.Struct('dummy', ('tv_sec', rffi.LONG), ('tv_usec', rffi.LONG))
@@ -30,3 +31,32 @@
assert res == 42
self.check_operations_history({'call': 2, 'guard_no_exception': 1,
'finish': 1})
+
+ def test_raw_storage_int(self):
+ def f():
+ p = alloc_raw_storage(15)
+ raw_storage_setitem(p, 3, 24)
+ res = raw_storage_getitem(lltype.Signed, p, 3)
+ free_raw_storage(p)
+ return res
+ res = self.interp_operations(f, [])
+ assert res == 24
+ self.check_operations_history({'call': 2, 'guard_no_exception': 1,
+ 'raw_store': 1, 'raw_load': 1,
+ 'finish': 1})
+
+ def test_raw_storage_float(self):
+ def f():
+ p = alloc_raw_storage(15)
+ raw_storage_setitem(p, 3, 2.4e15)
+ res = raw_storage_getitem(lltype.Float, p, 3)
+ free_raw_storage(p)
+ return res
+ res = self.interp_operations(f, [])
+ assert res == 2.4e15
+ self.check_operations_history({'call': 2, 'guard_no_exception': 1,
+ 'raw_store': 1, 'raw_load': 1,
+ 'finish': 1})
+
+class TestRawMem(RawMemTests, LLJitMixin):
+ pass
diff --git a/pypy/jit/metainterp/test/test_warmspot.py b/pypy/jit/metainterp/test/test_warmspot.py
--- a/pypy/jit/metainterp/test/test_warmspot.py
+++ b/pypy/jit/metainterp/test/test_warmspot.py
@@ -260,6 +260,33 @@
pass # other case
self.meta_interp(f1, [18])
+ def test_bug_constant_int(self):
+ py.test.skip("crashes because a is a constant")
+ from pypy.rpython.lltypesystem import lltype, rffi
+ mydriver = JitDriver(greens=['a'], reds=['m'])
+ def f1(m, a):
+ while m > 0:
+ mydriver.jit_merge_point(a=a, m=m)
+ m = m - 1
+ def entry(m):
+ f1(m, 42)
+ self.meta_interp(entry, [18])
+
+ def test_bug_constant_instance(self):
+ py.test.skip("crashes because a is a constant")
+ from pypy.rpython.lltypesystem import lltype, rffi
+ mydriver = JitDriver(greens=['a'], reds=['m'])
+ class A(object):
+ pass
+ a1 = A()
+ def f1(m, a):
+ while m > 0:
+ mydriver.jit_merge_point(a=a, m=m)
+ m = m - 1
+ def entry(m):
+ f1(m, a1)
+ self.meta_interp(entry, [18])
+
def test_bug_constant_rawptrs(self):
py.test.skip("crashes because a is a constant")
from pypy.rpython.lltypesystem import lltype, rffi
diff --git a/pypy/jit/metainterp/warmspot.py b/pypy/jit/metainterp/warmspot.py
--- a/pypy/jit/metainterp/warmspot.py
+++ b/pypy/jit/metainterp/warmspot.py
@@ -79,10 +79,6 @@
translator.config.translation.list_comprehension_operations = True
except ConfigError:
pass
- try:
- translator.config.translation.jit_ffi = True
- except ConfigError:
- pass
warmrunnerdesc = WarmRunnerDesc(translator, backendopt=backendopt, **kwds)
for jd in warmrunnerdesc.jitdrivers_sd:
jd.warmstate.set_param_threshold(3) # for tests
diff --git a/pypy/module/__pypy__/interp_time.py b/pypy/module/__pypy__/interp_time.py
--- a/pypy/module/__pypy__/interp_time.py
+++ b/pypy/module/__pypy__/interp_time.py
@@ -1,5 +1,5 @@
from __future__ import with_statement
-import os
+import sys
from pypy.interpreter.error import exception_from_errno
from pypy.interpreter.gateway import unwrap_spec
@@ -7,10 +7,11 @@
from pypy.rpython.tool import rffi_platform
from pypy.translator.tool.cbuild import ExternalCompilationInfo
-if os.name == 'nt':
+if sys.platform == 'linux2':
+ libraries = ["rt"]
+else:
libraries = []
-else:
- libraries = ["rt"]
+
class CConfig:
_compilation_info_ = ExternalCompilationInfo(
diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/__init__.py
@@ -0,0 +1,42 @@
+from pypy.interpreter.mixedmodule import MixedModule
+
+class Module(MixedModule):
+
+ appleveldefs = {
+ }
+ interpleveldefs = {
+ '__version__': 'space.wrap("0.3")',
+
+ 'nonstandard_integer_types': 'misc.nonstandard_integer_types',
+
+ 'load_library': 'libraryobj.load_library',
+
+ 'new_primitive_type': 'newtype.new_primitive_type',
+ 'new_pointer_type': 'newtype.new_pointer_type',
+ 'new_array_type': 'newtype.new_array_type',
+ 'new_struct_type': 'newtype.new_struct_type',
+ 'new_union_type': 'newtype.new_union_type',
+ 'complete_struct_or_union': 'newtype.complete_struct_or_union',
+ 'new_void_type': 'newtype.new_void_type',
+ 'new_enum_type': 'newtype.new_enum_type',
+ 'new_function_type': 'newtype.new_function_type',
+
+ 'newp': 'func.newp',
+ 'cast': 'func.cast',
+ 'callback': 'func.callback',
+ 'alignof': 'func.alignof',
+ 'sizeof': 'func.sizeof',
+ 'typeof': 'func.typeof',
+ 'offsetof': 'func.offsetof',
+ '_getfields': 'func._getfields',
+ 'getcname': 'func.getcname',
+
+ 'string': 'func.string',
+ 'buffer': 'cbuffer.buffer',
+
+ 'get_errno': 'cerrno.get_errno',
+ 'set_errno': 'cerrno.set_errno',
+
+ 'FFI_DEFAULT_ABI': 'ctypefunc._get_abi(space, "FFI_DEFAULT_ABI")',
+ 'FFI_CDECL': 'ctypefunc._get_abi(space,"FFI_DEFAULT_ABI")',#win32 name
+ }
diff --git a/pypy/module/_cffi_backend/cbuffer.py b/pypy/module/_cffi_backend/cbuffer.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/cbuffer.py
@@ -0,0 +1,55 @@
+from pypy.interpreter.error import operationerrfmt
+from pypy.interpreter.buffer import RWBuffer
+from pypy.interpreter.gateway import unwrap_spec
+from pypy.rpython.lltypesystem import rffi
+from pypy.module._cffi_backend import cdataobj, ctypeptr, ctypearray
+
+
+class LLBuffer(RWBuffer):
+ _immutable_ = True
+
+ def __init__(self, raw_cdata, size):
+ self.raw_cdata = raw_cdata
+ self.size = size
+
+ def getlength(self):
+ return self.size
+
+ def getitem(self, index):
+ return self.raw_cdata[index]
+
+ def setitem(self, index, char):
+ self.raw_cdata[index] = char
+
+ def get_raw_address(self):
+ return self.raw_cdata
+
+ def getslice(self, start, stop, step, size):
+ if step == 1:
+ return rffi.charpsize2str(rffi.ptradd(self.raw_cdata, start), size)
+ return RWBuffer.getslice(self, start, stop, step, size)
+
+ def setslice(self, start, string):
+ raw_cdata = rffi.ptradd(self.raw_cdata, start)
+ for i in range(len(string)):
+ raw_cdata[i] = string[i]
+
+
+ at unwrap_spec(cdata=cdataobj.W_CData, size=int)
+def buffer(space, cdata, size=-1):
+ ctype = cdata.ctype
+ if isinstance(ctype, ctypeptr.W_CTypePointer):
+ if size < 0:
+ size = ctype.ctitem.size
+ elif isinstance(ctype, ctypearray.W_CTypeArray):
+ if size < 0:
+ size = cdata._sizeof()
+ else:
+ raise operationerrfmt(space.w_TypeError,
+ "expected a pointer or array cdata, got '%s'",
+ ctype.name)
+ if size < 0:
+ raise operationerrfmt(space.w_TypeError,
+ "don't know the size pointed to by '%s'",
+ ctype.name)
+ return space.wrap(LLBuffer(cdata._cdata, size))
diff --git a/pypy/module/_cffi_backend/ccallback.py b/pypy/module/_cffi_backend/ccallback.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ccallback.py
@@ -0,0 +1,200 @@
+"""
+Callbacks.
+"""
+import os
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rlib.objectmodel import compute_unique_id, keepalive_until_here
+from pypy.rlib import clibffi, rweakref, rgc
+from pypy.rlib.rarithmetic import r_ulonglong
+
+from pypy.module._cffi_backend.cdataobj import W_CData
+from pypy.module._cffi_backend.ctypefunc import SIZE_OF_FFI_ARG, BIG_ENDIAN
+from pypy.module._cffi_backend.ctypefunc import W_CTypeFunc
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveSigned
+from pypy.module._cffi_backend.ctypevoid import W_CTypeVoid
+from pypy.module._cffi_backend import cerrno, misc
+
+# ____________________________________________________________
+
+
+class W_CDataCallback(W_CData):
+ #_immutable_fields_ = ...
+ ll_error = lltype.nullptr(rffi.CCHARP.TO)
+
+ def __init__(self, space, ctype, w_callable, w_error):
+ raw_closure = rffi.cast(rffi.CCHARP, clibffi.closureHeap.alloc())
+ W_CData.__init__(self, space, raw_closure, ctype)
+ #
+ if not space.is_true(space.callable(w_callable)):
+ raise operationerrfmt(space.w_TypeError,
+ "expected a callable object, not %s",
+ space.type(w_callable).getname(space))
+ self.w_callable = w_callable
+ self.w_error = w_error
+ #
+ fresult = self.getfunctype().ctitem
+ size = fresult.size
+ if size > 0:
+ if fresult.is_primitive_integer and size < SIZE_OF_FFI_ARG:
+ size = SIZE_OF_FFI_ARG
+ self.ll_error = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw',
+ zero=True)
+ if not space.is_w(w_error, space.w_None):
+ convert_from_object_fficallback(fresult, self.ll_error, w_error)
+ #
+ self.unique_id = compute_unique_id(self)
+ global_callback_mapping.set(self.unique_id, self)
+ #
+ cif_descr = self.getfunctype().cif_descr
+ if not cif_descr:
+ raise OperationError(space.w_NotImplementedError,
+ space.wrap("callbacks with '...'"))
+ res = clibffi.c_ffi_prep_closure(self.get_closure(), cif_descr.cif,
+ invoke_callback,
+ rffi.cast(rffi.VOIDP, self.unique_id))
+ if rffi.cast(lltype.Signed, res) != clibffi.FFI_OK:
+ raise OperationError(space.w_SystemError,
+ space.wrap("libffi failed to build this callback"))
+
+ def get_closure(self):
+ return rffi.cast(clibffi.FFI_CLOSUREP, self._cdata)
+
+ #@rgc.must_be_light_finalizer
+ def __del__(self):
+ clibffi.closureHeap.free(self.get_closure())
+ if self.ll_error:
+ lltype.free(self.ll_error, flavor='raw')
+
+ def _repr_extra(self):
+ space = self.space
+ return 'calling ' + space.str_w(space.repr(self.w_callable))
+
+ def getfunctype(self):
+ ctype = self.ctype
+ if not isinstance(ctype, W_CTypeFunc):
+ space = self.space
+ raise OperationError(space.w_TypeError,
+ space.wrap("expected a function ctype"))
+ return ctype
+
+ def invoke(self, ll_args, ll_res):
+ space = self.space
+ ctype = self.getfunctype()
+ args_w = []
+ for i, farg in enumerate(ctype.fargs):
+ ll_arg = rffi.cast(rffi.CCHARP, ll_args[i])
+ args_w.append(farg.convert_to_object(ll_arg))
+ fresult = ctype.ctitem
+ #
+ w_res = space.call(self.w_callable, space.newtuple(args_w))
+ #
+ convert_from_object_fficallback(fresult, ll_res, w_res)
+
+ def print_error(self, operr):
+ space = self.space
+ operr.write_unraisable(space, "cffi callback", self.w_callable)
+
+ def write_error_return_value(self, ll_res):
+ fresult = self.getfunctype().ctitem
+ if fresult.size > 0:
+ misc._raw_memcopy(self.ll_error, ll_res, fresult.size)
+ keepalive_until_here(self)
+
+
+global_callback_mapping = rweakref.RWeakValueDictionary(int, W_CDataCallback)
+
+
+def convert_from_object_fficallback(fresult, ll_res, w_res):
+ space = fresult.space
+ small_result = fresult.size < SIZE_OF_FFI_ARG
+ if small_result and isinstance(fresult, W_CTypeVoid):
+ if not space.is_w(w_res, space.w_None):
+ raise OperationError(space.w_TypeError,
+ space.wrap("callback with the return type 'void'"
+ " must return None"))
+ return
+ #
+ if small_result and fresult.is_primitive_integer:
+ # work work work around a libffi irregularity: for integer return
+ # types we have to fill at least a complete 'ffi_arg'-sized result
+ # buffer.
+ if type(fresult) is W_CTypePrimitiveSigned:
+ # It's probably fine to always zero-extend, but you never
+ # know: maybe some code somewhere expects a negative
+ # 'short' result to be returned into EAX as a 32-bit
+ # negative number. Better safe than sorry. This code
+ # is about that case. Let's ignore this for enums.
+ #
+ # do a first conversion only to detect overflows. This
+ # conversion produces stuff that is otherwise ignored.
+ fresult.convert_from_object(ll_res, w_res)
+ #
+ # manual inlining and tweaking of
+ # W_CTypePrimitiveSigned.convert_from_object() in order
+ # to write a whole 'ffi_arg'.
+ value = misc.as_long_long(space, w_res)
+ value = r_ulonglong(value)
+ misc.write_raw_integer_data(ll_res, value, SIZE_OF_FFI_ARG)
+ return
+ else:
+ # zero extension: fill the '*result' with zeros, and (on big-
+ # endian machines) correct the 'result' pointer to write to
+ misc._raw_memclear(ll_res, SIZE_OF_FFI_ARG)
+ if BIG_ENDIAN:
+ diff = SIZE_OF_FFI_ARG - fresult.size
+ ll_res = rffi.ptradd(ll_res, diff)
+ #
+ fresult.convert_from_object(ll_res, w_res)
+
+
+# ____________________________________________________________
+
+STDERR = 2
+
+def invoke_callback(ffi_cif, ll_res, ll_args, ll_userdata):
+ """ Callback specification.
+ ffi_cif - something ffi specific, don't care
+ ll_args - rffi.VOIDPP - pointer to array of pointers to args
+ ll_restype - rffi.VOIDP - pointer to result
+ ll_userdata - a special structure which holds necessary information
+ (what the real callback is for example), casted to VOIDP
+ """
+ e = cerrno.get_real_errno()
+ ll_res = rffi.cast(rffi.CCHARP, ll_res)
+ unique_id = rffi.cast(lltype.Signed, ll_userdata)
+ callback = global_callback_mapping.get(unique_id)
+ if callback is None:
+ # oups!
+ try:
+ os.write(STDERR, "SystemError: invoking a callback "
+ "that was already freed\n")
+ except OSError:
+ pass
+ # In this case, we don't even know how big ll_res is. Let's assume
+ # it is just a 'ffi_arg', and store 0 there.
+ misc._raw_memclear(ll_res, SIZE_OF_FFI_ARG)
+ return
+ #
+ ec = None
+ try:
+ ec = cerrno.get_errno_container(callback.space)
+ cerrno.save_errno_into(ec, e)
+ try:
+ callback.invoke(ll_args, ll_res)
+ except OperationError, e:
+ # got an app-level exception
+ callback.print_error(e)
+ callback.write_error_return_value(ll_res)
+ #
+ except Exception, e:
+ # oups! last-level attempt to recover.
+ try:
+ os.write(STDERR, "SystemError: callback raised ")
+ os.write(STDERR, str(e))
+ os.write(STDERR, "\n")
+ except OSError:
+ pass
+ callback.write_error_return_value(ll_res)
+ if ec is not None:
+ cerrno.restore_errno_from(ec)
diff --git a/pypy/module/_cffi_backend/cdataobj.py b/pypy/module/_cffi_backend/cdataobj.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/cdataobj.py
@@ -0,0 +1,309 @@
+import operator
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.interpreter.typedef import TypeDef, make_weakref_descr
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rlib.objectmodel import keepalive_until_here
+from pypy.rlib import objectmodel, rgc
+from pypy.tool.sourcetools import func_with_new_name
+
+from pypy.module._cffi_backend import misc
+
+
+class W_CData(Wrappable):
+ _attrs_ = ['space', '_cdata', 'ctype', '_lifeline_']
+ _immutable_fields_ = ['_cdata', 'ctype']
+ _cdata = lltype.nullptr(rffi.CCHARP.TO)
+
+ def __init__(self, space, cdata, ctype):
+ from pypy.module._cffi_backend import ctypeprim
+ assert lltype.typeOf(cdata) == rffi.CCHARP
+ assert isinstance(ctype, ctypeprim.W_CType)
+ self.space = space
+ self._cdata = cdata # don't forget keepalive_until_here!
+ self.ctype = ctype
+
+ def _repr_extra(self):
+ extra = self.ctype.extra_repr(self._cdata)
+ keepalive_until_here(self)
+ return extra
+
+ def _repr_extra_owning(self):
+ from pypy.module._cffi_backend.ctypeptr import W_CTypePointer
+ ctype = self.ctype
+ if isinstance(ctype, W_CTypePointer):
+ num_bytes = ctype.ctitem.size
+ else:
+ num_bytes = self._sizeof()
+ return 'owning %d bytes' % num_bytes
+
+ def repr(self):
+ extra2 = self._repr_extra()
+ extra1 = ''
+ if not isinstance(self, W_CDataNewOwning):
+ # it's slightly confusing to get "<cdata 'struct foo' 0x...>"
+ # because the struct foo is not owned. Trying to make it
+ # clearer, write in this case "<cdata 'struct foo &' 0x...>".
+ from pypy.module._cffi_backend import ctypestruct
+ if isinstance(self.ctype, ctypestruct.W_CTypeStructOrUnion):
+ extra1 = ' &'
+ return self.space.wrap("<cdata '%s%s' %s>" % (
+ self.ctype.name, extra1, extra2))
+
+ def nonzero(self):
+ return self.space.wrap(bool(self._cdata))
+
+ def int(self):
+ w_result = self.ctype.int(self._cdata)
+ keepalive_until_here(self)
+ return w_result
+
+ def long(self):
+ w_result = self.int()
+ space = self.space
+ if space.is_w(space.type(w_result), space.w_int):
+ w_result = space.newlong(space.int_w(w_result))
+ return w_result
+
+ def float(self):
+ w_result = self.ctype.float(self._cdata)
+ keepalive_until_here(self)
+ return w_result
+
+ def len(self):
+ from pypy.module._cffi_backend import ctypearray
+ space = self.space
+ if isinstance(self.ctype, ctypearray.W_CTypeArray):
+ return space.wrap(self.get_array_length())
+ raise operationerrfmt(space.w_TypeError,
+ "cdata of type '%s' has no len()",
+ self.ctype.name)
+
+ def _make_comparison(name):
+ op = getattr(operator, name)
+ requires_ordering = name not in ('eq', 'ne')
+ #
+ def _cmp(self, w_other):
+ from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitive
+ space = self.space
+ cdata1 = self._cdata
+ other = space.interpclass_w(w_other)
+ if isinstance(other, W_CData):
+ cdata2 = other._cdata
+ else:
+ return space.w_NotImplemented
+
+ if requires_ordering:
+ if (isinstance(self.ctype, W_CTypePrimitive) or
+ isinstance(other.ctype, W_CTypePrimitive)):
+ raise OperationError(space.w_TypeError,
+ space.wrap("cannot do comparison on a primitive cdata"))
+ cdata1 = rffi.cast(lltype.Unsigned, cdata1)
+ cdata2 = rffi.cast(lltype.Unsigned, cdata2)
+ return space.newbool(op(cdata1, cdata2))
+ #
+ return func_with_new_name(_cmp, name)
+
+ lt = _make_comparison('lt')
+ le = _make_comparison('le')
+ eq = _make_comparison('eq')
+ ne = _make_comparison('ne')
+ gt = _make_comparison('gt')
+ ge = _make_comparison('ge')
+
+ def hash(self):
+ h = (objectmodel.compute_identity_hash(self.ctype) ^
+ rffi.cast(lltype.Signed, self._cdata))
+ return self.space.wrap(h)
+
+ def getitem(self, w_index):
+ space = self.space
+ i = space.getindex_w(w_index, space.w_IndexError)
+ ctype = self.ctype._check_subscript_index(self, i)
+ w_o = self._do_getitem(ctype, i)
+ keepalive_until_here(self)
+ return w_o
+
+ def _do_getitem(self, ctype, i):
+ ctitem = ctype.ctitem
+ return ctitem.convert_to_object(
+ rffi.ptradd(self._cdata, i * ctitem.size))
+
+ def setitem(self, w_index, w_value):
+ space = self.space
+ i = space.getindex_w(w_index, space.w_IndexError)
+ ctype = self.ctype._check_subscript_index(self, i)
+ ctitem = ctype.ctitem
+ ctitem.convert_from_object(
+ rffi.ptradd(self._cdata, i * ctitem.size),
+ w_value)
+ keepalive_until_here(self)
+
+ def _add_or_sub(self, w_other, sign):
+ space = self.space
+ i = sign * space.getindex_w(w_other, space.w_OverflowError)
+ return self.ctype.add(self._cdata, i)
+
+ def add(self, w_other):
+ return self._add_or_sub(w_other, +1)
+
+ def sub(self, w_other):
+ space = self.space
+ ob = space.interpclass_w(w_other)
+ if isinstance(ob, W_CData):
+ from pypy.module._cffi_backend import ctypeptr, ctypearray
+ ct = ob.ctype
+ if isinstance(ct, ctypearray.W_CTypeArray):
+ ct = ct.ctptr
+ #
+ if (ct is not self.ctype or
+ not isinstance(ct, ctypeptr.W_CTypePointer) or
+ ct.ctitem.size <= 0):
+ raise operationerrfmt(space.w_TypeError,
+ "cannot subtract cdata '%s' and cdata '%s'",
+ self.ctype.name, ct.name)
+ #
+ diff = (rffi.cast(lltype.Signed, self._cdata) -
+ rffi.cast(lltype.Signed, ob._cdata)) // ct.ctitem.size
+ return space.wrap(diff)
+ #
+ return self._add_or_sub(w_other, -1)
+
+ def getcfield(self, w_attr):
+ return self.ctype.getcfield(self.space.str_w(w_attr))
+
+ def getattr(self, w_attr):
+ w_res = self.getcfield(w_attr).read(self._cdata)
+ keepalive_until_here(self)
+ return w_res
+
+ def setattr(self, w_attr, w_value):
+ self.getcfield(w_attr).write(self._cdata, w_value)
+ keepalive_until_here(self)
+
+ def call(self, args_w):
+ w_result = self.ctype.call(self._cdata, args_w)
+ keepalive_until_here(self)
+ return w_result
+
+ def iter(self):
+ return self.ctype.iter(self)
+
+ def write_raw_integer_data(self, source):
+ misc.write_raw_integer_data(self._cdata, source, self.ctype.size)
+ keepalive_until_here(self)
+
+ def write_raw_float_data(self, source):
+ misc.write_raw_float_data(self._cdata, source, self.ctype.size)
+ keepalive_until_here(self)
+
+ def convert_to_object(self):
+ w_obj = self.ctype.convert_to_object(self._cdata)
+ keepalive_until_here(self)
+ return w_obj
+
+ def get_array_length(self):
+ from pypy.module._cffi_backend import ctypearray
+ ctype = self.ctype
+ assert isinstance(ctype, ctypearray.W_CTypeArray)
+ length = ctype.length
+ assert length >= 0
+ return length
+
+ def _sizeof(self):
+ return self.ctype.size
+
+
+class W_CDataMem(W_CData):
+ """This is the base class used for cdata objects that own and free
+ their memory. Used directly by the results of cffi.cast('int', x)
+ or other primitive explicitly-casted types. It is further subclassed
+ by W_CDataNewOwning."""
+ _attrs_ = []
+
+ def __init__(self, space, size, ctype):
+ cdata = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw', zero=True)
+ W_CData.__init__(self, space, cdata, ctype)
+
+ @rgc.must_be_light_finalizer
+ def __del__(self):
+ lltype.free(self._cdata, flavor='raw')
+
+
+class W_CDataNewOwning(W_CDataMem):
+ """This is the class used for the cata objects created by newp()."""
+ _attrs_ = []
+
+ def _repr_extra(self):
+ return self._repr_extra_owning()
+
+
+class W_CDataNewOwningLength(W_CDataNewOwning):
+ """Subclass with an explicit length, for allocated instances of
+ the C type 'foo[]'."""
+ _attrs_ = ['length']
+ _immutable_fields_ = ['length']
+
+ def __init__(self, space, size, ctype, length):
+ W_CDataNewOwning.__init__(self, space, size, ctype)
+ self.length = length
+
+ def _sizeof(self):
+ from pypy.module._cffi_backend import ctypearray
+ ctype = self.ctype
+ assert isinstance(ctype, ctypearray.W_CTypeArray)
+ return self.length * ctype.ctitem.size
+
+ def get_array_length(self):
+ return self.length
+
+
+class W_CDataPtrToStructOrUnion(W_CData):
+ """This subclass is used for the pointer returned by new('struct foo').
+ It has a strong reference to a W_CDataNewOwning that really owns the
+ struct, which is the object returned by the app-level expression 'p[0]'.
+ But it is not itself owning any memory, although its repr says so;
+ it is merely a co-owner."""
+ _attrs_ = ['structobj']
+ _immutable_fields_ = ['structobj']
+
+ def __init__(self, space, cdata, ctype, structobj):
+ W_CData.__init__(self, space, cdata, ctype)
+ self.structobj = structobj
+
+ def _repr_extra(self):
+ return self._repr_extra_owning()
+
+ def _do_getitem(self, ctype, i):
+ assert i == 0
+ return self.structobj
+
+
+W_CData.typedef = TypeDef(
+ 'CData',
+ __module__ = '_cffi_backend',
+ __repr__ = interp2app(W_CData.repr),
+ __nonzero__ = interp2app(W_CData.nonzero),
+ __int__ = interp2app(W_CData.int),
+ __long__ = interp2app(W_CData.long),
+ __float__ = interp2app(W_CData.float),
+ __len__ = interp2app(W_CData.len),
+ __lt__ = interp2app(W_CData.lt),
+ __le__ = interp2app(W_CData.le),
+ __eq__ = interp2app(W_CData.eq),
+ __ne__ = interp2app(W_CData.ne),
+ __gt__ = interp2app(W_CData.gt),
+ __ge__ = interp2app(W_CData.ge),
+ __hash__ = interp2app(W_CData.hash),
+ __getitem__ = interp2app(W_CData.getitem),
+ __setitem__ = interp2app(W_CData.setitem),
+ __add__ = interp2app(W_CData.add),
+ __sub__ = interp2app(W_CData.sub),
+ __getattr__ = interp2app(W_CData.getattr),
+ __setattr__ = interp2app(W_CData.setattr),
+ __call__ = interp2app(W_CData.call),
+ __iter__ = interp2app(W_CData.iter),
+ __weakref__ = make_weakref_descr(W_CData),
+ )
+W_CData.typedef.acceptable_as_base_class = False
diff --git a/pypy/module/_cffi_backend/cerrno.py b/pypy/module/_cffi_backend/cerrno.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/cerrno.py
@@ -0,0 +1,29 @@
+from pypy.rlib import rposix
+from pypy.interpreter.executioncontext import ExecutionContext
+from pypy.interpreter.gateway import unwrap_spec
+
+
+ExecutionContext._cffi_saved_errno = 0
+
+
+def get_errno_container(space):
+ return space.getexecutioncontext()
+
+get_real_errno = rposix.get_errno
+
+
+def restore_errno_from(ec):
+ rposix.set_errno(ec._cffi_saved_errno)
+
+def save_errno_into(ec, errno):
+ ec._cffi_saved_errno = errno
+
+
+def get_errno(space):
+ ec = get_errno_container(space)
+ return space.wrap(ec._cffi_saved_errno)
+
+ at unwrap_spec(errno=int)
+def set_errno(space, errno):
+ ec = get_errno_container(space)
+ ec._cffi_saved_errno = errno
diff --git a/pypy/module/_cffi_backend/ctypearray.py b/pypy/module/_cffi_backend/ctypearray.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ctypearray.py
@@ -0,0 +1,128 @@
+"""
+Arrays.
+"""
+
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.gateway import interp2app
+from pypy.interpreter.typedef import TypeDef
+from pypy.rpython.lltypesystem import rffi
+from pypy.rlib.objectmodel import keepalive_until_here
+from pypy.rlib.rarithmetic import ovfcheck
+
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveChar
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveUniChar
+from pypy.module._cffi_backend.ctypeptr import W_CTypePtrOrArray
+from pypy.module._cffi_backend import cdataobj
+
+
+class W_CTypeArray(W_CTypePtrOrArray):
+ _attrs_ = ['ctptr']
+ _immutable_fields_ = ['ctptr']
+
+ def __init__(self, space, ctptr, length, arraysize, extra):
+ W_CTypePtrOrArray.__init__(self, space, arraysize, extra, 0,
+ ctptr.ctitem)
+ self.length = length
+ self.ctptr = ctptr
+
+ def _alignof(self):
+ return self.ctitem.alignof()
+
+ def newp(self, w_init):
+ space = self.space
+ datasize = self.size
+ #
+ if datasize < 0:
+ if (space.isinstance_w(w_init, space.w_list) or
+ space.isinstance_w(w_init, space.w_tuple)):
+ length = space.int_w(space.len(w_init))
+ elif space.isinstance_w(w_init, space.w_basestring):
+ # from a string, we add the null terminator
+ length = space.int_w(space.len(w_init)) + 1
+ else:
+ length = space.getindex_w(w_init, space.w_OverflowError)
+ if length < 0:
+ raise OperationError(space.w_ValueError,
+ space.wrap("negative array length"))
+ w_init = space.w_None
+ #
+ try:
+ datasize = ovfcheck(length * self.ctitem.size)
+ except OverflowError:
+ raise OperationError(space.w_OverflowError,
+ space.wrap("array size would overflow a ssize_t"))
+ #
+ cdata = cdataobj.W_CDataNewOwningLength(space, datasize,
+ self, length)
+ #
+ else:
+ cdata = cdataobj.W_CDataNewOwning(space, datasize, self)
+ #
+ if not space.is_w(w_init, space.w_None):
+ self.convert_from_object(cdata._cdata, w_init)
+ keepalive_until_here(cdata)
+ return cdata
+
+ def _check_subscript_index(self, w_cdata, i):
+ space = self.space
+ if i < 0:
+ raise OperationError(space.w_IndexError,
+ space.wrap("negative index not supported"))
+ if i >= w_cdata.get_array_length():
+ raise operationerrfmt(space.w_IndexError,
+ "index too large for cdata '%s' (expected %d < %d)",
+ self.name, i, w_cdata.get_array_length())
+ return self
+
+ def convert_from_object(self, cdata, w_ob):
+ self.convert_array_from_object(cdata, w_ob)
+
+ def convert_to_object(self, cdata):
+ if self.length < 0:
+ # we can't return a <cdata 'int[]'> here, because we don't
+ # know the length to give it. As a compromize, returns
+ # <cdata 'int *'> in this case.
+ self = self.ctptr
+ #
+ return cdataobj.W_CData(self.space, cdata, self)
+
+ def add(self, cdata, i):
+ p = rffi.ptradd(cdata, i * self.ctitem.size)
+ return cdataobj.W_CData(self.space, p, self.ctptr)
+
+ def iter(self, cdata):
+ return W_CDataIter(self.space, self.ctitem, cdata)
+
+ def get_vararg_type(self):
+ return self.ctptr
+
+
+class W_CDataIter(Wrappable):
+ _immutable_fields_ = ['ctitem', 'cdata', '_stop'] # but not '_next'
+
+ def __init__(self, space, ctitem, cdata):
+ self.space = space
+ self.ctitem = ctitem
+ self.cdata = cdata
+ length = cdata.get_array_length()
+ self._next = cdata._cdata
+ self._stop = rffi.ptradd(cdata._cdata, length * ctitem.size)
+
+ def iter_w(self):
+ return self.space.wrap(self)
+
+ def next_w(self):
+ result = self._next
+ if result == self._stop:
+ raise OperationError(self.space.w_StopIteration, self.space.w_None)
+ self._next = rffi.ptradd(result, self.ctitem.size)
+ return self.ctitem.convert_to_object(result)
+
+W_CDataIter.typedef = TypeDef(
+ 'CDataIter',
+ __module__ = '_cffi_backend',
+ __iter__ = interp2app(W_CDataIter.iter_w),
+ next = interp2app(W_CDataIter.next_w),
+ )
+W_CDataIter.typedef.acceptable_as_base_class = False
diff --git a/pypy/module/_cffi_backend/ctypeenum.py b/pypy/module/_cffi_backend/ctypeenum.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ctypeenum.py
@@ -0,0 +1,88 @@
+"""
+Enums.
+"""
+
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.rpython.lltypesystem import rffi
+from pypy.rlib.rarithmetic import intmask, r_ulonglong
+from pypy.rlib.objectmodel import keepalive_until_here
+
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveSigned
+from pypy.module._cffi_backend import misc
+
+
+class W_CTypeEnum(W_CTypePrimitiveSigned):
+ _attrs_ = ['enumerators2values', 'enumvalues2erators']
+ _immutable_fields_ = ['enumerators2values', 'enumvalues2erators']
+
+ def __init__(self, space, name, enumerators, enumvalues):
+ from pypy.module._cffi_backend.newtype import alignment
+ name = "enum " + name
+ size = rffi.sizeof(rffi.INT)
+ align = alignment(rffi.INT)
+ W_CTypePrimitiveSigned.__init__(self, space, size,
+ name, len(name), align)
+ self.enumerators2values = {} # str -> int
+ self.enumvalues2erators = {} # int -> str
+ for i in range(len(enumerators)-1, -1, -1):
+ self.enumerators2values[enumerators[i]] = enumvalues[i]
+ self.enumvalues2erators[enumvalues[i]] = enumerators[i]
+
+ def _getfields(self):
+ space = self.space
+ lst = []
+ for enumerator in self.enumerators2values:
+ enumvalue = self.enumerators2values[enumerator]
+ lst.append(space.newtuple([space.wrap(enumvalue),
+ space.wrap(enumerator)]))
+ w_lst = space.newlist(lst)
+ space.call_method(w_lst, 'sort')
+ return w_lst
+
+ def string(self, cdataobj, maxlen):
+ w_result = self.convert_to_object(cdataobj._cdata)
+ keepalive_until_here(cdataobj)
+ return w_result
+
+ def convert_to_object(self, cdata):
+ value = intmask(misc.read_raw_signed_data(cdata, self.size))
+ try:
+ enumerator = self.enumvalues2erators[value]
+ except KeyError:
+ enumerator = '#%d' % (value,)
+ return self.space.wrap(enumerator)
+
+ def convert_from_object(self, cdata, w_ob):
+ space = self.space
+ try:
+ return W_CTypePrimitiveSigned.convert_from_object(self, cdata,
+ w_ob)
+ except OperationError, e:
+ if not e.match(space, space.w_TypeError):
+ raise
+ if space.isinstance_w(w_ob, space.w_str):
+ value = self.convert_enum_string_to_int(space.str_w(w_ob))
+ value = r_ulonglong(value)
+ misc.write_raw_integer_data(cdata, value, self.size)
+ else:
+ raise self._convert_error("str or int", w_ob)
+
+ def cast_str(self, w_ob):
+ space = self.space
+ return self.convert_enum_string_to_int(space.str_w(w_ob))
+
+ def convert_enum_string_to_int(self, s):
+ space = self.space
+ if s.startswith('#'):
+ try:
+ return int(s[1:]) # xxx is it RPython?
+ except ValueError:
+ raise OperationError(space.w_ValueError,
+ space.wrap("invalid literal after '#'"))
+ else:
+ try:
+ return self.enumerators2values[s]
+ except KeyError:
+ raise operationerrfmt(space.w_ValueError,
+ "'%s' is not an enumerator for %s",
+ s, self.name)
diff --git a/pypy/module/_cffi_backend/ctypefunc.py b/pypy/module/_cffi_backend/ctypefunc.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ctypefunc.py
@@ -0,0 +1,415 @@
+"""
+Function pointers.
+"""
+
+import sys
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rlib import jit, clibffi, jit_libffi
+from pypy.rlib.jit_libffi import CIF_DESCRIPTION, CIF_DESCRIPTION_P
+from pypy.rlib.jit_libffi import FFI_TYPE, FFI_TYPE_P, FFI_TYPE_PP
+from pypy.rlib.jit_libffi import SIZE_OF_FFI_ARG
+from pypy.rlib.objectmodel import we_are_translated, instantiate
+from pypy.rlib.objectmodel import keepalive_until_here
+
+from pypy.module._cffi_backend.ctypeobj import W_CType
+from pypy.module._cffi_backend.ctypeptr import W_CTypePtrBase, W_CTypePointer
+from pypy.module._cffi_backend.ctypevoid import W_CTypeVoid
+from pypy.module._cffi_backend.ctypestruct import W_CTypeStruct
+from pypy.module._cffi_backend.ctypestruct import W_CTypeStructOrUnion
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveSigned
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveUnsigned
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveCharOrUniChar
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveFloat
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveLongDouble
+from pypy.module._cffi_backend import ctypearray, cdataobj, cerrno
+
+
+class W_CTypeFunc(W_CTypePtrBase):
+ _attrs_ = ['fargs', 'ellipsis', 'cif_descr']
+ _immutable_fields_ = ['fargs[*]', 'ellipsis', 'cif_descr']
+
+ def __init__(self, space, fargs, fresult, ellipsis):
+ extra = self._compute_extra_text(fargs, fresult, ellipsis)
+ size = rffi.sizeof(rffi.VOIDP)
+ W_CTypePtrBase.__init__(self, space, size, extra, 2, fresult,
+ could_cast_anything=False)
+ self.fargs = fargs
+ self.ellipsis = bool(ellipsis)
+ # fresult is stored in self.ctitem
+
+ if not ellipsis:
+ # Functions with '...' varargs are stored without a cif_descr
+ # at all. The cif is computed on every call from the actual
+ # types passed in. For all other functions, the cif_descr
+ # is computed here.
+ CifDescrBuilder(fargs, fresult).rawallocate(self)
+
+ def new_ctypefunc_completing_argtypes(self, args_w):
+ space = self.space
+ nargs_declared = len(self.fargs)
+ fvarargs = [None] * len(args_w)
+ fvarargs[:nargs_declared] = self.fargs
+ for i in range(nargs_declared, len(args_w)):
+ w_obj = args_w[i]
+ if isinstance(w_obj, cdataobj.W_CData):
+ ct = w_obj.ctype.get_vararg_type()
+ else:
+ raise operationerrfmt(space.w_TypeError,
+ "argument %d passed in the variadic part "
+ "needs to be a cdata object (got %s)",
+ i + 1, space.type(w_obj).getname(space))
+ fvarargs[i] = ct
+ ctypefunc = instantiate(W_CTypeFunc)
+ ctypefunc.space = space
+ ctypefunc.fargs = fvarargs
+ ctypefunc.ctitem = self.ctitem
+ CifDescrBuilder(fvarargs, self.ctitem).rawallocate(ctypefunc)
+ return ctypefunc
+
+ def __del__(self):
+ if self.cif_descr:
+ lltype.free(self.cif_descr, flavor='raw')
+
+ def _compute_extra_text(self, fargs, fresult, ellipsis):
+ argnames = ['(*)(']
+ for i, farg in enumerate(fargs):
+ if i > 0:
+ argnames.append(', ')
+ argnames.append(farg.name)
+ if ellipsis:
+ if len(fargs) > 0:
+ argnames.append(', ')
+ argnames.append('...')
+ argnames.append(')')
+ return ''.join(argnames)
+
+
+ def call(self, funcaddr, args_w):
+ if self.cif_descr:
+ # regular case: this function does not take '...' arguments
+ self = jit.promote(self)
+ nargs_declared = len(self.fargs)
+ if len(args_w) != nargs_declared:
+ space = self.space
+ raise operationerrfmt(space.w_TypeError,
+ "'%s' expects %d arguments, got %d",
+ self.name, nargs_declared, len(args_w))
+ return self._call(funcaddr, args_w)
+ else:
+ # call of a variadic function
+ return self.call_varargs(funcaddr, args_w)
+
+ @jit.dont_look_inside
+ def call_varargs(self, funcaddr, args_w):
+ nargs_declared = len(self.fargs)
+ if len(args_w) < nargs_declared:
+ space = self.space
+ raise operationerrfmt(space.w_TypeError,
+ "'%s' expects at least %d arguments, got %d",
+ self.name, nargs_declared, len(args_w))
+ completed = self.new_ctypefunc_completing_argtypes(args_w)
+ return completed._call(funcaddr, args_w)
+
+ # The following is the core of function calls. It is @unroll_safe,
+ # which means that the JIT is free to unroll the argument handling.
+ # But in case the function takes variable arguments, we don't unroll
+ # this (yet) for better safety: this is handled by @dont_look_inside
+ # in call_varargs.
+ @jit.unroll_safe
+ def _call(self, funcaddr, args_w):
+ space = self.space
+ cif_descr = self.cif_descr
+ size = cif_descr.exchange_size
+ mustfree_max_plus_1 = 0
+ buffer = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw')
+ try:
+ for i in range(len(args_w)):
+ data = rffi.ptradd(buffer, cif_descr.exchange_args[i])
+ w_obj = args_w[i]
+ argtype = self.fargs[i]
+ if argtype.convert_argument_from_object(data, w_obj):
+ # argtype is a pointer type, and w_obj a list/tuple/str
+ mustfree_max_plus_1 = i + 1
+
+ ec = cerrno.get_errno_container(space)
+ cerrno.restore_errno_from(ec)
+ jit_libffi.jit_ffi_call(cif_descr,
+ rffi.cast(rffi.VOIDP, funcaddr),
+ buffer)
+ e = cerrno.get_real_errno()
+ cerrno.save_errno_into(ec, e)
+
+ resultdata = rffi.ptradd(buffer, cif_descr.exchange_result)
+ w_res = self.ctitem.copy_and_convert_to_object(resultdata)
+ finally:
+ for i in range(mustfree_max_plus_1):
+ argtype = self.fargs[i]
+ if isinstance(argtype, W_CTypePointer):
+ data = rffi.ptradd(buffer, cif_descr.exchange_args[i])
+ if get_mustfree_flag(data):
+ raw_string = rffi.cast(rffi.CCHARPP, data)[0]
+ lltype.free(raw_string, flavor='raw')
+ lltype.free(buffer, flavor='raw')
+ return w_res
+
+def get_mustfree_flag(data):
+ return ord(rffi.ptradd(data, -1)[0])
+
+def set_mustfree_flag(data, flag):
+ rffi.ptradd(data, -1)[0] = chr(flag)
+
+def _get_abi(space, name):
+ abi = getattr(clibffi, name)
+ assert isinstance(abi, int)
+ return space.wrap(abi)
+
+# ____________________________________________________________
+
+
+W_CTypeFunc.cif_descr = lltype.nullptr(CIF_DESCRIPTION) # default value
+
+BIG_ENDIAN = sys.byteorder == 'big'
+
+
+# ----------
+# We attach to the classes small methods that return a 'ffi_type'
+def _missing_ffi_type(self, cifbuilder):
+ space = self.space
+ if self.size < 0:
+ raise operationerrfmt(space.w_TypeError,
+ "ctype '%s' has incomplete type",
+ self.name)
+ raise operationerrfmt(space.w_NotImplementedError,
+ "ctype '%s' (size %d) not supported as argument"
+ " or return value",
+ self.name, self.size)
+
+def _struct_ffi_type(self, cifbuilder):
+ if self.size >= 0:
+ return cifbuilder.fb_struct_ffi_type(self)
+ return _missing_ffi_type(self, cifbuilder)
+
+def _primsigned_ffi_type(self, cifbuilder):
+ size = self.size
+ if size == 1: return clibffi.ffi_type_sint8
+ elif size == 2: return clibffi.ffi_type_sint16
+ elif size == 4: return clibffi.ffi_type_sint32
+ elif size == 8: return clibffi.ffi_type_sint64
+ return _missing_ffi_type(self, cifbuilder)
+
+def _primunsigned_ffi_type(self, cifbuilder):
+ size = self.size
+ if size == 1: return clibffi.ffi_type_uint8
+ elif size == 2: return clibffi.ffi_type_uint16
+ elif size == 4: return clibffi.ffi_type_uint32
+ elif size == 8: return clibffi.ffi_type_uint64
+ return _missing_ffi_type(self, cifbuilder)
+
+def _primfloat_ffi_type(self, cifbuilder):
+ size = self.size
+ if size == 4: return clibffi.ffi_type_float
+ elif size == 8: return clibffi.ffi_type_double
+ return _missing_ffi_type(self, cifbuilder)
+
+def _primlongdouble_ffi_type(self, cifbuilder):
+ return clibffi.ffi_type_longdouble
+
+def _ptr_ffi_type(self, cifbuilder):
+ return clibffi.ffi_type_pointer
+
+def _void_ffi_type(self, cifbuilder):
+ return clibffi.ffi_type_void
+
+W_CType._get_ffi_type = _missing_ffi_type
+W_CTypeStruct._get_ffi_type = _struct_ffi_type
+W_CTypePrimitiveSigned._get_ffi_type = _primsigned_ffi_type
+W_CTypePrimitiveCharOrUniChar._get_ffi_type = _primunsigned_ffi_type
+W_CTypePrimitiveUnsigned._get_ffi_type = _primunsigned_ffi_type
+W_CTypePrimitiveFloat._get_ffi_type = _primfloat_ffi_type
+W_CTypePrimitiveLongDouble._get_ffi_type = _primlongdouble_ffi_type
+W_CTypePtrBase._get_ffi_type = _ptr_ffi_type
+W_CTypeVoid._get_ffi_type = _void_ffi_type
+# ----------
+
+
+class CifDescrBuilder(object):
+ rawmem = lltype.nullptr(rffi.CCHARP.TO)
+
+ def __init__(self, fargs, fresult):
+ self.fargs = fargs
+ self.fresult = fresult
+
+ def fb_alloc(self, size):
+ size = llmemory.raw_malloc_usage(size)
+ if not self.bufferp:
+ self.nb_bytes += size
+ return lltype.nullptr(rffi.CCHARP.TO)
+ else:
+ result = self.bufferp
+ self.bufferp = rffi.ptradd(result, size)
+ return result
+
+
+ def fb_fill_type(self, ctype):
+ return ctype._get_ffi_type(self)
+
+ def fb_struct_ffi_type(self, ctype):
+ # We can't pass a struct that was completed by verify().
+ # Issue: assume verify() is given "struct { long b; ...; }".
+ # Then it will complete it in the same way whether it is actually
+ # "struct { long a, b; }" or "struct { double a; long b; }".
+ # But on 64-bit UNIX, these two structs are passed by value
+ # differently: e.g. on x86-64, "b" ends up in register "rsi" in
+ # the first case and "rdi" in the second case.
+ space = self.space
+ if ctype.custom_field_pos:
+ raise OperationError(space.w_TypeError,
+ space.wrap(
+ "cannot pass as an argument a struct that was completed "
+ "with verify() (see pypy/module/_cffi_backend/ctypefunc.py "
+ "for details)"))
+
+ # allocate an array of (n + 1) ffi_types
+ n = len(ctype.fields_list)
+ elements = self.fb_alloc(rffi.sizeof(FFI_TYPE_P) * (n + 1))
+ elements = rffi.cast(FFI_TYPE_PP, elements)
+
+ # fill it with the ffi types of the fields
+ for i, cf in enumerate(ctype.fields_list):
+ if cf.is_bitfield():
+ raise OperationError(space.w_NotImplementedError,
+ space.wrap("cannot pass as argument a struct "
+ "with bit fields"))
+ ffi_subtype = self.fb_fill_type(cf.ctype)
+ if elements:
+ elements[i] = ffi_subtype
+
+ # zero-terminate the array
+ if elements:
+ elements[n] = lltype.nullptr(FFI_TYPE_P.TO)
+
+ # allocate and fill an ffi_type for the struct itself
+ ffistruct = self.fb_alloc(rffi.sizeof(FFI_TYPE))
+ ffistruct = rffi.cast(FFI_TYPE_P, ffistruct)
+ if ffistruct:
+ rffi.setintfield(ffistruct, 'c_size', ctype.size)
+ rffi.setintfield(ffistruct, 'c_alignment', ctype.alignof())
+ rffi.setintfield(ffistruct, 'c_type', clibffi.FFI_TYPE_STRUCT)
+ ffistruct.c_elements = elements
+
+ return ffistruct
+
+
+ def fb_build(self):
+ # Build a CIF_DESCRIPTION. Actually this computes the size and
+ # allocates a larger amount of data. It starts with a
+ # CIF_DESCRIPTION and continues with data needed for the CIF:
+ #
+ # - the argument types, as an array of 'ffi_type *'.
+ #
+ # - optionally, the result's and the arguments' ffi type data
+ # (this is used only for 'struct' ffi types; in other cases the
+ # 'ffi_type *' just points to static data like 'ffi_type_sint32').
+ #
+ nargs = len(self.fargs)
+
+ # start with a cif_description (cif and exchange_* fields)
+ self.fb_alloc(llmemory.sizeof(CIF_DESCRIPTION, nargs))
+
+ # next comes an array of 'ffi_type*', one per argument
+ atypes = self.fb_alloc(rffi.sizeof(FFI_TYPE_P) * nargs)
+ self.atypes = rffi.cast(FFI_TYPE_PP, atypes)
+
+ # next comes the result type data
+ self.rtype = self.fb_fill_type(self.fresult)
+
+ # next comes each argument's type data
+ for i, farg in enumerate(self.fargs):
+ atype = self.fb_fill_type(farg)
+ if self.atypes:
+ self.atypes[i] = atype
+
+
+ def align_arg(self, n):
+ return (n + 7) & ~7
+
+ def fb_build_exchange(self, cif_descr):
+ nargs = len(self.fargs)
+
+ # first, enough room for an array of 'nargs' pointers
+ exchange_offset = rffi.sizeof(rffi.CCHARP) * nargs
+ exchange_offset = self.align_arg(exchange_offset)
+ cif_descr.exchange_result = exchange_offset
+ cif_descr.exchange_result_libffi = exchange_offset
+
+ if BIG_ENDIAN and self.fresult.is_primitive_integer:
+ # For results of precisely these types, libffi has a
+ # strange rule that they will be returned as a whole
+ # 'ffi_arg' if they are smaller. The difference
+ # only matters on big-endian.
+ if self.fresult.size < SIZE_OF_FFI_ARG:
+ diff = SIZE_OF_FFI_ARG - self.fresult.size
+ cif_descr.exchange_result += diff
+
+ # then enough room for the result, rounded up to sizeof(ffi_arg)
+ exchange_offset += max(rffi.getintfield(self.rtype, 'c_size'),
+ SIZE_OF_FFI_ARG)
+
+ # loop over args
+ for i, farg in enumerate(self.fargs):
+ if isinstance(farg, W_CTypePointer):
+ exchange_offset += 1 # for the "must free" flag
+ exchange_offset = self.align_arg(exchange_offset)
+ cif_descr.exchange_args[i] = exchange_offset
+ exchange_offset += rffi.getintfield(self.atypes[i], 'c_size')
+
+ # store the exchange data size
+ cif_descr.exchange_size = exchange_offset
+
+ def fb_extra_fields(self, cif_descr):
+ cif_descr.abi = clibffi.FFI_DEFAULT_ABI # XXX
+ cif_descr.nargs = len(self.fargs)
+ cif_descr.rtype = self.rtype
+ cif_descr.atypes = self.atypes
+
+ @jit.dont_look_inside
+ def rawallocate(self, ctypefunc):
+ space = ctypefunc.space
+ self.space = space
+
+ # compute the total size needed in the CIF_DESCRIPTION buffer
+ self.nb_bytes = 0
+ self.bufferp = lltype.nullptr(rffi.CCHARP.TO)
+ self.fb_build()
+
+ # allocate the buffer
+ if we_are_translated():
+ rawmem = lltype.malloc(rffi.CCHARP.TO, self.nb_bytes,
+ flavor='raw')
+ rawmem = rffi.cast(CIF_DESCRIPTION_P, rawmem)
+ else:
+ # gross overestimation of the length below, but too bad
+ rawmem = lltype.malloc(CIF_DESCRIPTION_P.TO, self.nb_bytes,
+ flavor='raw')
+
+ # the buffer is automatically managed from the W_CTypeFunc instance
+ ctypefunc.cif_descr = rawmem
+
+ # call again fb_build() to really build the libffi data structures
+ self.bufferp = rffi.cast(rffi.CCHARP, rawmem)
+ self.fb_build()
+ assert self.bufferp == rffi.ptradd(rffi.cast(rffi.CCHARP, rawmem),
+ self.nb_bytes)
+
+ # fill in the 'exchange_*' fields
+ self.fb_build_exchange(rawmem)
+
+ # fill in the extra fields
+ self.fb_extra_fields(rawmem)
+
+ # call libffi's ffi_prep_cif() function
+ res = jit_libffi.jit_ffi_prep_cif(rawmem)
+ if res != clibffi.FFI_OK:
+ raise OperationError(space.w_SystemError,
+ space.wrap("libffi failed to build this function type"))
diff --git a/pypy/module/_cffi_backend/ctypeobj.py b/pypy/module/_cffi_backend/ctypeobj.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ctypeobj.py
@@ -0,0 +1,175 @@
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.gateway import interp2app
+from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.typedef import make_weakref_descr
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rlib.objectmodel import we_are_translated
+
+from pypy.module._cffi_backend import cdataobj
+
+
+class W_CType(Wrappable):
+ _attrs_ = ['space', 'size', 'name', 'name_position', '_lifeline_']
+ _immutable_fields_ = ['size?', 'name', 'name_position']
+ # note that 'size' is not strictly immutable, because it can change
+ # from -1 to the real value in the W_CTypeStruct subclass.
+
+ cast_anything = False
+ is_primitive_integer = False
+
+ def __init__(self, space, size, name, name_position):
+ self.space = space
+ self.size = size # size of instances, or -1 if unknown
+ self.name = name # the name of the C type as a string
+ self.name_position = name_position
+ # 'name_position' is the index in 'name' where it must be extended,
+ # e.g. with a '*' or a variable name.
+
+ def repr(self):
+ space = self.space
+ return space.wrap("<ctype '%s'>" % (self.name,))
+
+ def extra_repr(self, cdata):
+ if cdata:
+ return '0x%x' % rffi.cast(lltype.Unsigned, cdata)
+ else:
+ return 'NULL'
+
+ def is_char_ptr_or_array(self):
+ return False
+
+ def is_unichar_ptr_or_array(self):
+ return False
+
+ def newp(self, w_init):
+ space = self.space
+ raise operationerrfmt(space.w_TypeError,
+ "expected a pointer or array ctype, got '%s'",
+ self.name)
+
+ def cast(self, w_ob):
+ space = self.space
+ raise operationerrfmt(space.w_TypeError,
+ "cannot cast to '%s'", self.name)
+
+ def int(self, cdata):
+ space = self.space
+ raise operationerrfmt(space.w_TypeError,
+ "int() not supported on cdata '%s'", self.name)
+
+ def float(self, cdata):
+ space = self.space
+ raise operationerrfmt(space.w_TypeError,
+ "float() not supported on cdata '%s'", self.name)
+
+ def convert_to_object(self, cdata):
+ space = self.space
+ raise operationerrfmt(space.w_TypeError,
+ "cannot return a cdata '%s'", self.name)
+
+ def convert_from_object(self, cdata, w_ob):
+ space = self.space
+ raise operationerrfmt(space.w_TypeError,
+ "cannot initialize cdata '%s'", self.name)
+
+ def convert_argument_from_object(self, cdata, w_ob):
+ self.convert_from_object(cdata, w_ob)
+ return False
+
+ def _convert_error(self, expected, w_got):
+ space = self.space
+ ob = space.interpclass_w(w_got)
+ if isinstance(ob, cdataobj.W_CData):
+ return operationerrfmt(space.w_TypeError,
+ "initializer for ctype '%s' must be a %s, "
+ "not cdata '%s'", self.name, expected,
+ ob.ctype.name)
+ else:
+ return operationerrfmt(space.w_TypeError,
+ "initializer for ctype '%s' must be a %s, "
+ "not %s", self.name, expected,
+ space.type(w_got).getname(space))
+
+ def _check_subscript_index(self, w_cdata, i):
+ space = self.space
+ raise operationerrfmt(space.w_TypeError,
+ "cdata of type '%s' cannot be indexed",
+ self.name)
+
+ def string(self, cdataobj, maxlen):
+ space = self.space
+ raise operationerrfmt(space.w_TypeError,
+ "string(): unexpected cdata '%s' argument",
+ self.name)
+
+ def add(self, cdata, i):
+ space = self.space
+ raise operationerrfmt(space.w_TypeError,
+ "cannot add a cdata '%s' and a number",
+ self.name)
+
+ def insert_name(self, extra, extra_position):
+ name = '%s%s%s' % (self.name[:self.name_position],
+ extra,
+ self.name[self.name_position:])
+ name_position = self.name_position + extra_position
+ return name, name_position
+
+ def alignof(self):
+ align = self._alignof()
+ if not we_are_translated():
+ # obscure hack when untranslated, maybe, approximate, don't use
+ if isinstance(align, llmemory.FieldOffset):
+ align = rffi.sizeof(align.TYPE.y)
+ else:
+ # a different hack when translated, to avoid seeing constants
+ # of a symbolic integer type
+ align = llmemory.raw_malloc_usage(align)
+ return align
+
+ def _alignof(self):
+ space = self.space
+ raise operationerrfmt(space.w_TypeError,
+ "ctype '%s' is of unknown alignment",
+ self.name)
+
+ def offsetof(self, fieldname):
+ space = self.space
+ raise OperationError(space.w_TypeError,
+ space.wrap("not a struct or union ctype"))
+
+ def _getfields(self):
+ return None
+
+ def call(self, funcaddr, args_w):
+ space = self.space
+ raise operationerrfmt(space.w_TypeError,
+ "cdata '%s' is not callable", self.name)
+
+ def iter(self, cdata):
+ space = self.space
+ raise operationerrfmt(space.w_TypeError,
+ "cdata '%s' does not support iteration",
+ self.name)
+
+ def get_vararg_type(self):
+ return self
+
+ def getcfield(self, attr):
+ space = self.space
+ raise operationerrfmt(space.w_AttributeError,
+ "cdata '%s' has no attribute '%s'",
+ self.name, attr)
+
+ def copy_and_convert_to_object(self, cdata):
+ return self.convert_to_object(cdata)
+
+
+W_CType.typedef = TypeDef(
+ 'CTypeDescr',
+ __module__ = '_cffi_backend',
+ __repr__ = interp2app(W_CType.repr),
+ __weakref__ = make_weakref_descr(W_CType),
+ )
+W_CType.typedef.acceptable_as_base_class = False
diff --git a/pypy/module/_cffi_backend/ctypeprim.py b/pypy/module/_cffi_backend/ctypeprim.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ctypeprim.py
@@ -0,0 +1,332 @@
+"""
+Primitives.
+"""
+
+from pypy.interpreter.error import operationerrfmt
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rlib.rarithmetic import r_ulonglong
+from pypy.rlib.objectmodel import keepalive_until_here
+from pypy.rlib import jit
+
+from pypy.module._cffi_backend.ctypeobj import W_CType
+from pypy.module._cffi_backend import cdataobj, misc
+
+
+class W_CTypePrimitive(W_CType):
+ _attrs_ = ['align']
+ _immutable_fields_ = ['align']
+
+ def __init__(self, space, size, name, name_position, align):
+ W_CType.__init__(self, space, size, name, name_position)
+ self.align = align
+
+ def extra_repr(self, cdata):
+ w_ob = self.convert_to_object(cdata)
+ return self.space.str_w(self.space.repr(w_ob))
+
+ def _alignof(self):
+ return self.align
+
+ def cast_str(self, w_ob):
+ space = self.space
+ s = space.str_w(w_ob)
+ if len(s) != 1:
+ raise operationerrfmt(space.w_TypeError,
+ "cannot cast string of length %d to ctype '%s'",
+ len(s), self.name)
+ return ord(s[0])
+
+ def cast_unicode(self, w_ob):
+ space = self.space
+ s = space.unicode_w(w_ob)
+ if len(s) != 1:
+ raise operationerrfmt(space.w_TypeError,
+ "cannot cast unicode string of length %d to ctype '%s'",
+ len(s), self.name)
+ return ord(s[0])
+
+ def cast(self, w_ob):
+ from pypy.module._cffi_backend import ctypeptr
+ space = self.space
+ ob = space.interpclass_w(w_ob)
+ if (isinstance(ob, cdataobj.W_CData) and
+ isinstance(ob.ctype, ctypeptr.W_CTypePtrOrArray)):
+ value = rffi.cast(lltype.Signed, ob._cdata)
+ value = r_ulonglong(value)
+ elif space.isinstance_w(w_ob, space.w_str):
+ value = self.cast_str(w_ob)
+ value = r_ulonglong(value)
+ elif space.isinstance_w(w_ob, space.w_unicode):
+ value = self.cast_unicode(w_ob)
+ value = r_ulonglong(value)
+ else:
+ value = misc.as_unsigned_long_long(space, w_ob, strict=False)
+ w_cdata = cdataobj.W_CDataMem(space, self.size, self)
+ w_cdata.write_raw_integer_data(value)
+ return w_cdata
+
+ def _overflow(self, w_ob):
+ space = self.space
+ s = space.str_w(space.str(w_ob))
+ raise operationerrfmt(space.w_OverflowError,
+ "integer %s does not fit '%s'", s, self.name)
+
+ def string(self, cdataobj, maxlen):
+ if self.size == 1:
+ s = cdataobj._cdata[0]
+ keepalive_until_here(cdataobj)
+ return self.space.wrap(s)
+ return W_CType.string(self, cdataobj, maxlen)
+
+
+class W_CTypePrimitiveCharOrUniChar(W_CTypePrimitive):
+ _attrs_ = []
+ is_primitive_integer = True
+
+ def get_vararg_type(self):
+ from pypy.module._cffi_backend import newtype
+ return newtype.new_primitive_type(self.space, "int")
+
+
+class W_CTypePrimitiveChar(W_CTypePrimitiveCharOrUniChar):
+ _attrs_ = []
+ cast_anything = True
+
+ def int(self, cdata):
+ return self.space.wrap(ord(cdata[0]))
+
+ def convert_to_object(self, cdata):
+ return self.space.wrap(cdata[0])
+
+ def _convert_to_char(self, w_ob):
+ space = self.space
+ if space.isinstance_w(w_ob, space.w_str):
+ s = space.str_w(w_ob)
+ if len(s) == 1:
+ return s[0]
+ ob = space.interpclass_w(w_ob)
+ if (isinstance(ob, cdataobj.W_CData) and
+ isinstance(ob.ctype, W_CTypePrimitiveChar)):
+ return ob._cdata[0]
+ raise self._convert_error("string of length 1", w_ob)
+
+ def convert_from_object(self, cdata, w_ob):
+ value = self._convert_to_char(w_ob)
+ cdata[0] = value
+
+
+class W_CTypePrimitiveUniChar(W_CTypePrimitiveCharOrUniChar):
+ _attrs_ = []
+
+ def int(self, cdata):
+ unichardata = rffi.cast(rffi.CWCHARP, cdata)
+ return self.space.wrap(ord(unichardata[0]))
+
+ def convert_to_object(self, cdata):
+ unichardata = rffi.cast(rffi.CWCHARP, cdata)
+ s = rffi.wcharpsize2unicode(unichardata, 1)
+ return self.space.wrap(s)
+
+ def string(self, cdataobj, maxlen):
+ w_res = self.convert_to_object(cdataobj._cdata)
+ keepalive_until_here(cdataobj)
+ return w_res
+
+ def _convert_to_unichar(self, w_ob):
+ space = self.space
+ if space.isinstance_w(w_ob, space.w_unicode):
+ s = space.unicode_w(w_ob)
+ if len(s) == 1:
+ return s[0]
+ ob = space.interpclass_w(w_ob)
+ if (isinstance(ob, cdataobj.W_CData) and
+ isinstance(ob.ctype, W_CTypePrimitiveUniChar)):
+ return rffi.cast(rffi.CWCHARP, ob._cdata)[0]
+ raise self._convert_error("unicode string of length 1", w_ob)
+
+ def convert_from_object(self, cdata, w_ob):
+ value = self._convert_to_unichar(w_ob)
+ rffi.cast(rffi.CWCHARP, cdata)[0] = value
+
+
+class W_CTypePrimitiveSigned(W_CTypePrimitive):
+ _attrs_ = ['value_fits_long', 'vmin', 'vrangemax']
+ _immutable_fields_ = ['value_fits_long', 'vmin', 'vrangemax']
+ is_primitive_integer = True
+
+ def __init__(self, *args):
+ W_CTypePrimitive.__init__(self, *args)
+ self.value_fits_long = self.size <= rffi.sizeof(lltype.Signed)
+ if self.size < rffi.sizeof(lltype.SignedLongLong):
+ sh = self.size * 8
+ self.vmin = r_ulonglong(-1) << (sh - 1)
+ self.vrangemax = (r_ulonglong(1) << sh) - 1
+
+ def int(self, cdata):
+ if self.value_fits_long:
+ # this case is to handle enums, but also serves as a slight
+ # performance improvement for some other primitive types
+ value = misc.read_raw_long_data(cdata, self.size)
+ return self.space.wrap(value)
+ else:
+ return self.convert_to_object(cdata)
+
+ def convert_to_object(self, cdata):
+ if self.value_fits_long:
+ value = misc.read_raw_long_data(cdata, self.size)
+ return self.space.wrap(value)
+ else:
+ value = misc.read_raw_signed_data(cdata, self.size)
+ return self.space.wrap(value) # r_longlong => on 32-bit, 'long'
+
+ def convert_from_object(self, cdata, w_ob):
+ value = misc.as_long_long(self.space, w_ob)
+ if self.size < rffi.sizeof(lltype.SignedLongLong):
+ if r_ulonglong(value) - self.vmin > self.vrangemax:
+ self._overflow(w_ob)
+ value = r_ulonglong(value)
+ misc.write_raw_integer_data(cdata, value, self.size)
+
+ def get_vararg_type(self):
+ if self.size < rffi.sizeof(rffi.INT):
+ from pypy.module._cffi_backend import newtype
+ return newtype.new_primitive_type(self.space, "int")
+ return self
+
+
+class W_CTypePrimitiveUnsigned(W_CTypePrimitive):
+ _attrs_ = ['value_fits_long', 'vrangemax']
+ _immutable_fields_ = ['value_fits_long', 'vrangemax']
+ is_primitive_integer = True
+
+ def __init__(self, *args):
+ W_CTypePrimitive.__init__(self, *args)
+ self.value_fits_long = self.size < rffi.sizeof(lltype.Signed)
+ if self.size < rffi.sizeof(lltype.SignedLongLong):
+ sh = self.size * 8
+ self.vrangemax = (r_ulonglong(1) << sh) - 1
+
+ def int(self, cdata):
+ return self.convert_to_object(cdata)
+
+ def convert_from_object(self, cdata, w_ob):
+ value = misc.as_unsigned_long_long(self.space, w_ob, strict=True)
+ if self.size < rffi.sizeof(lltype.SignedLongLong):
+ if value > self.vrangemax:
+ self._overflow(w_ob)
+ misc.write_raw_integer_data(cdata, value, self.size)
+
+ def convert_to_object(self, cdata):
+ if self.value_fits_long:
+ value = misc.read_raw_ulong_data(cdata, self.size)
+ return self.space.wrap(value)
+ else:
+ value = misc.read_raw_unsigned_data(cdata, self.size)
+ return self.space.wrap(value) # r_ulonglong => 'long' object
+
+ def get_vararg_type(self):
+ if self.size < rffi.sizeof(rffi.INT):
+ from pypy.module._cffi_backend import newtype
+ return newtype.new_primitive_type(self.space, "int")
+ return self
+
+
+class W_CTypePrimitiveFloat(W_CTypePrimitive):
+ _attrs_ = []
+
+ def cast(self, w_ob):
+ space = self.space
+ ob = space.interpclass_w(w_ob)
+ if isinstance(ob, cdataobj.W_CData):
+ if not isinstance(ob.ctype, W_CTypePrimitive):
+ raise operationerrfmt(space.w_TypeError,
+ "cannot cast ctype '%s' to ctype '%s'",
+ ob.ctype.name, self.name)
+ w_ob = ob.convert_to_object()
+ #
+ if space.isinstance_w(w_ob, space.w_str):
+ value = self.cast_str(w_ob)
+ elif space.isinstance_w(w_ob, space.w_unicode):
+ value = self.cast_unicode(w_ob)
+ else:
+ value = space.float_w(w_ob)
+ w_cdata = cdataobj.W_CDataMem(space, self.size, self)
+ if not isinstance(self, W_CTypePrimitiveLongDouble):
+ w_cdata.write_raw_float_data(value)
+ else:
+ self._to_longdouble_and_write(value, w_cdata._cdata)
+ keepalive_until_here(w_cdata)
+ return w_cdata
+
+ def int(self, cdata):
+ w_value = self.float(cdata)
+ return self.space.int(w_value)
+
+ def float(self, cdata):
+ return self.convert_to_object(cdata)
+
+ def convert_to_object(self, cdata):
+ value = misc.read_raw_float_data(cdata, self.size)
+ return self.space.wrap(value)
+
+ def convert_from_object(self, cdata, w_ob):
+ space = self.space
+ value = space.float_w(space.float(w_ob))
+ misc.write_raw_float_data(cdata, value, self.size)
+
+
+class W_CTypePrimitiveLongDouble(W_CTypePrimitiveFloat):
+ _attrs_ = []
+
+ @jit.dont_look_inside
+ def extra_repr(self, cdata):
+ lvalue = misc.read_raw_longdouble_data(cdata)
+ return misc.longdouble2str(lvalue)
+
+ def cast(self, w_ob):
+ space = self.space
+ ob = space.interpclass_w(w_ob)
+ if (isinstance(ob, cdataobj.W_CData) and
+ isinstance(ob.ctype, W_CTypePrimitiveLongDouble)):
+ w_cdata = self.convert_to_object(ob._cdata)
+ keepalive_until_here(ob)
+ return w_cdata
+ else:
+ return W_CTypePrimitiveFloat.cast(self, w_ob)
+
+ @jit.dont_look_inside
+ def _to_longdouble_and_write(self, value, cdata):
+ lvalue = rffi.cast(rffi.LONGDOUBLE, value)
+ misc.write_raw_longdouble_data(cdata, lvalue)
+
+ @jit.dont_look_inside
+ def _read_from_longdouble(self, cdata):
+ lvalue = misc.read_raw_longdouble_data(cdata)
+ value = rffi.cast(lltype.Float, lvalue)
+ return value
+
+ @jit.dont_look_inside
+ def _copy_longdouble(self, cdatasrc, cdatadst):
+ lvalue = misc.read_raw_longdouble_data(cdatasrc)
+ misc.write_raw_longdouble_data(cdatadst, lvalue)
+
+ def float(self, cdata):
+ value = self._read_from_longdouble(cdata)
+ return self.space.wrap(value)
+
+ def convert_to_object(self, cdata):
+ w_cdata = cdataobj.W_CDataMem(self.space, self.size, self)
+ self._copy_longdouble(cdata, w_cdata._cdata)
+ keepalive_until_here(w_cdata)
+ return w_cdata
+
+ def convert_from_object(self, cdata, w_ob):
+ space = self.space
+ ob = space.interpclass_w(w_ob)
+ if (isinstance(ob, cdataobj.W_CData) and
+ isinstance(ob.ctype, W_CTypePrimitiveLongDouble)):
+ self._copy_longdouble(ob._cdata, cdata)
+ keepalive_until_here(ob)
+ else:
+ value = space.float_w(space.float(w_ob))
+ self._to_longdouble_and_write(value, cdata)
diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ctypeptr.py
@@ -0,0 +1,291 @@
+"""
+Pointers.
+"""
+
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rlib.objectmodel import keepalive_until_here
+from pypy.rlib.rarithmetic import ovfcheck
+
+from pypy.module._cffi_backend.ctypeobj import W_CType
+from pypy.module._cffi_backend import cdataobj, misc, ctypeprim
+
+
+class W_CTypePtrOrArray(W_CType):
+ _attrs_ = ['ctitem', 'can_cast_anything', 'is_struct_ptr',
+ 'length']
+ _immutable_fields_ = ['ctitem', 'can_cast_anything', 'is_struct_ptr',
+ 'length']
+ length = -1
+
+ def __init__(self, space, size, extra, extra_position, ctitem,
+ could_cast_anything=True):
+ from pypy.module._cffi_backend.ctypestruct import W_CTypeStructOrUnion
+ name, name_position = ctitem.insert_name(extra, extra_position)
+ W_CType.__init__(self, space, size, name, name_position)
+ # this is the "underlying type":
+ # - for pointers, it is the pointed-to type
+ # - for arrays, it is the array item type
+ # - for functions, it is the return type
+ self.ctitem = ctitem
+ self.can_cast_anything = could_cast_anything and ctitem.cast_anything
+ self.is_struct_ptr = isinstance(ctitem, W_CTypeStructOrUnion)
+
+ def is_char_ptr_or_array(self):
+ return isinstance(self.ctitem, ctypeprim.W_CTypePrimitiveChar)
+
+ def is_unichar_ptr_or_array(self):
+ return isinstance(self.ctitem, ctypeprim.W_CTypePrimitiveUniChar)
+
+ def is_char_or_unichar_ptr_or_array(self):
+ return isinstance(self.ctitem, ctypeprim.W_CTypePrimitiveCharOrUniChar)
+
+ def cast(self, w_ob):
+ # cast to a pointer, to a funcptr, or to an array.
+ # Note that casting to an array is an extension to the C language,
+ # which seems to be necessary in order to sanely get a
+ # <cdata 'int[3]'> at some address.
+ if self.size < 0:
+ return W_CType.cast(self, w_ob)
+ space = self.space
+ ob = space.interpclass_w(w_ob)
+ if (isinstance(ob, cdataobj.W_CData) and
+ isinstance(ob.ctype, W_CTypePtrOrArray)):
+ value = ob._cdata
+ else:
+ value = misc.as_unsigned_long_long(space, w_ob, strict=False)
+ value = rffi.cast(rffi.CCHARP, value)
+ return cdataobj.W_CData(space, value, self)
+
+ def convert_array_from_object(self, cdata, w_ob):
+ space = self.space
+ if (space.isinstance_w(w_ob, space.w_list) or
+ space.isinstance_w(w_ob, space.w_tuple)):
+ lst_w = space.listview(w_ob)
+ if self.length >= 0 and len(lst_w) > self.length:
+ raise operationerrfmt(space.w_IndexError,
+ "too many initializers for '%s' (got %d)",
+ self.name, len(lst_w))
+ ctitem = self.ctitem
+ for i in range(len(lst_w)):
+ ctitem.convert_from_object(cdata, lst_w[i])
+ cdata = rffi.ptradd(cdata, ctitem.size)
+ elif isinstance(self.ctitem, ctypeprim.W_CTypePrimitiveChar):
+ try:
+ s = space.str_w(w_ob)
+ except OperationError, e:
+ if not e.match(space, space.w_TypeError):
+ raise
+ raise self._convert_error("str or list or tuple", w_ob)
+ n = len(s)
+ if self.length >= 0 and n > self.length:
+ raise operationerrfmt(space.w_IndexError,
+ "initializer string is too long for '%s'"
+ " (got %d characters)",
+ self.name, n)
+ for i in range(n):
+ cdata[i] = s[i]
+ if n != self.length:
+ cdata[n] = '\x00'
+ elif isinstance(self.ctitem, ctypeprim.W_CTypePrimitiveUniChar):
+ try:
+ s = space.unicode_w(w_ob)
+ except OperationError, e:
+ if not e.match(space, space.w_TypeError):
+ raise
+ raise self._convert_error("unicode or list or tuple", w_ob)
+ n = len(s)
+ if self.length >= 0 and n > self.length:
+ raise operationerrfmt(space.w_IndexError,
+ "initializer unicode string is too long for '%s'"
+ " (got %d characters)",
+ self.name, n)
+ unichardata = rffi.cast(rffi.CWCHARP, cdata)
+ for i in range(n):
+ unichardata[i] = s[i]
+ if n != self.length:
+ unichardata[n] = u'\x00'
+ else:
+ raise self._convert_error("list or tuple", w_ob)
+
+ def string(self, cdataobj, maxlen):
+ space = self.space
+ if isinstance(self.ctitem, ctypeprim.W_CTypePrimitive):
+ cdata = cdataobj._cdata
+ if not cdata:
+ raise operationerrfmt(space.w_RuntimeError,
+ "cannot use string() on %s",
+ space.str_w(cdataobj.repr()))
+ #
+ from pypy.module._cffi_backend import ctypearray
+ length = maxlen
+ if length < 0 and isinstance(self, ctypearray.W_CTypeArray):
+ length = cdataobj.get_array_length()
+ #
+ # pointer to a primitive type of size 1: builds and returns a str
+ if self.ctitem.size == rffi.sizeof(lltype.Char):
+ if length < 0:
+ s = rffi.charp2str(cdata)
+ else:
+ s = rffi.charp2strn(cdata, length)
+ keepalive_until_here(cdataobj)
+ return space.wrap(s)
+ #
+ # pointer to a wchar_t: builds and returns a unicode
+ if self.is_unichar_ptr_or_array():
+ cdata = rffi.cast(rffi.CWCHARP, cdata)
+ if length < 0:
+ u = rffi.wcharp2unicode(cdata)
+ else:
+ u = rffi.wcharp2unicoden(cdata, length)
+ keepalive_until_here(cdataobj)
+ return space.wrap(u)
+ #
+ return W_CType.string(self, cdataobj, maxlen)
+
+
+class W_CTypePtrBase(W_CTypePtrOrArray):
+ # base class for both pointers and pointers-to-functions
+ _attrs_ = []
+
+ def convert_to_object(self, cdata):
+ ptrdata = rffi.cast(rffi.CCHARPP, cdata)[0]
+ return cdataobj.W_CData(self.space, ptrdata, self)
+
+ def convert_from_object(self, cdata, w_ob):
+ space = self.space
+ ob = space.interpclass_w(w_ob)
+ if not isinstance(ob, cdataobj.W_CData):
+ raise self._convert_error("compatible pointer", w_ob)
+ other = ob.ctype
+ if not isinstance(other, W_CTypePtrBase):
+ from pypy.module._cffi_backend import ctypearray
+ if isinstance(other, ctypearray.W_CTypeArray):
+ other = other.ctptr
+ else:
+ raise self._convert_error("compatible pointer", w_ob)
+ if self is not other:
+ if not (self.can_cast_anything or other.can_cast_anything):
+ raise self._convert_error("compatible pointer", w_ob)
+
+ rffi.cast(rffi.CCHARPP, cdata)[0] = ob._cdata
+
+ def _alignof(self):
+ from pypy.module._cffi_backend import newtype
+ return newtype.alignment_of_pointer
+
+
+class W_CTypePointer(W_CTypePtrBase):
+ _attrs_ = []
+
+ def __init__(self, space, ctitem):
+ from pypy.module._cffi_backend import ctypearray
+ size = rffi.sizeof(rffi.VOIDP)
+ if isinstance(ctitem, ctypearray.W_CTypeArray):
+ extra = "(*)" # obscure case: see test_array_add
+ else:
+ extra = " *"
+ W_CTypePtrBase.__init__(self, space, size, extra, 2, ctitem)
+
+ def newp(self, w_init):
+ space = self.space
+ ctitem = self.ctitem
+ datasize = ctitem.size
+ if datasize < 0:
+ raise operationerrfmt(space.w_TypeError,
+ "cannot instantiate ctype '%s' of unknown size",
+ self.name)
+ if self.is_struct_ptr:
+ # 'newp' on a struct-or-union pointer: in this case, we return
+ # a W_CDataPtrToStruct object which has a strong reference
+ # to a W_CDataNewOwning that really contains the structure.
+ cdatastruct = cdataobj.W_CDataNewOwning(space, datasize, ctitem)
+ cdata = cdataobj.W_CDataPtrToStructOrUnion(space,
+ cdatastruct._cdata,
+ self, cdatastruct)
+ else:
+ if self.is_char_or_unichar_ptr_or_array():
+ datasize *= 2 # forcefully add a null character
+ cdata = cdataobj.W_CDataNewOwning(space, datasize, self)
+ #
+ if not space.is_w(w_init, space.w_None):
+ ctitem.convert_from_object(cdata._cdata, w_init)
+ keepalive_until_here(cdata)
+ return cdata
+
+ def _check_subscript_index(self, w_cdata, i):
+ if (isinstance(w_cdata, cdataobj.W_CDataNewOwning) or
+ isinstance(w_cdata, cdataobj.W_CDataPtrToStructOrUnion)):
+ if i != 0:
+ space = self.space
+ raise operationerrfmt(space.w_IndexError,
+ "cdata '%s' can only be indexed by 0",
+ self.name)
+ return self
+
+ def add(self, cdata, i):
+ space = self.space
+ ctitem = self.ctitem
+ if ctitem.size < 0:
+ raise operationerrfmt(space.w_TypeError,
+ "ctype '%s' points to items of unknown size",
+ self.name)
+ p = rffi.ptradd(cdata, i * self.ctitem.size)
+ return cdataobj.W_CData(space, p, self)
+
+ def _prepare_pointer_call_argument(self, w_init):
+ space = self.space
+ if (space.isinstance_w(w_init, space.w_list) or
+ space.isinstance_w(w_init, space.w_tuple)):
+ length = space.int_w(space.len(w_init))
+ elif space.isinstance_w(w_init, space.w_basestring):
+ # from a string, we add the null terminator
+ length = space.int_w(space.len(w_init)) + 1
+ else:
+ return lltype.nullptr(rffi.CCHARP.TO)
+ if self.ctitem.size <= 0:
+ return lltype.nullptr(rffi.CCHARP.TO)
+ try:
+ datasize = ovfcheck(length * self.ctitem.size)
+ except OverflowError:
+ raise OperationError(space.w_OverflowError,
+ space.wrap("array size would overflow a ssize_t"))
+ result = lltype.malloc(rffi.CCHARP.TO, datasize,
+ flavor='raw', zero=True)
+ try:
+ self.convert_array_from_object(result, w_init)
+ except Exception:
+ lltype.free(result, flavor='raw')
+ raise
+ return result
+
+ def convert_argument_from_object(self, cdata, w_ob):
+ from pypy.module._cffi_backend.ctypefunc import set_mustfree_flag
+ space = self.space
+ ob = space.interpclass_w(w_ob)
+ if isinstance(ob, cdataobj.W_CData):
+ buffer = lltype.nullptr(rffi.CCHARP.TO)
+ else:
+ buffer = self._prepare_pointer_call_argument(w_ob)
+ #
+ if buffer:
+ rffi.cast(rffi.CCHARPP, cdata)[0] = buffer
+ set_mustfree_flag(cdata, True)
+ return True
+ else:
+ set_mustfree_flag(cdata, False)
+ try:
+ self.convert_from_object(cdata, w_ob)
+ except OperationError:
+ if (self.is_struct_ptr and isinstance(ob, cdataobj.W_CData)
+ and ob.ctype is self.ctitem):
+ # special case to make the life of verifier.py easier:
+ # if the formal argument type is 'struct foo *' but
+ # we pass a 'struct foo', then get a pointer to it
+ rffi.cast(rffi.CCHARPP, cdata)[0] = ob._cdata
+ else:
+ raise
+ return False
+
+ def getcfield(self, attr):
+ return self.ctitem.getcfield(attr)
diff --git a/pypy/module/_cffi_backend/ctypestruct.py b/pypy/module/_cffi_backend/ctypestruct.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ctypestruct.py
@@ -0,0 +1,247 @@
+"""
+Struct and unions.
+"""
+
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.rpython.lltypesystem import rffi
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.typedef import TypeDef, interp_attrproperty
+from pypy.rlib.objectmodel import keepalive_until_here
+from pypy.rlib.rarithmetic import r_ulonglong, r_longlong, intmask
+from pypy.rlib import jit
+
+from pypy.module._cffi_backend.ctypeobj import W_CType
+from pypy.module._cffi_backend import cdataobj, ctypeprim, misc
+
+
+class W_CTypeStructOrUnion(W_CType):
+ _immutable_fields_ = ['alignment?', 'fields_list?', 'fields_dict?',
+ 'custom_field_pos?']
+ # fields added by complete_struct_or_union():
+ alignment = -1
+ fields_list = None
+ fields_dict = None
+ custom_field_pos = False
+
+ def __init__(self, space, name):
+ name = '%s %s' % (self.kind, name)
+ W_CType.__init__(self, space, -1, name, len(name))
+
+ def check_complete(self):
+ if self.fields_dict is None:
+ space = self.space
+ raise operationerrfmt(space.w_TypeError,
+ "'%s' is not completed yet", self.name)
+
+ def _alignof(self):
+ self.check_complete()
+ return self.alignment
+
+ def _getfields(self):
+ if self.size < 0:
+ return None
+ space = self.space
+ result = [None] * len(self.fields_list)
+ for fname, field in self.fields_dict.iteritems():
+ i = self.fields_list.index(field)
+ result[i] = space.newtuple([space.wrap(fname),
+ space.wrap(field)])
+ return space.newlist(result)
+
+ def convert_to_object(self, cdata):
+ space = self.space
+ self.check_complete()
+ return cdataobj.W_CData(space, cdata, self)
+
+ def copy_and_convert_to_object(self, cdata):
+ space = self.space
+ self.check_complete()
+ ob = cdataobj.W_CDataNewOwning(space, self.size, self)
+ misc._raw_memcopy(cdata, ob._cdata, self.size)
+ keepalive_until_here(ob)
+ return ob
+
+ def offsetof(self, fieldname):
+ self.check_complete()
+ try:
+ cfield = self.fields_dict[fieldname]
+ except KeyError:
+ space = self.space
+ raise OperationError(space.w_KeyError, space.wrap(fieldname))
+ return cfield.offset
+
+ def _copy_from_same(self, cdata, w_ob):
+ space = self.space
+ ob = space.interpclass_w(w_ob)
+ if isinstance(ob, cdataobj.W_CData):
+ if ob.ctype is self and self.size >= 0:
+ misc._raw_memcopy(ob._cdata, cdata, self.size)
+ keepalive_until_here(ob)
+ return True
+ return False
+
+ def _check_only_one_argument_for_union(self, w_ob):
+ pass
+
+ def convert_from_object(self, cdata, w_ob):
+ space = self.space
+ if self._copy_from_same(cdata, w_ob):
+ return
+
+ self._check_only_one_argument_for_union(w_ob)
+
+ if (space.isinstance_w(w_ob, space.w_list) or
+ space.isinstance_w(w_ob, space.w_tuple)):
+ lst_w = space.listview(w_ob)
+ if len(lst_w) > len(self.fields_list):
+ raise operationerrfmt(space.w_ValueError,
+ "too many initializers for '%s' (got %d)",
+ self.name, len(lst_w))
+ for i in range(len(lst_w)):
+ self.fields_list[i].write(cdata, lst_w[i])
+
+ elif space.isinstance_w(w_ob, space.w_dict):
+ lst_w = space.fixedview(w_ob)
+ for i in range(len(lst_w)):
+ w_key = lst_w[i]
+ key = space.str_w(w_key)
+ try:
+ cf = self.fields_dict[key]
+ except KeyError:
+ space.raise_key_error(w_key)
+ assert 0
+ cf.write(cdata, space.getitem(w_ob, w_key))
+
+ else:
+ raise self._convert_error("list or tuple or dict or struct-cdata",
+ w_ob)
+
+ @jit.elidable
+ def _getcfield_const(self, attr):
+ return self.fields_dict[attr]
+
+ def getcfield(self, attr):
+ if self.fields_dict is not None:
+ self = jit.promote(self)
+ attr = jit.promote_string(attr)
+ try:
+ return self._getcfield_const(attr)
+ except KeyError:
+ pass
+ return W_CType.getcfield(self, attr)
+
+
+class W_CTypeStruct(W_CTypeStructOrUnion):
+ kind = "struct"
+
+class W_CTypeUnion(W_CTypeStructOrUnion):
+ kind = "union"
+
+ def _check_only_one_argument_for_union(self, w_ob):
+ space = self.space
+ n = space.int_w(space.len(w_ob))
+ if n > 1:
+ raise operationerrfmt(space.w_ValueError,
+ "initializer for '%s': %d items given, but "
+ "only one supported (use a dict if needed)",
+ self.name, n)
+
+
+class W_CField(Wrappable):
+ _immutable_ = True
+
+ BS_REGULAR = -1
+ BS_EMPTY_ARRAY = -2
+
+ def __init__(self, ctype, offset, bitshift, bitsize):
+ self.ctype = ctype
+ self.offset = offset
+ self.bitshift = bitshift # >= 0: bitshift; or BS_REGULAR/BS_EMPTY_ARRAY
+ self.bitsize = bitsize
+
+ def is_bitfield(self):
+ return self.bitshift >= 0
+
+ def read(self, cdata):
+ cdata = rffi.ptradd(cdata, self.offset)
+ if self.bitshift == self.BS_REGULAR:
+ return self.ctype.convert_to_object(cdata)
+ elif self.bitshift == self.BS_EMPTY_ARRAY:
+ from pypy.module._cffi_backend import ctypearray
+ ctype = self.ctype
+ assert isinstance(ctype, ctypearray.W_CTypeArray)
+ return cdataobj.W_CData(ctype.space, cdata, ctype.ctptr)
+ else:
+ return self.convert_bitfield_to_object(cdata)
+
+ def write(self, cdata, w_ob):
+ cdata = rffi.ptradd(cdata, self.offset)
+ if self.is_bitfield():
+ self.convert_bitfield_from_object(cdata, w_ob)
+ else:
+ self.ctype.convert_from_object(cdata, w_ob)
+
+ def convert_bitfield_to_object(self, cdata):
+ ctype = self.ctype
+ space = ctype.space
+ #
+ if isinstance(ctype, ctypeprim.W_CTypePrimitiveSigned):
+ value = r_ulonglong(misc.read_raw_signed_data(cdata, ctype.size))
+ valuemask = (r_ulonglong(1) << self.bitsize) - 1
+ shiftforsign = r_ulonglong(1) << (self.bitsize - 1)
+ value = ((value >> self.bitshift) + shiftforsign) & valuemask
+ result = r_longlong(value) - r_longlong(shiftforsign)
+ if ctype.value_fits_long:
+ return space.wrap(intmask(result))
+ else:
+ return space.wrap(result)
+ #
+ if isinstance(ctype, ctypeprim.W_CTypePrimitiveUnsigned):
+ value_fits_long = ctype.value_fits_long
+ elif isinstance(ctype, ctypeprim.W_CTypePrimitiveCharOrUniChar):
+ value_fits_long = True
+ else:
+ raise NotImplementedError
+ #
+ value = misc.read_raw_unsigned_data(cdata, ctype.size)
+ valuemask = (r_ulonglong(1) << self.bitsize) - 1
+ value = (value >> self.bitshift) & valuemask
+ if value_fits_long:
+ return space.wrap(intmask(value))
+ else:
+ return space.wrap(value)
+
+ def convert_bitfield_from_object(self, cdata, w_ob):
+ ctype = self.ctype
+ space = ctype.space
+ #
+ value = misc.as_long_long(space, w_ob)
+ if isinstance(ctype, ctypeprim.W_CTypePrimitiveSigned):
+ fmin = -(r_longlong(1) << (self.bitsize-1))
+ fmax = (r_longlong(1) << (self.bitsize-1)) - 1
+ if fmax == 0:
+ fmax = 1 # special case to let "int x:1" receive "1"
+ else:
+ fmin = r_longlong(0)
+ fmax = r_longlong((r_ulonglong(1) << self.bitsize) - 1)
+ if value < fmin or value > fmax:
+ raise operationerrfmt(space.w_OverflowError,
+ "value %d outside the range allowed by the "
+ "bit field width: %d <= x <= %d",
+ value, fmin, fmax)
+ rawmask = ((r_ulonglong(1) << self.bitsize) - 1) << self.bitshift
+ rawvalue = r_ulonglong(value) << self.bitshift
+ rawfielddata = misc.read_raw_unsigned_data(cdata, ctype.size)
+ rawfielddata = (rawfielddata & ~rawmask) | (rawvalue & rawmask)
+ misc.write_raw_integer_data(cdata, rawfielddata, ctype.size)
+
+
+W_CField.typedef = TypeDef(
+ 'CField',
+ __module__ = '_cffi_backend',
+ type = interp_attrproperty('ctype', W_CField),
+ offset = interp_attrproperty('offset', W_CField),
+ bitshift = interp_attrproperty('bitshift', W_CField),
+ bitsize = interp_attrproperty('bitsize', W_CField),
+ )
+W_CField.typedef.acceptable_as_base_class = False
diff --git a/pypy/module/_cffi_backend/ctypevoid.py b/pypy/module/_cffi_backend/ctypevoid.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ctypevoid.py
@@ -0,0 +1,16 @@
+"""
+Void.
+"""
+
+from pypy.module._cffi_backend.ctypeobj import W_CType
+
+
+class W_CTypeVoid(W_CType):
+ _attrs_ = []
+ cast_anything = True
+
+ def __init__(self, space):
+ W_CType.__init__(self, space, -1, "void", len("void"))
+
+ def copy_and_convert_to_object(self, cdata):
+ return self.space.w_None
diff --git a/pypy/module/_cffi_backend/func.py b/pypy/module/_cffi_backend/func.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/func.py
@@ -0,0 +1,77 @@
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.rpython.lltypesystem import lltype, rffi
+
+from pypy.module._cffi_backend import ctypeobj, cdataobj
+
+
+# ____________________________________________________________
+
+ at unwrap_spec(ctype=ctypeobj.W_CType)
+def newp(space, ctype, w_init=None):
+ return ctype.newp(w_init)
+
+# ____________________________________________________________
+
+ at unwrap_spec(ctype=ctypeobj.W_CType)
+def cast(space, ctype, w_ob):
+ return ctype.cast(w_ob)
+
+# ____________________________________________________________
+
+ at unwrap_spec(ctype=ctypeobj.W_CType)
+def callback(space, ctype, w_callable, w_error=None):
+ from pypy.module._cffi_backend.ccallback import W_CDataCallback
+ return W_CDataCallback(space, ctype, w_callable, w_error)
+
+# ____________________________________________________________
+
+ at unwrap_spec(cdata=cdataobj.W_CData)
+def typeof(space, cdata):
+ return cdata.ctype
+
+# ____________________________________________________________
+
+def sizeof(space, w_obj):
+ ob = space.interpclass_w(w_obj)
+ if isinstance(ob, cdataobj.W_CData):
+ size = ob._sizeof()
+ elif isinstance(ob, ctypeobj.W_CType):
+ size = ob.size
+ if size < 0:
+ raise operationerrfmt(space.w_ValueError,
+ "ctype '%s' is of unknown size",
+ ob.name)
+ else:
+ raise OperationError(space.w_TypeError,
+ space.wrap("expected a 'cdata' or 'ctype' object"))
+ return space.wrap(size)
+
+ at unwrap_spec(ctype=ctypeobj.W_CType)
+def alignof(space, ctype):
+ align = ctype.alignof()
+ return space.wrap(align)
+
+ at unwrap_spec(ctype=ctypeobj.W_CType, fieldname=str)
+def offsetof(space, ctype, fieldname):
+ ofs = ctype.offsetof(fieldname)
+ return space.wrap(ofs)
+
+ at unwrap_spec(ctype=ctypeobj.W_CType)
+def _getfields(space, ctype):
+ return ctype._getfields()
+
+# ____________________________________________________________
+
+ at unwrap_spec(ctype=ctypeobj.W_CType, replace_with=str)
+def getcname(space, ctype, replace_with):
+ p = ctype.name_position
+ s = '%s%s%s' % (ctype.name[:p], replace_with, ctype.name[p:])
+ return space.wrap(s)
+
+# ____________________________________________________________
+
+ at unwrap_spec(cdata=cdataobj.W_CData, maxlen=int)
+def string(space, cdata, maxlen=-1):
+ return cdata.ctype.string(cdata, maxlen)
diff --git a/pypy/module/_cffi_backend/libraryobj.py b/pypy/module/_cffi_backend/libraryobj.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/libraryobj.py
@@ -0,0 +1,106 @@
+from __future__ import with_statement
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.interpreter.typedef import TypeDef
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rlib.rdynload import DLLHANDLE, dlopen, dlsym, dlclose, DLOpenError
+from pypy.rlib.rdynload import RTLD_GLOBAL
+
+from pypy.module._cffi_backend.cdataobj import W_CData
+from pypy.module._cffi_backend.ctypeobj import W_CType
+
+
+class W_Library(Wrappable):
+ _immutable_ = True
+ handle = rffi.cast(DLLHANDLE, 0)
+
+ def __init__(self, space, filename, is_global):
+ self.space = space
+ if is_global and RTLD_GLOBAL is not None:
+ mode = RTLD_GLOBAL
+ else:
+ mode = -1 # default value, corresponds to RTLD_LOCAL
+ with rffi.scoped_str2charp(filename) as ll_libname:
+ if filename is None:
+ filename = "<None>"
+ try:
+ self.handle = dlopen(ll_libname, mode)
+ except DLOpenError, e:
+ raise operationerrfmt(space.w_OSError,
+ "cannot load '%s': %s",
+ filename, e.msg)
+ self.name = filename
+
+ def __del__(self):
+ h = self.handle
+ if h != rffi.cast(DLLHANDLE, 0):
+ self.handle = rffi.cast(DLLHANDLE, 0)
+ dlclose(h)
+
+ def repr(self):
+ space = self.space
+ return space.wrap("<clibrary '%s'>" % self.name)
+
+ @unwrap_spec(ctype=W_CType, name=str)
+ def load_function(self, ctype, name):
+ from pypy.module._cffi_backend import ctypefunc, ctypeptr, ctypevoid
+ space = self.space
+ #
+ ok = False
+ if isinstance(ctype, ctypefunc.W_CTypeFunc):
+ ok = True
+ if (isinstance(ctype, ctypeptr.W_CTypePointer) and
+ isinstance(ctype.ctitem, ctypevoid.W_CTypeVoid)):
+ ok = True
+ if not ok:
+ raise operationerrfmt(space.w_TypeError,
+ "function cdata expected, got '%s'",
+ ctype.name)
+ #
+ try:
+ cdata = dlsym(self.handle, name)
+ except KeyError:
+ raise operationerrfmt(space.w_KeyError,
+ "function '%s' not found in library '%s'",
+ name, self.name)
+ return W_CData(space, rffi.cast(rffi.CCHARP, cdata), ctype)
+
+ @unwrap_spec(ctype=W_CType, name=str)
+ def read_variable(self, ctype, name):
+ space = self.space
+ try:
+ cdata = dlsym(self.handle, name)
+ except KeyError:
+ raise operationerrfmt(space.w_KeyError,
+ "variable '%s' not found in library '%s'",
+ name, self.name)
+ return ctype.convert_to_object(rffi.cast(rffi.CCHARP, cdata))
+
+ @unwrap_spec(ctype=W_CType, name=str)
+ def write_variable(self, ctype, name, w_value):
+ space = self.space
+ try:
+ cdata = dlsym(self.handle, name)
+ except KeyError:
+ raise operationerrfmt(space.w_KeyError,
+ "variable '%s' not found in library '%s'",
+ name, self.name)
+ ctype.convert_from_object(rffi.cast(rffi.CCHARP, cdata), w_value)
+
+
+W_Library.typedef = TypeDef(
+ 'Library',
+ __module__ = '_cffi_backend',
+ __repr__ = interp2app(W_Library.repr),
+ load_function = interp2app(W_Library.load_function),
+ read_variable = interp2app(W_Library.read_variable),
+ write_variable = interp2app(W_Library.write_variable),
+ )
+W_Library.acceptable_as_base_class = False
+
+
+ at unwrap_spec(filename="str_or_None", is_global=int)
+def load_library(space, filename, is_global=0):
+ lib = W_Library(space, filename, is_global)
+ return space.wrap(lib)
diff --git a/pypy/module/_cffi_backend/misc.py b/pypy/module/_cffi_backend/misc.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/misc.py
@@ -0,0 +1,202 @@
+from __future__ import with_statement
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rlib.rarithmetic import r_ulonglong
+from pypy.rlib.unroll import unrolling_iterable
+from pypy.rlib import jit
+
+# ____________________________________________________________
+
+_prim_signed_types = unrolling_iterable([
+ (rffi.SIGNEDCHAR, rffi.SIGNEDCHARP),
+ (rffi.SHORT, rffi.SHORTP),
+ (rffi.INT, rffi.INTP),
+ (rffi.LONG, rffi.LONGP),
+ (rffi.LONGLONG, rffi.LONGLONGP)])
+
+_prim_unsigned_types = unrolling_iterable([
+ (rffi.UCHAR, rffi.UCHARP),
+ (rffi.USHORT, rffi.USHORTP),
+ (rffi.UINT, rffi.UINTP),
+ (rffi.ULONG, rffi.ULONGP),
+ (rffi.ULONGLONG, rffi.ULONGLONGP)])
+
+_prim_float_types = unrolling_iterable([
+ (rffi.FLOAT, rffi.FLOATP),
+ (rffi.DOUBLE, rffi.DOUBLEP)])
+
+def read_raw_signed_data(target, size):
+ for TP, TPP in _prim_signed_types:
+ if size == rffi.sizeof(TP):
+ return rffi.cast(lltype.SignedLongLong, rffi.cast(TPP, target)[0])
+ raise NotImplementedError("bad integer size")
+
+def read_raw_long_data(target, size):
+ for TP, TPP in _prim_signed_types:
+ if size == rffi.sizeof(TP):
+ assert rffi.sizeof(TP) <= rffi.sizeof(lltype.Signed)
+ return rffi.cast(lltype.Signed, rffi.cast(TPP, target)[0])
+ raise NotImplementedError("bad integer size")
+
+def read_raw_unsigned_data(target, size):
+ for TP, TPP in _prim_unsigned_types:
+ if size == rffi.sizeof(TP):
+ return rffi.cast(lltype.UnsignedLongLong, rffi.cast(TPP,target)[0])
+ raise NotImplementedError("bad integer size")
+
+def read_raw_ulong_data(target, size):
+ for TP, TPP in _prim_unsigned_types:
+ if size == rffi.sizeof(TP):
+ assert rffi.sizeof(TP) < rffi.sizeof(lltype.Signed)
+ return rffi.cast(lltype.Signed, rffi.cast(TPP,target)[0])
+ raise NotImplementedError("bad integer size")
+
+def read_raw_float_data(target, size):
+ for TP, TPP in _prim_float_types:
+ if size == rffi.sizeof(TP):
+ return rffi.cast(lltype.Float, rffi.cast(TPP, target)[0])
+ raise NotImplementedError("bad float size")
+
+def read_raw_longdouble_data(target):
+ return rffi.cast(rffi.LONGDOUBLEP, target)[0]
+
+def write_raw_integer_data(target, source, size):
+ for TP, TPP in _prim_unsigned_types:
+ if size == rffi.sizeof(TP):
+ rffi.cast(TPP, target)[0] = rffi.cast(TP, source)
+ return
+ raise NotImplementedError("bad integer size")
+
+def write_raw_float_data(target, source, size):
+ for TP, TPP in _prim_float_types:
+ if size == rffi.sizeof(TP):
+ rffi.cast(TPP, target)[0] = rffi.cast(TP, source)
+ return
+ raise NotImplementedError("bad float size")
+
+def write_raw_longdouble_data(target, source):
+ rffi.cast(rffi.LONGDOUBLEP, target)[0] = source
+
+# ____________________________________________________________
+
+sprintf_longdouble = rffi.llexternal(
+ "sprintf", [rffi.CCHARP, rffi.CCHARP, rffi.LONGDOUBLE], lltype.Void,
+ _nowrapper=True, sandboxsafe=True)
+
+FORMAT_LONGDOUBLE = rffi.str2charp("%LE")
+
+def longdouble2str(lvalue):
+ with lltype.scoped_alloc(rffi.CCHARP.TO, 128) as p: # big enough
+ sprintf_longdouble(p, FORMAT_LONGDOUBLE, lvalue)
+ return rffi.charp2str(p)
+
+# ____________________________________________________________
+
+
+UNSIGNED = 0x1000
+
+TYPES = [
+ ("int8_t", 1),
+ ("uint8_t", 1 | UNSIGNED),
+ ("int16_t", 2),
+ ("uint16_t", 2 | UNSIGNED),
+ ("int32_t", 4),
+ ("uint32_t", 4 | UNSIGNED),
+ ("int64_t", 8),
+ ("uint64_t", 8 | UNSIGNED),
+
+ ("intptr_t", rffi.sizeof(rffi.INTPTR_T)),
+ ("uintptr_t", rffi.sizeof(rffi.UINTPTR_T) | UNSIGNED),
+ ("ptrdiff_t", rffi.sizeof(rffi.INTPTR_T)), # XXX can it be different?
+ ("size_t", rffi.sizeof(rffi.SIZE_T) | UNSIGNED),
+ ("ssize_t", rffi.sizeof(rffi.SSIZE_T)),
+]
+
+
+def nonstandard_integer_types(space):
+ w_d = space.newdict()
+ for name, size in TYPES:
+ space.setitem(w_d, space.wrap(name), space.wrap(size))
+ return w_d
+
+# ____________________________________________________________
+
+def as_long_long(space, w_ob):
+ # (possibly) convert and cast a Python object to a long long.
+ # This version accepts a Python int too, and does convertions from
+ # other types of objects. It refuses floats.
+ if space.is_w(space.type(w_ob), space.w_int): # shortcut
+ return space.int_w(w_ob)
+ try:
+ bigint = space.bigint_w(w_ob)
+ except OperationError, e:
+ if not e.match(space, space.w_TypeError):
+ raise
+ if space.isinstance_w(w_ob, space.w_float):
+ raise
+ bigint = space.bigint_w(space.int(w_ob))
+ try:
+ return bigint.tolonglong()
+ except OverflowError:
+ raise OperationError(space.w_OverflowError, space.wrap(ovf_msg))
+
+def as_unsigned_long_long(space, w_ob, strict):
+ # (possibly) convert and cast a Python object to an unsigned long long.
+ # This accepts a Python int too, and does convertions from other types of
+ # objects. If 'strict', complains with OverflowError; if 'not strict',
+ # mask the result and round floats.
+ if space.is_w(space.type(w_ob), space.w_int): # shortcut
+ value = space.int_w(w_ob)
+ if strict and value < 0:
+ raise OperationError(space.w_OverflowError, space.wrap(neg_msg))
+ return r_ulonglong(value)
+ try:
+ bigint = space.bigint_w(w_ob)
+ except OperationError, e:
+ if not e.match(space, space.w_TypeError):
+ raise
+ if strict and space.isinstance_w(w_ob, space.w_float):
+ raise
+ bigint = space.bigint_w(space.int(w_ob))
+ if strict:
+ try:
+ return bigint.toulonglong()
+ except ValueError:
+ raise OperationError(space.w_OverflowError, space.wrap(neg_msg))
+ except OverflowError:
+ raise OperationError(space.w_OverflowError, space.wrap(ovf_msg))
+ else:
+ return bigint.ulonglongmask()
+
+neg_msg = "can't convert negative number to unsigned"
+ovf_msg = "long too big to convert"
+
+# ____________________________________________________________
+
+def _raw_memcopy(source, dest, size):
+ if jit.isconstant(size):
+ # for the JIT: first handle the case where 'size' is known to be
+ # a constant equal to 1, 2, 4, 8
+ for TP, TPP in _prim_unsigned_types:
+ if size == rffi.sizeof(TP):
+ rffi.cast(TPP, dest)[0] = rffi.cast(TPP, source)[0]
+ return
+ _raw_memcopy_opaque(source, dest, size)
+
+ at jit.dont_look_inside
+def _raw_memcopy_opaque(source, dest, size):
+ # push push push at the llmemory interface (with hacks that are all
+ # removed after translation)
+ zero = llmemory.itemoffsetof(rffi.CCHARP.TO, 0)
+ llmemory.raw_memcopy(
+ llmemory.cast_ptr_to_adr(source) + zero,
+ llmemory.cast_ptr_to_adr(dest) + zero,
+ size * llmemory.sizeof(lltype.Char))
+
+def _raw_memclear(dest, size):
+ # for now, only supports the cases of size = 1, 2, 4, 8
+ for TP, TPP in _prim_unsigned_types:
+ if size == rffi.sizeof(TP):
+ rffi.cast(TPP, dest)[0] = rffi.cast(TP, 0)
+ return
+ raise NotImplementedError("bad clear size")
diff --git a/pypy/module/_cffi_backend/newtype.py b/pypy/module/_cffi_backend/newtype.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/newtype.py
@@ -0,0 +1,258 @@
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.gateway import unwrap_spec
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rlib.rarithmetic import ovfcheck
+from pypy.rlib.objectmodel import specialize
+
+from pypy.module._cffi_backend import ctypeobj, ctypeprim, ctypeptr, ctypearray
+from pypy.module._cffi_backend import ctypestruct, ctypevoid, ctypeenum
+
+
+ at specialize.memo()
+def alignment(TYPE):
+ S = lltype.Struct('aligncheck', ('x', lltype.Char), ('y', TYPE))
+ return rffi.offsetof(S, 'y')
+
+alignment_of_pointer = alignment(rffi.CCHARP)
+
+# ____________________________________________________________
+
+
+PRIMITIVE_TYPES = {}
+
+def eptype(name, TYPE, ctypecls):
+ PRIMITIVE_TYPES[name] = ctypecls, rffi.sizeof(TYPE), alignment(TYPE)
+
+eptype("char", lltype.Char, ctypeprim.W_CTypePrimitiveChar)
+eptype("wchar_t", lltype.UniChar, ctypeprim.W_CTypePrimitiveUniChar)
+eptype("signed char", rffi.SIGNEDCHAR, ctypeprim.W_CTypePrimitiveSigned)
+eptype("short", rffi.SHORT, ctypeprim.W_CTypePrimitiveSigned)
+eptype("int", rffi.INT, ctypeprim.W_CTypePrimitiveSigned)
+eptype("long", rffi.LONG, ctypeprim.W_CTypePrimitiveSigned)
+eptype("long long", rffi.LONGLONG, ctypeprim.W_CTypePrimitiveSigned)
+eptype("unsigned char", rffi.UCHAR, ctypeprim.W_CTypePrimitiveUnsigned)
+eptype("unsigned short", rffi.SHORT, ctypeprim.W_CTypePrimitiveUnsigned)
+eptype("unsigned int", rffi.INT, ctypeprim.W_CTypePrimitiveUnsigned)
+eptype("unsigned long", rffi.LONG, ctypeprim.W_CTypePrimitiveUnsigned)
+eptype("unsigned long long", rffi.LONGLONG, ctypeprim.W_CTypePrimitiveUnsigned)
+eptype("float", rffi.FLOAT, ctypeprim.W_CTypePrimitiveFloat)
+eptype("double", rffi.DOUBLE, ctypeprim.W_CTypePrimitiveFloat)
+eptype("long double", rffi.LONGDOUBLE, ctypeprim.W_CTypePrimitiveLongDouble)
+
+ at unwrap_spec(name=str)
+def new_primitive_type(space, name):
+ try:
+ ctypecls, size, align = PRIMITIVE_TYPES[name]
+ except KeyError:
+ raise OperationError(space.w_KeyError, space.wrap(name))
+ ctype = ctypecls(space, size, name, len(name), align)
+ return ctype
+
+# ____________________________________________________________
+
+ at unwrap_spec(ctype=ctypeobj.W_CType)
+def new_pointer_type(space, ctype):
+ ctypepointer = ctypeptr.W_CTypePointer(space, ctype)
+ return ctypepointer
+
+# ____________________________________________________________
+
+ at unwrap_spec(ctptr=ctypeobj.W_CType)
+def new_array_type(space, ctptr, w_length):
+ if not isinstance(ctptr, ctypeptr.W_CTypePointer):
+ raise OperationError(space.w_TypeError,
+ space.wrap("first arg must be a pointer ctype"))
+ ctitem = ctptr.ctitem
+ if ctitem.size < 0:
+ raise operationerrfmt(space.w_ValueError,
+ "array item of unknown size: '%s'",
+ ctitem.name)
+ if space.is_w(w_length, space.w_None):
+ length = -1
+ arraysize = -1
+ extra = '[]'
+ else:
+ length = space.getindex_w(w_length, space.w_OverflowError)
+ if length < 0:
+ raise OperationError(space.w_ValueError,
+ space.wrap("negative array length"))
+ try:
+ arraysize = ovfcheck(length * ctitem.size)
+ except OverflowError:
+ raise OperationError(space.w_OverflowError,
+ space.wrap("array size would overflow a ssize_t"))
+ extra = '[%d]' % length
+ #
+ ctype = ctypearray.W_CTypeArray(space, ctptr, length, arraysize, extra)
+ return ctype
+
+# ____________________________________________________________
+
+ at unwrap_spec(name=str)
+def new_struct_type(space, name):
+ return ctypestruct.W_CTypeStruct(space, name)
+
+ at unwrap_spec(name=str)
+def new_union_type(space, name):
+ return ctypestruct.W_CTypeUnion(space, name)
+
+ at unwrap_spec(ctype=ctypeobj.W_CType, totalsize=int, totalalignment=int)
+def complete_struct_or_union(space, ctype, w_fields, w_ignored=None,
+ totalsize=-1, totalalignment=-1):
+ if (not isinstance(ctype, ctypestruct.W_CTypeStructOrUnion)
+ or ctype.size >= 0):
+ raise OperationError(space.w_TypeError,
+ space.wrap("first arg must be a non-initialized"
+ " struct or union ctype"))
+
+ is_union = isinstance(ctype, ctypestruct.W_CTypeUnion)
+ maxsize = 1
+ alignment = 1
+ offset = 0
+ fields_w = space.listview(w_fields)
+ fields_list = []
+ fields_dict = {}
+ prev_bit_position = 0
+ custom_field_pos = False
+
+ for w_field in fields_w:
+ field_w = space.fixedview(w_field)
+ if not (2 <= len(field_w) <= 4):
+ raise OperationError(space.w_TypeError,
+ space.wrap("bad field descr"))
+ fname = space.str_w(field_w[0])
+ ftype = space.interp_w(ctypeobj.W_CType, field_w[1])
+ fbitsize = -1
+ foffset = -1
+ if len(field_w) > 2: fbitsize = space.int_w(field_w[2])
+ if len(field_w) > 3: foffset = space.int_w(field_w[3])
+ #
+ if fname in fields_dict:
+ raise operationerrfmt(space.w_KeyError,
+ "duplicate field name '%s'", fname)
+ #
+ if ftype.size < 0:
+ raise operationerrfmt(space.w_TypeError,
+ "field '%s.%s' has ctype '%s' of unknown size",
+ ctype.name, fname, ftype.name)
+ #
+ falign = ftype.alignof()
+ if alignment < falign:
+ alignment = falign
+ #
+ if foffset < 0:
+ # align this field to its own 'falign' by inserting padding
+ offset = (offset + falign - 1) & ~(falign-1)
+ else:
+ # a forced field position: ignore the offset just computed,
+ # except to know if we must set 'custom_field_pos'
+ custom_field_pos |= (offset != foffset)
+ offset = foffset
+ #
+ if fbitsize < 0 or (
+ fbitsize == 8 * ftype.size and not
+ isinstance(ftype, ctypeprim.W_CTypePrimitiveCharOrUniChar)):
+ fbitsize = -1
+ if isinstance(ftype, ctypearray.W_CTypeArray) and ftype.length==0:
+ bitshift = ctypestruct.W_CField.BS_EMPTY_ARRAY
+ else:
+ bitshift = ctypestruct.W_CField.BS_REGULAR
+ prev_bit_position = 0
+ else:
+ if (not (isinstance(ftype, ctypeprim.W_CTypePrimitiveSigned) or
+ isinstance(ftype, ctypeprim.W_CTypePrimitiveUnsigned) or
+ isinstance(ftype, ctypeprim.W_CTypePrimitiveChar)) or
+ fbitsize == 0 or
+ fbitsize > 8 * ftype.size):
+ raise operationerrfmt(space.w_TypeError,
+ "invalid bit field '%s'", fname)
+ if prev_bit_position > 0:
+ prev_field = fields_list[-1]
+ assert prev_field.bitshift >= 0
+ if prev_field.ctype.size != ftype.size:
+ raise OperationError(space.w_NotImplementedError,
+ space.wrap("consecutive bit fields should be "
+ "declared with a same-sized type"))
+ if prev_bit_position + fbitsize > 8 * ftype.size:
+ prev_bit_position = 0
+ else:
+ # we can share the same field as 'prev_field'
+ offset = prev_field.offset
+ bitshift = prev_bit_position
+ if not is_union:
+ prev_bit_position += fbitsize
+ #
+ fld = ctypestruct.W_CField(ftype, offset, bitshift, fbitsize)
+ fields_list.append(fld)
+ fields_dict[fname] = fld
+ #
+ if maxsize < ftype.size:
+ maxsize = ftype.size
+ if not is_union:
+ offset += ftype.size
+
+ if is_union:
+ assert offset == 0
+ offset = maxsize
+ else:
+ if offset == 0:
+ offset = 1
+ offset = (offset + alignment - 1) & ~(alignment-1)
+
+ if totalsize < 0:
+ totalsize = offset
+ elif totalsize < offset:
+ raise operationerrfmt(space.w_TypeError,
+ "%s cannot be of size %d: there are fields at least "
+ "up to %d", ctype.name, totalsize, offset)
+ if totalalignment < 0:
+ totalalignment = alignment
+
+ ctype.size = totalsize
+ ctype.alignment = totalalignment
+ ctype.fields_list = fields_list
+ ctype.fields_dict = fields_dict
+ ctype.custom_field_pos = custom_field_pos
+
+# ____________________________________________________________
+
+def new_void_type(space):
+ ctype = ctypevoid.W_CTypeVoid(space)
+ return ctype
+
+# ____________________________________________________________
+
+ at unwrap_spec(name=str)
+def new_enum_type(space, name, w_enumerators, w_enumvalues):
+ enumerators_w = space.fixedview(w_enumerators)
+ enumvalues_w = space.fixedview(w_enumvalues)
+ if len(enumerators_w) != len(enumvalues_w):
+ raise OperationError(space.w_ValueError,
+ space.wrap("tuple args must have the same size"))
+ enumerators = [space.str_w(w) for w in enumerators_w]
+ enumvalues = [space.int_w(w) for w in enumvalues_w]
+ ctype = ctypeenum.W_CTypeEnum(space, name, enumerators, enumvalues)
+ return ctype
+
+# ____________________________________________________________
+
+ at unwrap_spec(fresult=ctypeobj.W_CType, ellipsis=int)
+def new_function_type(space, w_fargs, fresult, ellipsis=0):
+ from pypy.module._cffi_backend import ctypefunc
+ fargs = []
+ for w_farg in space.fixedview(w_fargs):
+ farg = space.interpclass_w(w_farg)
+ if not isinstance(farg, ctypeobj.W_CType):
+ raise OperationError(space.w_TypeError,
+ space.wrap("first arg must be a tuple of ctype objects"))
+ if isinstance(farg, ctypearray.W_CTypeArray):
+ farg = farg.ctptr
+ fargs.append(farg)
+ #
+ if ((fresult.size < 0 and not isinstance(fresult, ctypevoid.W_CTypeVoid))
+ or isinstance(fresult, ctypearray.W_CTypeArray)):
+ raise operationerrfmt(space.w_TypeError,
+ "invalid result type: '%s'", fresult.name)
+ #
+ fct = ctypefunc.W_CTypeFunc(space, fargs, fresult, ellipsis)
+ return fct
diff --git a/pypy/module/_cffi_backend/test/__init__.py b/pypy/module/_cffi_backend/test/__init__.py
new file mode 100644
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -0,0 +1,1953 @@
+# ____________________________________________________________
+
+import sys
+if sys.version_info < (3,):
+ type_or_class = "type"
+ mandatory_b_prefix = ''
+ mandatory_u_prefix = 'u'
+ readbuf = str
+ bufchar = lambda x: x
+ bytechr = chr
+else:
+ type_or_class = "class"
+ long = int
+ unicode = str
+ unichr = chr
+ mandatory_b_prefix = 'b'
+ mandatory_u_prefix = ''
+ readbuf = lambda buf: buf.tobytes()
+ bufchar = ord
+ bytechr = lambda n: bytes([n])
+
+def size_of_int():
+ BInt = new_primitive_type("int")
+ return sizeof(BInt)
+
+def size_of_long():
+ BLong = new_primitive_type("long")
+ return sizeof(BLong)
+
+def size_of_ptr():
+ BInt = new_primitive_type("int")
+ BPtr = new_pointer_type(BInt)
+ return sizeof(BPtr)
+
+
+def find_and_load_library(name, is_global=0):
+ import ctypes.util
+ if name is None:
+ path = None
+ else:
+ path = ctypes.util.find_library(name)
+ return load_library(path, is_global)
+
+def test_load_library():
+ x = find_and_load_library('c')
+ assert repr(x).startswith("<clibrary '")
+ x = find_and_load_library('c', 1)
+ assert repr(x).startswith("<clibrary '")
+
+def test_nonstandard_integer_types():
+ d = nonstandard_integer_types()
+ assert type(d) is dict
+ assert 'char' not in d
+ assert d['size_t'] in (0x1004, 0x1008)
+ assert d['size_t'] == d['ssize_t'] + 0x1000
+
+def test_new_primitive_type():
+ py.test.raises(KeyError, new_primitive_type, "foo")
+ p = new_primitive_type("signed char")
+ assert repr(p) == "<ctype 'signed char'>"
+
+def test_cast_to_signed_char():
+ p = new_primitive_type("signed char")
+ x = cast(p, -65 + 17*256)
+ assert repr(x) == "<cdata 'signed char' -65>"
+ assert repr(type(x)) == "<%s '_cffi_backend.CData'>" % type_or_class
+ assert int(x) == -65
+ x = cast(p, -66 + (1<<199)*256)
+ assert repr(x) == "<cdata 'signed char' -66>"
+ assert int(x) == -66
+ assert (x == cast(p, -66)) is False
+ assert (x != cast(p, -66)) is True
+ q = new_primitive_type("short")
+ assert (x == cast(q, -66)) is False
+ assert (x != cast(q, -66)) is True
+
+def test_sizeof_type():
+ py.test.raises(TypeError, sizeof, 42.5)
+ p = new_primitive_type("short")
+ assert sizeof(p) == 2
+
+def test_integer_types():
+ for name in ['signed char', 'short', 'int', 'long', 'long long']:
+ p = new_primitive_type(name)
+ size = sizeof(p)
+ min = -(1 << (8*size-1))
+ max = (1 << (8*size-1)) - 1
+ assert int(cast(p, min)) == min
+ assert int(cast(p, max)) == max
+ assert int(cast(p, min - 1)) == max
+ assert int(cast(p, max + 1)) == min
+ py.test.raises(TypeError, cast, p, None)
+ assert long(cast(p, min - 1)) == max
+ assert int(cast(p, b'\x08')) == 8
+ assert int(cast(p, u'\x08')) == 8
+ for name in ['char', 'short', 'int', 'long', 'long long']:
+ p = new_primitive_type('unsigned ' + name)
+ size = sizeof(p)
+ max = (1 << (8*size)) - 1
+ assert int(cast(p, 0)) == 0
+ assert int(cast(p, max)) == max
+ assert int(cast(p, -1)) == max
+ assert int(cast(p, max + 1)) == 0
+ assert long(cast(p, -1)) == max
+ assert int(cast(p, b'\xFE')) == 254
+ assert int(cast(p, u'\xFE')) == 254
+
+def test_no_float_on_int_types():
+ p = new_primitive_type('long')
+ py.test.raises(TypeError, float, cast(p, 42))
+ py.test.raises(TypeError, complex, cast(p, 42))
+
+def test_float_types():
+ INF = 1E200 * 1E200
+ for name in ["float", "double"]:
+ p = new_primitive_type(name)
+ assert bool(cast(p, 0))
+ assert bool(cast(p, INF))
+ assert bool(cast(p, -INF))
+ assert int(cast(p, -150)) == -150
+ assert int(cast(p, 61.91)) == 61
+ assert long(cast(p, 61.91)) == 61
+ assert type(int(cast(p, 61.91))) is int
+ assert type(int(cast(p, 1E22))) is long
+ assert type(long(cast(p, 61.91))) is long
+ assert type(long(cast(p, 1E22))) is long
+ py.test.raises(OverflowError, int, cast(p, INF))
+ py.test.raises(OverflowError, int, cast(p, -INF))
+ assert float(cast(p, 1.25)) == 1.25
+ assert float(cast(p, INF)) == INF
+ assert float(cast(p, -INF)) == -INF
+ if name == "float":
+ assert float(cast(p, 1.1)) != 1.1 # rounding error
+ assert float(cast(p, 1E200)) == INF # limited range
+
+ assert cast(p, -1.1) != cast(p, -1.1)
+ assert repr(float(cast(p, -0.0))) == '-0.0'
+ assert float(cast(p, b'\x09')) == 9.0
+ assert float(cast(p, u'\x09')) == 9.0
+ assert float(cast(p, True)) == 1.0
+ py.test.raises(TypeError, cast, p, None)
+
+def test_complex_types():
+ py.test.skip("later")
+ INF = 1E200 * 1E200
+ for name in ["float", "double"]:
+ p = new_primitive_type("_Complex " + name)
+ assert bool(cast(p, 0))
+ assert bool(cast(p, INF))
+ assert bool(cast(p, -INF))
+ assert bool(cast(p, 0j))
+ assert bool(cast(p, INF*1j))
+ assert bool(cast(p, -INF*1j))
+ py.test.raises(TypeError, int, cast(p, -150))
+ py.test.raises(TypeError, long, cast(p, -150))
+ py.test.raises(TypeError, float, cast(p, -150))
+ assert complex(cast(p, 1.25)) == 1.25
+ assert complex(cast(p, 1.25j)) == 1.25j
+ assert float(cast(p, INF*1j)) == INF*1j
+ assert float(cast(p, -INF)) == -INF
+ if name == "float":
+ assert complex(cast(p, 1.1j)) != 1.1j # rounding error
+ assert complex(cast(p, 1E200+3j)) == INF+3j # limited range
+ assert complex(cast(p, 3+1E200j)) == 3+INF*1j # limited range
+
+ assert cast(p, -1.1j) != cast(p, -1.1j)
+ assert repr(complex(cast(p, -0.0)).real) == '-0.0'
+ assert repr(complex(cast(p, -0j))) == '-0j'
+ assert complex(cast(p, '\x09')) == 9.0
+ assert complex(cast(p, True)) == 1.0
+ py.test.raises(TypeError, cast, p, None)
+ #
+ py.test.raises(cast, new_primitive_type(name), 1+2j)
+ py.test.raises(cast, new_primitive_type("int"), 1+2j)
+
+def test_character_type():
+ p = new_primitive_type("char")
+ assert bool(cast(p, '\x00'))
+ assert cast(p, '\x00') != cast(p, -17*256)
+ assert int(cast(p, 'A')) == 65
+ assert long(cast(p, 'A')) == 65
+ assert type(int(cast(p, 'A'))) is int
+ assert type(long(cast(p, 'A'))) is long
+ assert str(cast(p, 'A')) == repr(cast(p, 'A'))
+ assert repr(cast(p, 'A')) == "<cdata 'char' %s'A'>" % mandatory_b_prefix
+ assert repr(cast(p, 255)) == r"<cdata 'char' %s'\xff'>" % mandatory_b_prefix
+ assert repr(cast(p, 0)) == r"<cdata 'char' %s'\x00'>" % mandatory_b_prefix
+
+def test_pointer_type():
+ p = new_primitive_type("int")
+ assert repr(p) == "<ctype 'int'>"
+ p = new_pointer_type(p)
+ assert repr(p) == "<ctype 'int *'>"
+ p = new_pointer_type(p)
+ assert repr(p) == "<ctype 'int * *'>"
+ p = new_pointer_type(p)
+ assert repr(p) == "<ctype 'int * * *'>"
+
+def test_pointer_to_int():
+ BInt = new_primitive_type("int")
+ py.test.raises(TypeError, newp, BInt)
+ py.test.raises(TypeError, newp, BInt, None)
+ BPtr = new_pointer_type(BInt)
+ p = newp(BPtr)
+ assert repr(p) == "<cdata 'int *' owning %d bytes>" % size_of_int()
+ p = newp(BPtr, None)
+ assert repr(p) == "<cdata 'int *' owning %d bytes>" % size_of_int()
+ p = newp(BPtr, 5000)
+ assert repr(p) == "<cdata 'int *' owning %d bytes>" % size_of_int()
+ q = cast(BPtr, p)
+ assert repr(q).startswith("<cdata 'int *' 0x")
+ assert p == q
+ assert hash(p) == hash(q)
+
+def test_pointer_bool():
+ BInt = new_primitive_type("int")
+ BPtr = new_pointer_type(BInt)
+ p = cast(BPtr, 0)
+ assert bool(p) is False
+ p = cast(BPtr, 42)
+ assert bool(p) is True
+
+def test_pointer_to_pointer():
+ BInt = new_primitive_type("int")
+ BPtr = new_pointer_type(BInt)
+ BPtrPtr = new_pointer_type(BPtr)
+ p = newp(BPtrPtr, None)
+ assert repr(p) == "<cdata 'int * *' owning %d bytes>" % size_of_ptr()
+
+def test_reading_pointer_to_int():
+ BInt = new_primitive_type("int")
+ BPtr = new_pointer_type(BInt)
+ p = newp(BPtr, None)
+ assert p[0] == 0
+ p = newp(BPtr, 5000)
+ assert p[0] == 5000
+ py.test.raises(IndexError, "p[1]")
+ py.test.raises(IndexError, "p[-1]")
+
+def test_reading_pointer_to_float():
+ BFloat = new_primitive_type("float")
+ py.test.raises(TypeError, newp, BFloat, None)
+ BPtr = new_pointer_type(BFloat)
+ p = newp(BPtr, None)
+ assert p[0] == 0.0 and type(p[0]) is float
+ p = newp(BPtr, 1.25)
+ assert p[0] == 1.25 and type(p[0]) is float
+ p = newp(BPtr, 1.1)
+ assert p[0] != 1.1 and abs(p[0] - 1.1) < 1E-5 # rounding errors
+
+def test_cast_float_to_int():
+ for type in ["int", "unsigned int", "long", "unsigned long",
+ "long long", "unsigned long long"]:
+ p = new_primitive_type(type)
+ assert int(cast(p, 4.2)) == 4
+ py.test.raises(TypeError, newp, new_pointer_type(p), 4.2)
+
+def test_newp_integer_types():
+ for name in ['signed char', 'short', 'int', 'long', 'long long']:
+ p = new_primitive_type(name)
+ pp = new_pointer_type(p)
+ size = sizeof(p)
+ min = -(1 << (8*size-1))
+ max = (1 << (8*size-1)) - 1
+ assert newp(pp, min)[0] == min
+ assert newp(pp, max)[0] == max
+ py.test.raises(OverflowError, newp, pp, min - 1)
+ py.test.raises(OverflowError, newp, pp, max + 1)
+ for name in ['char', 'short', 'int', 'long', 'long long']:
+ p = new_primitive_type('unsigned ' + name)
+ pp = new_pointer_type(p)
+ size = sizeof(p)
+ max = (1 << (8*size)) - 1
+ assert newp(pp, 0)[0] == 0
+ assert newp(pp, max)[0] == max
+ py.test.raises(OverflowError, newp, pp, -1)
+ py.test.raises(OverflowError, newp, pp, max + 1)
+
+def test_reading_pointer_to_char():
+ BChar = new_primitive_type("char")
+ py.test.raises(TypeError, newp, BChar, None)
+ BPtr = new_pointer_type(BChar)
+ p = newp(BPtr, None)
+ assert p[0] == b'\x00'
+ p = newp(BPtr, b'A')
+ assert p[0] == b'A'
+ py.test.raises(TypeError, newp, BPtr, 65)
+ py.test.raises(TypeError, newp, BPtr, b"foo")
+ py.test.raises(TypeError, newp, BPtr, u"foo")
+ c = cast(BChar, b'A')
+ assert str(c) == repr(c)
+ assert int(c) == ord(b'A')
+ py.test.raises(TypeError, cast, BChar, b'foo')
+ py.test.raises(TypeError, cast, BChar, u'foo')
+
+def test_reading_pointer_to_pointer():
+ BVoidP = new_pointer_type(new_void_type())
+ BCharP = new_pointer_type(new_primitive_type("char"))
+ BInt = new_primitive_type("int")
+ BIntPtr = new_pointer_type(BInt)
+ BIntPtrPtr = new_pointer_type(BIntPtr)
+ q = newp(BIntPtr, 42)
+ assert q[0] == 42
+ p = newp(BIntPtrPtr, None)
+ assert p[0] is not None
+ assert p[0] == cast(BVoidP, 0)
+ assert p[0] == cast(BCharP, 0)
+ assert p[0] != None
+ assert repr(p[0]) == "<cdata 'int *' NULL>"
+ p[0] = q
+ assert p[0] != cast(BVoidP, 0)
+ assert p[0] != cast(BCharP, 0)
+ assert p[0][0] == 42
+ q[0] += 1
+ assert p[0][0] == 43
+ p = newp(BIntPtrPtr, q)
+ assert p[0][0] == 43
+
+def test_load_standard_library():
+ if sys.platform == "win32":
+ py.test.raises(OSError, find_and_load_library, None)
+ return
+ x = find_and_load_library(None)
+ BVoidP = new_pointer_type(new_void_type())
+ assert x.load_function(BVoidP, 'strcpy')
+ py.test.raises(KeyError, x.load_function,
+ BVoidP, 'xxx_this_function_does_not_exist')
+
+def test_hash_differences():
+ BChar = new_primitive_type("char")
+ BInt = new_primitive_type("int")
+ BFloat = new_primitive_type("float")
+ for i in range(1, 20):
+ if (hash(cast(BChar, chr(i))) !=
+ hash(cast(BInt, i))):
+ break
+ else:
+ raise AssertionError("hashes are equal")
+ for i in range(1, 20):
+ if hash(cast(BFloat, i)) != hash(float(i)):
+ break
+ else:
+ raise AssertionError("hashes are equal")
+
+def test_no_len_on_nonarray():
+ p = new_primitive_type("int")
+ py.test.raises(TypeError, len, cast(p, 42))
+
+def test_cmp_none():
+ p = new_primitive_type("int")
+ x = cast(p, 42)
+ assert (x == None) is False
+ assert (x != None) is True
+ assert (x == ["hello"]) is False
+ assert (x != ["hello"]) is True
+
+def test_invalid_indexing():
+ p = new_primitive_type("int")
+ x = cast(p, 42)
+ py.test.raises(TypeError, "p[0]")
+
+def test_default_str():
+ BChar = new_primitive_type("char")
+ x = cast(BChar, 42)
+ assert str(x) == repr(x)
+ BInt = new_primitive_type("int")
+ x = cast(BInt, 42)
+ assert str(x) == repr(x)
+ BArray = new_array_type(new_pointer_type(BInt), 10)
+ x = newp(BArray, None)
+ assert str(x) == repr(x)
+
+def test_default_unicode():
+ BInt = new_primitive_type("int")
+ x = cast(BInt, 42)
+ assert unicode(x) == unicode(repr(x))
+ BArray = new_array_type(new_pointer_type(BInt), 10)
+ x = newp(BArray, None)
+ assert unicode(x) == unicode(repr(x))
+
+def test_cast_from_cdataint():
+ BInt = new_primitive_type("int")
+ x = cast(BInt, 0)
+ y = cast(new_pointer_type(BInt), x)
+ assert bool(y) is False
+ #
+ x = cast(BInt, 42)
+ y = cast(BInt, x)
+ assert int(y) == 42
+ y = cast(new_primitive_type("char"), x)
+ assert int(y) == 42
+ y = cast(new_primitive_type("float"), x)
+ assert float(y) == 42.0
+ #
+ z = cast(BInt, 42.5)
+ assert int(z) == 42
+ z = cast(BInt, y)
+ assert int(z) == 42
+
+def test_array_type():
+ p = new_primitive_type("int")
+ assert repr(p) == "<ctype 'int'>"
+ #
+ py.test.raises(TypeError, new_array_type, new_pointer_type(p), "foo")
+ py.test.raises(ValueError, new_array_type, new_pointer_type(p), -42)
+ #
+ p1 = new_array_type(new_pointer_type(p), None)
+ assert repr(p1) == "<ctype 'int[]'>"
+ py.test.raises(ValueError, new_array_type, new_pointer_type(p1), 42)
+ #
+ p1 = new_array_type(new_pointer_type(p), 42)
+ p2 = new_array_type(new_pointer_type(p1), 25)
+ assert repr(p2) == "<ctype 'int[25][42]'>"
+ p2 = new_array_type(new_pointer_type(p1), None)
+ assert repr(p2) == "<ctype 'int[][42]'>"
+ #
+ py.test.raises(OverflowError,
+ new_array_type, new_pointer_type(p), sys.maxsize+1)
+ py.test.raises(OverflowError,
+ new_array_type, new_pointer_type(p), sys.maxsize // 3)
+
+def test_array_instance():
+ LENGTH = 1423
+ p = new_primitive_type("int")
+ p1 = new_array_type(new_pointer_type(p), LENGTH)
+ a = newp(p1, None)
+ assert repr(a) == "<cdata 'int[%d]' owning %d bytes>" % (
+ LENGTH, LENGTH * size_of_int())
+ assert len(a) == LENGTH
+ for i in range(LENGTH):
+ assert a[i] == 0
+ py.test.raises(IndexError, "a[LENGTH]")
+ py.test.raises(IndexError, "a[-1]")
+ for i in range(LENGTH):
+ a[i] = i * i + 1
+ for i in range(LENGTH):
+ assert a[i] == i * i + 1
+ e = py.test.raises(IndexError, "a[LENGTH+100] = 500")
+ assert ('(expected %d < %d)' % (LENGTH+100, LENGTH)) in str(e.value)
+ py.test.raises(TypeError, int, a)
+
+def test_array_of_unknown_length_instance():
+ p = new_primitive_type("int")
+ p1 = new_array_type(new_pointer_type(p), None)
+ py.test.raises(TypeError, newp, p1, None)
+ py.test.raises(ValueError, newp, p1, -42)
+ a = newp(p1, 42)
+ assert len(a) == 42
+ for i in range(42):
+ a[i] -= i
+ for i in range(42):
+ assert a[i] == -i
+ py.test.raises(IndexError, "a[42]")
+ py.test.raises(IndexError, "a[-1]")
+ py.test.raises(IndexError, "a[42] = 123")
+ py.test.raises(IndexError, "a[-1] = 456")
+
+def test_array_of_unknown_length_instance_with_initializer():
+ p = new_primitive_type("int")
+ p1 = new_array_type(new_pointer_type(p), None)
+ a = newp(p1, list(range(42)))
+ assert len(a) == 42
+ a = newp(p1, tuple(range(142)))
+ assert len(a) == 142
+
+def test_array_initializer():
+ p = new_primitive_type("int")
+ p1 = new_array_type(new_pointer_type(p), None)
+ a = newp(p1, list(range(100, 142)))
+ for i in range(42):
+ assert a[i] == 100 + i
+ #
+ p2 = new_array_type(new_pointer_type(p), 43)
+ a = newp(p2, tuple(range(100, 142)))
+ for i in range(42):
+ assert a[i] == 100 + i
+ assert a[42] == 0 # extra uninitialized item
+
+def test_array_add():
+ p = new_primitive_type("int")
+ p1 = new_array_type(new_pointer_type(p), 5) # int[5]
+ p2 = new_array_type(new_pointer_type(p1), 3) # int[3][5]
+ a = newp(p2, [list(range(n, n+5)) for n in [100, 200, 300]])
+ assert repr(a) == "<cdata 'int[3][5]' owning %d bytes>" % (
+ 3*5*size_of_int(),)
+ assert repr(a + 0).startswith("<cdata 'int(*)[5]' 0x")
+ assert repr(a[0]).startswith("<cdata 'int[5]' 0x")
+ assert repr((a + 0)[0]).startswith("<cdata 'int[5]' 0x")
+ assert repr(a[0] + 0).startswith("<cdata 'int *' 0x")
+ assert type(a[0][0]) is int
+ assert type((a[0] + 0)[0]) is int
+
+def test_array_sub():
+ BInt = new_primitive_type("int")
+ BArray = new_array_type(new_pointer_type(BInt), 5) # int[5]
+ a = newp(BArray, None)
+ p = a + 1
+ assert p - a == 1
+ assert p - (a+0) == 1
+ assert a == (p - 1)
+ BPtr = new_pointer_type(new_primitive_type("short"))
+ q = newp(BPtr, None)
+ py.test.raises(TypeError, "p - q")
+ py.test.raises(TypeError, "q - p")
+ py.test.raises(TypeError, "a - q")
+ e = py.test.raises(TypeError, "q - a")
+ assert str(e.value) == "cannot subtract cdata 'short *' and cdata 'int *'"
+
+def test_cast_primitive_from_cdata():
+ p = new_primitive_type("int")
+ n = cast(p, cast(p, -42))
+ assert int(n) == -42
+ #
+ p = new_primitive_type("unsigned int")
+ n = cast(p, cast(p, 42))
+ assert int(n) == 42
+ #
+ p = new_primitive_type("long long")
+ n = cast(p, cast(p, -(1<<60)))
+ assert int(n) == -(1<<60)
+ #
+ p = new_primitive_type("unsigned long long")
+ n = cast(p, cast(p, 1<<63))
+ assert int(n) == 1<<63
+ #
+ p = new_primitive_type("float")
+ n = cast(p, cast(p, 42.5))
+ assert float(n) == 42.5
+ #
+ p = new_primitive_type("char")
+ n = cast(p, cast(p, "A"))
+ assert int(n) == ord("A")
+
+def test_new_primitive_from_cdata():
+ p = new_primitive_type("int")
+ p1 = new_pointer_type(p)
+ n = newp(p1, cast(p, -42))
+ assert n[0] == -42
+ #
+ p = new_primitive_type("unsigned int")
+ p1 = new_pointer_type(p)
+ n = newp(p1, cast(p, 42))
+ assert n[0] == 42
+ #
+ p = new_primitive_type("float")
+ p1 = new_pointer_type(p)
+ n = newp(p1, cast(p, 42.5))
+ assert n[0] == 42.5
+ #
+ p = new_primitive_type("char")
+ p1 = new_pointer_type(p)
+ n = newp(p1, cast(p, "A"))
+ assert n[0] == b"A"
+
+def test_cast_between_pointers():
+ BIntP = new_pointer_type(new_primitive_type("int"))
+ BIntA = new_array_type(BIntP, None)
+ a = newp(BIntA, [40, 41, 42, 43, 44])
+ BShortP = new_pointer_type(new_primitive_type("short"))
+ b = cast(BShortP, a)
+ c = cast(BIntP, b)
+ assert c[3] == 43
+ BLongLong = new_primitive_type("long long")
+ d = cast(BLongLong, c)
+ e = cast(BIntP, d)
+ assert e[3] == 43
+ f = cast(BIntP, int(d))
+ assert f[3] == 43
+ #
+ b = cast(BShortP, 0)
+ assert not b
+ c = cast(BIntP, b)
+ assert not c
+ assert int(cast(BLongLong, c)) == 0
+
+def test_alignof():
+ BInt = new_primitive_type("int")
+ assert alignof(BInt) == sizeof(BInt)
+ BPtr = new_pointer_type(BInt)
+ assert alignof(BPtr) == sizeof(BPtr)
+ BArray = new_array_type(BPtr, None)
+ assert alignof(BArray) == alignof(BInt)
+
+def test_new_struct_type():
+ BStruct = new_struct_type("foo")
+ assert repr(BStruct) == "<ctype 'struct foo'>"
+ BPtr = new_pointer_type(BStruct)
+ assert repr(BPtr) == "<ctype 'struct foo *'>"
+ py.test.raises(TypeError, alignof, BStruct)
+
+def test_new_union_type():
+ BUnion = new_union_type("foo")
+ assert repr(BUnion) == "<ctype 'union foo'>"
+ BPtr = new_pointer_type(BUnion)
+ assert repr(BPtr) == "<ctype 'union foo *'>"
+
+def test_complete_struct():
+ BLong = new_primitive_type("long")
+ BChar = new_primitive_type("char")
+ BShort = new_primitive_type("short")
+ BStruct = new_struct_type("foo")
+ assert _getfields(BStruct) is None
+ complete_struct_or_union(BStruct, [('a1', BLong, -1),
+ ('a2', BChar, -1),
+ ('a3', BShort, -1)])
+ d = _getfields(BStruct)
+ assert len(d) == 3
+ assert d[0][0] == 'a1'
+ assert d[0][1].type is BLong
+ assert d[0][1].offset == 0
+ assert d[0][1].bitshift == -1
+ assert d[0][1].bitsize == -1
+ assert d[1][0] == 'a2'
+ assert d[1][1].type is BChar
+ assert d[1][1].offset == sizeof(BLong)
+ assert d[1][1].bitshift == -1
+ assert d[1][1].bitsize == -1
+ assert d[2][0] == 'a3'
+ assert d[2][1].type is BShort
+ assert d[2][1].offset == sizeof(BLong) + sizeof(BShort)
+ assert d[2][1].bitshift == -1
+ assert d[2][1].bitsize == -1
+ assert sizeof(BStruct) == 2 * sizeof(BLong)
+ assert alignof(BStruct) == alignof(BLong)
+
+def test_complete_union():
+ BLong = new_primitive_type("long")
+ BChar = new_primitive_type("char")
+ BUnion = new_union_type("foo")
+ assert _getfields(BUnion) is None
+ complete_struct_or_union(BUnion, [('a1', BLong, -1),
+ ('a2', BChar, -1)])
+ d = _getfields(BUnion)
+ assert len(d) == 2
+ assert d[0][0] == 'a1'
+ assert d[0][1].type is BLong
+ assert d[0][1].offset == 0
+ assert d[1][0] == 'a2'
+ assert d[1][1].type is BChar
+ assert d[1][1].offset == 0
+ assert sizeof(BUnion) == sizeof(BLong)
+ assert alignof(BUnion) == alignof(BLong)
+
+def test_struct_instance():
+ BInt = new_primitive_type("int")
+ BStruct = new_struct_type("foo")
+ BStructPtr = new_pointer_type(BStruct)
+ p = cast(BStructPtr, 0)
+ py.test.raises(AttributeError, "p.a1") # opaque
+ complete_struct_or_union(BStruct, [('a1', BInt, -1),
+ ('a2', BInt, -1)])
+ p = newp(BStructPtr, None)
+ s = p[0]
+ assert s.a1 == 0
+ s.a2 = 123
+ assert s.a1 == 0
+ assert s.a2 == 123
+ py.test.raises(OverflowError, "s.a1 = sys.maxsize+1")
+ assert s.a1 == 0
+ py.test.raises(AttributeError, "p.foobar")
+ py.test.raises(AttributeError, "s.foobar")
+
+def test_union_instance():
+ BInt = new_primitive_type("int")
+ BUInt = new_primitive_type("unsigned int")
+ BUnion = new_union_type("bar")
+ complete_struct_or_union(BUnion, [('a1', BInt, -1), ('a2', BUInt, -1)])
+ p = newp(new_pointer_type(BUnion), [-42])
+ bigval = -42 + (1 << (8*size_of_int()))
+ assert p.a1 == -42
+ assert p.a2 == bigval
+ p = newp(new_pointer_type(BUnion), {'a2': bigval})
+ assert p.a1 == -42
+ assert p.a2 == bigval
+ py.test.raises(OverflowError, newp, new_pointer_type(BUnion),
+ {'a1': bigval})
+ p = newp(new_pointer_type(BUnion), [])
+ assert p.a1 == p.a2 == 0
+
+def test_struct_pointer():
+ BInt = new_primitive_type("int")
+ BStruct = new_struct_type("foo")
+ BStructPtr = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('a1', BInt, -1),
+ ('a2', BInt, -1)])
+ p = newp(BStructPtr, None)
+ assert p.a1 == 0 # read/write via the pointer (C equivalent: '->')
+ p.a2 = 123
+ assert p.a1 == 0
+ assert p.a2 == 123
+
+def test_struct_init_list():
+ BVoidP = new_pointer_type(new_void_type())
+ BInt = new_primitive_type("int")
+ BIntPtr = new_pointer_type(BInt)
+ BStruct = new_struct_type("foo")
+ BStructPtr = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('a1', BInt, -1),
+ ('a2', BInt, -1),
+ ('a3', BInt, -1),
+ ('p4', BIntPtr, -1)])
+ s = newp(BStructPtr, [123, 456])
+ assert s.a1 == 123
+ assert s.a2 == 456
+ assert s.a3 == 0
+ assert s.p4 == cast(BVoidP, 0)
+ #
+ s = newp(BStructPtr, {'a2': 41122, 'a3': -123})
+ assert s.a1 == 0
+ assert s.a2 == 41122
+ assert s.a3 == -123
+ assert s.p4 == cast(BVoidP, 0)
+ #
+ py.test.raises(KeyError, newp, BStructPtr, {'foobar': 0})
+ #
+ p = newp(BIntPtr, 14141)
+ s = newp(BStructPtr, [12, 34, 56, p])
+ assert s.p4 == p
+ #
+ s = newp(BStructPtr, [12, 34, 56, cast(BVoidP, 0)])
+ assert s.p4 == cast(BVoidP, 0)
+ #
+ py.test.raises(TypeError, newp, BStructPtr, [12, 34, 56, None])
+
+def test_array_in_struct():
+ BInt = new_primitive_type("int")
+ BStruct = new_struct_type("foo")
+ BArrayInt5 = new_array_type(new_pointer_type(BInt), 5)
+ complete_struct_or_union(BStruct, [('a1', BArrayInt5, -1)])
+ s = newp(new_pointer_type(BStruct), [[20, 24, 27, 29, 30]])
+ assert s.a1[2] == 27
+ assert repr(s.a1).startswith("<cdata 'int[5]' 0x")
+
+def test_offsetof():
+ BInt = new_primitive_type("int")
+ BStruct = new_struct_type("foo")
+ py.test.raises(TypeError, offsetof, BInt, "abc")
+ py.test.raises(TypeError, offsetof, BStruct, "abc")
+ complete_struct_or_union(BStruct, [('abc', BInt, -1), ('def', BInt, -1)])
+ assert offsetof(BStruct, 'abc') == 0
+ assert offsetof(BStruct, 'def') == size_of_int()
+ py.test.raises(KeyError, offsetof, BStruct, "ghi")
+
+def test_function_type():
+ BInt = new_primitive_type("int")
+ BFunc = new_function_type((BInt, BInt), BInt, False)
+ assert repr(BFunc) == "<ctype 'int(*)(int, int)'>"
+ BFunc2 = new_function_type((), BFunc, False)
+ assert repr(BFunc2) == "<ctype 'int(*(*)())(int, int)'>"
+
+def test_function_type_taking_struct():
+ BChar = new_primitive_type("char")
+ BShort = new_primitive_type("short")
+ BStruct = new_struct_type("foo")
+ complete_struct_or_union(BStruct, [('a1', BChar, -1),
+ ('a2', BShort, -1)])
+ BFunc = new_function_type((BStruct,), BShort, False)
+ assert repr(BFunc) == "<ctype 'short(*)(struct foo)'>"
+
+def test_function_void_result():
+ BVoid = new_void_type()
+ BInt = new_primitive_type("int")
+ BFunc = new_function_type((BInt, BInt), BVoid, False)
+ assert repr(BFunc) == "<ctype 'void(*)(int, int)'>"
+
+def test_call_function_0():
+ BSignedChar = new_primitive_type("signed char")
+ BFunc0 = new_function_type((BSignedChar, BSignedChar), BSignedChar, False)
+ f = cast(BFunc0, _testfunc(0))
+ assert f(40, 2) == 42
+ assert f(-100, -100) == -200 + 256
+ py.test.raises(OverflowError, f, 128, 0)
+ py.test.raises(OverflowError, f, 0, 128)
+
+def test_call_function_1():
+ BInt = new_primitive_type("int")
+ BLong = new_primitive_type("long")
+ BFunc1 = new_function_type((BInt, BLong), BLong, False)
+ f = cast(BFunc1, _testfunc(1))
+ assert f(40, 2) == 42
+ assert f(-100, -100) == -200
+ int_max = (1 << (8*size_of_int()-1)) - 1
+ long_max = (1 << (8*size_of_long()-1)) - 1
+ if int_max == long_max:
+ assert f(int_max, 1) == - int_max - 1
+ else:
+ assert f(int_max, 1) == int_max + 1
+
+def test_call_function_2():
+ BLongLong = new_primitive_type("long long")
+ BFunc2 = new_function_type((BLongLong, BLongLong), BLongLong, False)
+ f = cast(BFunc2, _testfunc(2))
+ longlong_max = (1 << (8*sizeof(BLongLong)-1)) - 1
+ assert f(longlong_max - 42, 42) == longlong_max
+ assert f(43, longlong_max - 42) == - longlong_max - 1
+
+def test_call_function_3():
+ BFloat = new_primitive_type("float")
+ BDouble = new_primitive_type("double")
+ BFunc3 = new_function_type((BFloat, BDouble), BDouble, False)
+ f = cast(BFunc3, _testfunc(3))
+ assert f(1.25, 5.1) == 1.25 + 5.1 # exact
+ res = f(1.3, 5.1)
+ assert res != 6.4 and abs(res - 6.4) < 1E-5 # inexact
+
+def test_call_function_4():
+ BFloat = new_primitive_type("float")
+ BDouble = new_primitive_type("double")
+ BFunc4 = new_function_type((BFloat, BDouble), BFloat, False)
+ f = cast(BFunc4, _testfunc(4))
+ res = f(1.25, 5.1)
+ assert res != 6.35 and abs(res - 6.35) < 1E-5 # inexact
+
+def test_call_function_5():
+ BVoid = new_void_type()
+ BFunc5 = new_function_type((), BVoid, False)
+ f = cast(BFunc5, _testfunc(5))
+ f() # did not crash
+
+def test_call_function_6():
+ BInt = new_primitive_type("int")
+ BIntPtr = new_pointer_type(BInt)
+ BFunc6 = new_function_type((BIntPtr,), BIntPtr, False)
+ f = cast(BFunc6, _testfunc(6))
+ x = newp(BIntPtr, 42)
+ res = f(x)
+ assert typeof(res) is BIntPtr
+ assert res[0] == 42 - 1000
+ #
+ BIntArray = new_array_type(BIntPtr, None)
+ BFunc6bis = new_function_type((BIntArray,), BIntPtr, False)
+ f = cast(BFunc6bis, _testfunc(6))
+ #
+ res = f([142])
+ assert typeof(res) is BIntPtr
+ assert res[0] == 142 - 1000
+ #
+ res = f((143,))
+ assert typeof(res) is BIntPtr
+ assert res[0] == 143 - 1000
+ #
+ x = newp(BIntArray, [242])
+ res = f(x)
+ assert typeof(res) is BIntPtr
+ assert res[0] == 242 - 1000
+ #
+ py.test.raises(TypeError, f, 123456)
+ py.test.raises(TypeError, f, "foo")
+ py.test.raises(TypeError, f, u"bar")
+
+def test_call_function_7():
+ BChar = new_primitive_type("char")
+ BShort = new_primitive_type("short")
+ BStruct = new_struct_type("foo")
+ BStructPtr = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('a1', BChar, -1),
+ ('a2', BShort, -1)])
+ BFunc7 = new_function_type((BStruct,), BShort, False)
+ f = cast(BFunc7, _testfunc(7))
+ res = f({'a1': b'A', 'a2': -4042})
+ assert res == -4042 + ord(b'A')
+ #
+ x = newp(BStructPtr, {'a1': b'A', 'a2': -4042})
+ res = f(x[0])
+ assert res == -4042 + ord(b'A')
+
+def test_call_function_20():
+ BChar = new_primitive_type("char")
+ BShort = new_primitive_type("short")
+ BStruct = new_struct_type("foo")
+ BStructPtr = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('a1', BChar, -1),
+ ('a2', BShort, -1)])
+ BFunc18 = new_function_type((BStructPtr,), BShort, False)
+ f = cast(BFunc18, _testfunc(20))
+ x = newp(BStructPtr, {'a1': b'A', 'a2': -4042})
+ # test the exception that allows us to pass a 'struct foo' where the
+ # function really expects a 'struct foo *'.
+ res = f(x[0])
+ assert res == -4042 + ord(b'A')
+ assert res == f(x)
+
+def test_call_function_9():
+ BInt = new_primitive_type("int")
+ BFunc9 = new_function_type((BInt,), BInt, True) # vararg
+ f = cast(BFunc9, _testfunc(9))
+ assert f(0) == 0
+ assert f(1, cast(BInt, 42)) == 42
+ assert f(2, cast(BInt, 40), cast(BInt, 2)) == 42
+ py.test.raises(TypeError, f, 1, 42)
+ py.test.raises(TypeError, f, 2, None)
+ # promotion of chars and shorts to ints
+ BSChar = new_primitive_type("signed char")
+ BUChar = new_primitive_type("unsigned char")
+ BSShort = new_primitive_type("short")
+ assert f(3, cast(BSChar, -3), cast(BUChar, 200), cast(BSShort, -5)) == 192
+
+def test_cannot_call_with_a_autocompleted_struct():
+ BSChar = new_primitive_type("signed char")
+ BDouble = new_primitive_type("double")
+ BStruct = new_struct_type("foo")
+ BStructPtr = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('c', BDouble, -1, 8),
+ ('a', BSChar, -1, 2),
+ ('b', BSChar, -1, 0)])
+ e = py.test.raises(TypeError, new_function_type, (BStruct,), BDouble)
+ msg ='cannot pass as an argument a struct that was completed with verify()'
+ assert msg in str(e.value)
+
+def test_new_charp():
+ BChar = new_primitive_type("char")
+ BCharP = new_pointer_type(BChar)
+ BCharA = new_array_type(BCharP, None)
+ x = newp(BCharA, 42)
+ assert len(x) == 42
+ x = newp(BCharA, b"foobar")
+ assert len(x) == 7
+
+def test_load_and_call_function():
+ BChar = new_primitive_type("char")
+ BCharP = new_pointer_type(BChar)
+ BLong = new_primitive_type("long")
+ BFunc = new_function_type((BCharP,), BLong, False)
+ ll = find_and_load_library('c')
+ strlen = ll.load_function(BFunc, "strlen")
+ input = newp(new_array_type(BCharP, None), b"foobar")
+ assert strlen(input) == 6
+ #
+ assert strlen(b"foobarbaz") == 9
+ #
+ BVoidP = new_pointer_type(new_void_type())
+ strlenaddr = ll.load_function(BVoidP, "strlen")
+ assert strlenaddr == cast(BVoidP, strlen)
+
+def test_read_variable():
+ if sys.platform == 'win32':
+ py.test.skip("untested")
+ BVoidP = new_pointer_type(new_void_type())
+ ll = find_and_load_library('c')
+ stderr = ll.read_variable(BVoidP, "stderr")
+ assert stderr == cast(BVoidP, _testfunc(8))
+
+def test_read_variable_as_unknown_length_array():
+ if sys.platform == 'win32':
+ py.test.skip("untested")
+ BCharP = new_pointer_type(new_primitive_type("char"))
+ BArray = new_array_type(BCharP, None)
+ ll = find_and_load_library('c')
+ stderr = ll.read_variable(BArray, "stderr")
+ assert repr(stderr).startswith("<cdata 'char *' 0x")
+ # ^^ and not 'char[]', which is basically not allowed and would crash
+
+def test_write_variable():
+ if sys.platform == 'win32':
+ py.test.skip("untested")
+ BVoidP = new_pointer_type(new_void_type())
+ ll = find_and_load_library('c')
+ stderr = ll.read_variable(BVoidP, "stderr")
+ ll.write_variable(BVoidP, "stderr", cast(BVoidP, 0))
+ assert ll.read_variable(BVoidP, "stderr") is not None
+ assert not ll.read_variable(BVoidP, "stderr")
+ ll.write_variable(BVoidP, "stderr", stderr)
+ assert ll.read_variable(BVoidP, "stderr") == stderr
+
+def test_callback():
+ BInt = new_primitive_type("int")
+ def make_callback():
+ def cb(n):
+ return n + 1
+ BFunc = new_function_type((BInt,), BInt, False)
+ return callback(BFunc, cb, 42) # 'cb' and 'BFunc' go out of scope
+ f = make_callback()
+ assert f(-142) == -141
+ assert repr(f).startswith(
+ "<cdata 'int(*)(int)' calling <function ")
+ assert "cb at 0x" in repr(f)
+ e = py.test.raises(TypeError, f)
+ assert str(e.value) == "'int(*)(int)' expects 1 arguments, got 0"
+
+def test_callback_return_type():
+ for rettype in ["signed char", "short", "int", "long", "long long",
+ "unsigned char", "unsigned short", "unsigned int",
+ "unsigned long", "unsigned long long"]:
+ BRet = new_primitive_type(rettype)
+ def cb(n):
+ return n + 1
+ BFunc = new_function_type((BRet,), BRet)
+ f = callback(BFunc, cb, 42)
+ assert f(41) == 42
+ if rettype.startswith("unsigned "):
+ min = 0
+ max = (1 << (8*sizeof(BRet))) - 1
+ else:
+ min = -(1 << (8*sizeof(BRet)-1))
+ max = (1 << (8*sizeof(BRet)-1)) - 1
+ assert f(min) == min + 1
+ assert f(max - 1) == max
+ assert f(max) == 42
+
+def test_a_lot_of_callbacks():
+ BIGNUM = 10000
+ if 'PY_DOT_PY' in globals(): BIGNUM = 100 # tests on py.py
+ #
+ BInt = new_primitive_type("int")
+ BFunc = new_function_type((BInt,), BInt, False)
+ def make_callback(m):
+ def cb(n):
+ return n + m
+ return callback(BFunc, cb, 42) # 'cb' and 'BFunc' go out of scope
+ #
+ flist = [make_callback(i) for i in range(BIGNUM)]
+ for i, f in enumerate(flist):
+ assert f(-142) == -142 + i
+
+def test_callback_returning_struct():
+ BSChar = new_primitive_type("signed char")
+ BInt = new_primitive_type("int")
+ BDouble = new_primitive_type("double")
+ BStruct = new_struct_type("foo")
+ BStructPtr = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('a', BSChar, -1),
+ ('b', BDouble, -1)])
+ def cb(n):
+ return newp(BStructPtr, [-n, 1E-42])[0]
+ BFunc = new_function_type((BInt,), BStruct)
+ f = callback(BFunc, cb)
+ s = f(10)
+ assert typeof(s) is BStruct
+ assert repr(s) in ["<cdata 'struct foo' owning 12 bytes>",
+ "<cdata 'struct foo' owning 16 bytes>"]
+ assert s.a == -10
+ assert s.b == 1E-42
+
+def test_callback_returning_void():
+ BVoid = new_void_type()
+ BFunc = new_function_type((), BVoid, False)
+ def cb():
+ seen.append(42)
+ f = callback(BFunc, cb)
+ seen = []
+ f()
+ assert seen == [42]
+ py.test.raises(TypeError, callback, BFunc, cb, -42)
+
+def test_enum_type():
+ BEnum = new_enum_type("foo", (), ())
+ assert repr(BEnum) == "<ctype 'enum foo'>"
+ assert _getfields(BEnum) == []
+ #
+ BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))
+ assert _getfields(BEnum) == [(-20, 'ab'), (0, 'def'), (1, 'c')]
+
+def test_cast_to_enum():
+ BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))
+ e = cast(BEnum, 0)
+ assert repr(e) == "<cdata 'enum foo' 'def'>"
+ assert string(e) == 'def'
+ assert string(cast(BEnum, -20)) == 'ab'
+ assert string(cast(BEnum, 'c')) == 'c'
+ assert int(cast(BEnum, 'c')) == 1
+ assert int(cast(BEnum, 'def')) == 0
+ assert int(cast(BEnum, -242 + 2**128)) == -242
+ assert string(cast(BEnum, -242 + 2**128)) == '#-242'
+ assert string(cast(BEnum, '#-20')) == 'ab'
+ assert repr(cast(BEnum, '#-20')) == "<cdata 'enum foo' 'ab'>"
+ assert repr(cast(BEnum, '#-21')) == "<cdata 'enum foo' '#-21'>"
+
+def test_enum_with_non_injective_mapping():
+ BEnum = new_enum_type("foo", ('ab', 'cd'), (7, 7))
+ e = cast(BEnum, 7)
+ assert repr(e) == "<cdata 'enum foo' 'ab'>"
+ assert string(e) == 'ab'
+
+def test_enum_in_struct():
+ BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))
+ BStruct = new_struct_type("bar")
+ BStructPtr = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('a1', BEnum, -1)])
+ p = newp(BStructPtr, [-20])
+ assert p.a1 == "ab"
+ p = newp(BStructPtr, ["c"])
+ assert p.a1 == "c"
+ e = py.test.raises(TypeError, newp, BStructPtr, [None])
+ assert "must be a str or int, not NoneType" in str(e.value)
+
+def test_callback_returning_enum():
+ BInt = new_primitive_type("int")
+ BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))
+ def cb(n):
+ return '#%d' % n
+ BFunc = new_function_type((BInt,), BEnum)
+ f = callback(BFunc, cb)
+ assert f(0) == 'def'
+ assert f(1) == 'c'
+ assert f(-20) == 'ab'
+ assert f(20) == '#20'
+
+def test_callback_returning_char():
+ BInt = new_primitive_type("int")
+ BChar = new_primitive_type("char")
+ def cb(n):
+ return bytechr(n)
+ BFunc = new_function_type((BInt,), BChar)
+ f = callback(BFunc, cb)
+ assert f(0) == b'\x00'
+ assert f(255) == b'\xFF'
+
+def _hacked_pypy_uni4():
+ pyuni4 = {1: True, 2: False}[len(u'\U00012345')]
+ return 'PY_DOT_PY' in globals() and not pyuni4
+
+def test_callback_returning_wchar_t():
+ BInt = new_primitive_type("int")
+ BWChar = new_primitive_type("wchar_t")
+ def cb(n):
+ if n == -1:
+ return u'\U00012345'
+ if n == -2:
+ raise ValueError
+ return unichr(n)
+ BFunc = new_function_type((BInt,), BWChar)
+ f = callback(BFunc, cb)
+ assert f(0) == unichr(0)
+ assert f(255) == unichr(255)
+ assert f(0x1234) == u'\u1234'
+ if sizeof(BWChar) == 4 and not _hacked_pypy_uni4():
+ assert f(-1) == u'\U00012345'
+ assert f(-2) == u'\x00' # and an exception printed to stderr
+
+def test_struct_with_bitfields():
+ BLong = new_primitive_type("long")
+ BStruct = new_struct_type("foo")
+ LONGBITS = 8 * sizeof(BLong)
+ complete_struct_or_union(BStruct, [('a1', BLong, 1),
+ ('a2', BLong, 2),
+ ('a3', BLong, 3),
+ ('a4', BLong, LONGBITS - 5)])
+ d = _getfields(BStruct)
+ assert d[0][1].offset == d[1][1].offset == d[2][1].offset == 0
+ assert d[3][1].offset == sizeof(BLong)
+ assert d[0][1].bitshift == 0
+ assert d[0][1].bitsize == 1
+ assert d[1][1].bitshift == 1
+ assert d[1][1].bitsize == 2
+ assert d[2][1].bitshift == 3
+ assert d[2][1].bitsize == 3
+ assert d[3][1].bitshift == 0
+ assert d[3][1].bitsize == LONGBITS - 5
+ assert sizeof(BStruct) == 2 * sizeof(BLong)
+ assert alignof(BStruct) == alignof(BLong)
+
+def test_bitfield_instance():
+ BInt = new_primitive_type("int")
+ BUnsignedInt = new_primitive_type("unsigned int")
+ BStruct = new_struct_type("foo")
+ complete_struct_or_union(BStruct, [('a1', BInt, 1),
+ ('a2', BUnsignedInt, 2),
+ ('a3', BInt, 3)])
+ p = newp(new_pointer_type(BStruct), None)
+ p.a1 = -1
+ assert p.a1 == -1
+ p.a1 = 0
+ py.test.raises(OverflowError, "p.a1 = 2")
+ assert p.a1 == 0
+ #
+ p.a1 = -1
+ p.a2 = 3
+ p.a3 = -4
+ py.test.raises(OverflowError, "p.a3 = 4")
+ e = py.test.raises(OverflowError, "p.a3 = -5")
+ assert str(e.value) == ("value -5 outside the range allowed by the "
+ "bit field width: -4 <= x <= 3")
+ assert p.a1 == -1 and p.a2 == 3 and p.a3 == -4
+ #
+ # special case for convenience: "int x:1", while normally signed,
+ # allows also setting the value "1" (it still gets read back as -1)
+ p.a1 = 1
+ assert p.a1 == -1
+ e = py.test.raises(OverflowError, "p.a1 = -2")
+ assert str(e.value) == ("value -2 outside the range allowed by the "
+ "bit field width: -1 <= x <= 1")
+
+def test_bitfield_instance_init():
+ BInt = new_primitive_type("int")
+ BStruct = new_struct_type("foo")
+ complete_struct_or_union(BStruct, [('a1', BInt, 1)])
+ p = newp(new_pointer_type(BStruct), [-1])
+ assert p.a1 == -1
+ p = newp(new_pointer_type(BStruct), {'a1': -1})
+ assert p.a1 == -1
+ #
+ BUnion = new_union_type("bar")
+ complete_struct_or_union(BUnion, [('a1', BInt, 1)])
+ p = newp(new_pointer_type(BUnion), [-1])
+ assert p.a1 == -1
+
+def test_weakref():
+ import weakref
+ BInt = new_primitive_type("int")
+ BPtr = new_pointer_type(BInt)
+ weakref.ref(BInt)
+ weakref.ref(newp(BPtr, 42))
+ weakref.ref(cast(BPtr, 42))
+ weakref.ref(cast(BInt, 42))
+
+def test_no_inheritance():
+ BInt = new_primitive_type("int")
+ try:
+ class foo(type(BInt)): pass
+ except TypeError:
+ pass
+ else:
+ raise AssertionError
+ x = cast(BInt, 42)
+ try:
+ class foo(type(x)): pass
+ except TypeError:
+ pass
+ else:
+ raise AssertionError
+
+def test_assign_string():
+ BChar = new_primitive_type("char")
+ BArray1 = new_array_type(new_pointer_type(BChar), 5)
+ BArray2 = new_array_type(new_pointer_type(BArray1), 5)
+ a = newp(BArray2, [b"abc", b"de", b"ghij"])
+ assert string(a[1]) == b"de"
+ assert string(a[2]) == b"ghij"
+ a[2] = b"."
+ assert string(a[2]) == b"."
+ a[2] = b"12345"
+ assert string(a[2]) == b"12345"
+ e = py.test.raises(IndexError, 'a[2] = b"123456"')
+ assert 'char[5]' in str(e.value)
+ assert 'got 6 characters' in str(e.value)
+
+def test_add_error():
+ x = cast(new_primitive_type("int"), 42)
+ py.test.raises(TypeError, "x + 1")
+ py.test.raises(TypeError, "x - 1")
+
+def test_void_errors():
+ py.test.raises(TypeError, alignof, new_void_type())
+ py.test.raises(TypeError, newp, new_pointer_type(new_void_type()), None)
+ x = cast(new_pointer_type(new_void_type()), 42)
+ py.test.raises(TypeError, "x + 1")
+ py.test.raises(TypeError, "x - 1")
+
+def test_too_many_items():
+ BChar = new_primitive_type("char")
+ BArray = new_array_type(new_pointer_type(BChar), 5)
+ py.test.raises(IndexError, newp, BArray, tuple(b'123456'))
+ py.test.raises(IndexError, newp, BArray, list(b'123456'))
+ py.test.raises(IndexError, newp, BArray, b'123456')
+ BStruct = new_struct_type("foo")
+ complete_struct_or_union(BStruct, [])
+ py.test.raises(TypeError, newp, new_pointer_type(BStruct), b'')
+ py.test.raises(ValueError, newp, new_pointer_type(BStruct), [b'1'])
+
+def test_more_type_errors():
+ BInt = new_primitive_type("int")
+ BChar = new_primitive_type("char")
+ BArray = new_array_type(new_pointer_type(BChar), 5)
+ py.test.raises(TypeError, newp, BArray, 12.34)
+ BArray = new_array_type(new_pointer_type(BInt), 5)
+ py.test.raises(TypeError, newp, BArray, 12.34)
+ BFloat = new_primitive_type("float")
+ py.test.raises(TypeError, cast, BFloat, newp(BArray, None))
+
+def test_more_overflow_errors():
+ BUInt = new_primitive_type("unsigned int")
+ py.test.raises(OverflowError, newp, new_pointer_type(BUInt), -1)
+ py.test.raises(OverflowError, newp, new_pointer_type(BUInt), 2**32)
+
+def test_newp_copying():
+ """Test that we can do newp(<type>, <cdata of the given type>) for most
+ types, with the exception of arrays, like in C.
+ """
+ BInt = new_primitive_type("int")
+ p = newp(new_pointer_type(BInt), cast(BInt, 42))
+ assert p[0] == 42
+ #
+ BUInt = new_primitive_type("unsigned int")
+ p = newp(new_pointer_type(BUInt), cast(BUInt, 42))
+ assert p[0] == 42
+ #
+ BChar = new_primitive_type("char")
+ p = newp(new_pointer_type(BChar), cast(BChar, '!'))
+ assert p[0] == b'!'
+ #
+ BFloat = new_primitive_type("float")
+ p = newp(new_pointer_type(BFloat), cast(BFloat, 12.25))
+ assert p[0] == 12.25
+ #
+ BStruct = new_struct_type("foo_s")
+ BStructPtr = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('a1', BInt, -1)])
+ s1 = newp(BStructPtr, [42])
+ p1 = newp(new_pointer_type(BStructPtr), s1)
+ assert p1[0] == s1
+ #
+ BArray = new_array_type(new_pointer_type(BInt), None)
+ a1 = newp(BArray, [1, 2, 3, 4])
+ py.test.raises(TypeError, newp, BArray, a1)
+ BArray6 = new_array_type(new_pointer_type(BInt), 6)
+ a1 = newp(BArray6, None)
+ py.test.raises(TypeError, newp, BArray6, a1)
+ #
+ s1 = newp(BStructPtr, [42])
+ s2 = newp(BStructPtr, s1[0])
+ assert s2.a1 == 42
+ #
+ BUnion = new_union_type("foo_u")
+ BUnionPtr = new_pointer_type(BUnion)
+ complete_struct_or_union(BUnion, [('a1', BInt, -1)])
+ u1 = newp(BUnionPtr, [42])
+ u2 = newp(BUnionPtr, u1[0])
+ assert u2.a1 == 42
+ #
+ BFunc = new_function_type((BInt,), BUInt)
+ p1 = cast(BFunc, 42)
+ p2 = newp(new_pointer_type(BFunc), p1)
+ assert p2[0] == p1
+
+def test_string():
+ BChar = new_primitive_type("char")
+ assert string(cast(BChar, 42)) == b'*'
+ assert string(cast(BChar, 0)) == b'\x00'
+ BCharP = new_pointer_type(BChar)
+ BArray = new_array_type(BCharP, 10)
+ a = newp(BArray, b"hello")
+ assert len(a) == 10
+ assert string(a) == b"hello"
+ p = a + 2
+ assert string(p) == b"llo"
+ assert string(newp(new_array_type(BCharP, 4), b"abcd")) == b"abcd"
+ py.test.raises(RuntimeError, string, cast(BCharP, 0))
+ assert string(a, 4) == b"hell"
+ assert string(a, 5) == b"hello"
+ assert string(a, 6) == b"hello"
+
+def test_string_byte():
+ BByte = new_primitive_type("signed char")
+ assert string(cast(BByte, 42)) == b'*'
+ assert string(cast(BByte, 0)) == b'\x00'
+ BArray = new_array_type(new_pointer_type(BByte), None)
+ a = newp(BArray, [65, 66, 67])
+ assert type(string(a)) is bytes and string(a) == b'ABC'
+ #
+ BByte = new_primitive_type("unsigned char")
+ assert string(cast(BByte, 42)) == b'*'
+ assert string(cast(BByte, 0)) == b'\x00'
+ BArray = new_array_type(new_pointer_type(BByte), None)
+ a = newp(BArray, [65, 66, 67])
+ assert type(string(a)) is bytes and string(a) == b'ABC'
+ if 'PY_DOT_PY' not in globals() and sys.version_info < (3,):
+ assert string(a, 8).startswith(b'ABC') # may contain additional garbage
+
+def test_string_wchar():
+ BWChar = new_primitive_type("wchar_t")
+ assert string(cast(BWChar, 42)) == u'*'
+ assert string(cast(BWChar, 0x4253)) == u'\u4253'
+ assert string(cast(BWChar, 0)) == u'\x00'
+ BArray = new_array_type(new_pointer_type(BWChar), None)
+ a = newp(BArray, [u'A', u'B', u'C'])
+ assert type(string(a)) is unicode and string(a) == u'ABC'
+ if 'PY_DOT_PY' not in globals() and sys.version_info < (3,):
+ assert string(a, 8).startswith(u'ABC') # may contain additional garbage
+
+def test_string_typeerror():
+ BShort = new_primitive_type("short")
+ BArray = new_array_type(new_pointer_type(BShort), None)
+ a = newp(BArray, [65, 66, 67])
+ py.test.raises(TypeError, string, a)
+
+def test_bug_convert_to_ptr():
+ BChar = new_primitive_type("char")
+ BCharP = new_pointer_type(BChar)
+ BDouble = new_primitive_type("double")
+ x = cast(BDouble, 42)
+ py.test.raises(TypeError, newp, new_pointer_type(BCharP), x)
+
+def test_set_struct_fields():
+ BChar = new_primitive_type("char")
+ BCharP = new_pointer_type(BChar)
+ BCharArray10 = new_array_type(BCharP, 10)
+ BStruct = new_struct_type("foo")
+ BStructPtr = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('a1', BCharArray10, -1)])
+ p = newp(BStructPtr, None)
+ assert string(p.a1) == b''
+ p.a1 = b'foo'
+ assert string(p.a1) == b'foo'
+ assert list(p.a1) == [b'f', b'o', b'o'] + [b'\x00'] * 7
+ p.a1 = [b'x', b'y']
+ assert string(p.a1) == b'xyo'
+
+def test_invalid_function_result_types():
+ BFunc = new_function_type((), new_void_type())
+ BArray = new_array_type(new_pointer_type(BFunc), 5) # works
+ new_function_type((), BFunc) # works
+ new_function_type((), new_primitive_type("int"))
+ new_function_type((), new_pointer_type(BFunc))
+ BUnion = new_union_type("foo_u")
+ complete_struct_or_union(BUnion, [])
+ py.test.raises(NotImplementedError, new_function_type, (), BUnion)
+ py.test.raises(TypeError, new_function_type, (), BArray)
+
+def test_struct_return_in_func():
+ BChar = new_primitive_type("char")
+ BShort = new_primitive_type("short")
+ BFloat = new_primitive_type("float")
+ BDouble = new_primitive_type("double")
+ BInt = new_primitive_type("int")
+ BStruct = new_struct_type("foo_s")
+ complete_struct_or_union(BStruct, [('a1', BChar, -1),
+ ('a2', BShort, -1)])
+ BFunc10 = new_function_type((BInt,), BStruct)
+ f = cast(BFunc10, _testfunc(10))
+ s = f(40)
+ assert repr(s) == "<cdata 'struct foo_s' owning 4 bytes>"
+ assert s.a1 == bytechr(40)
+ assert s.a2 == 40 * 40
+ #
+ BStruct11 = new_struct_type("test11")
+ complete_struct_or_union(BStruct11, [('a1', BInt, -1),
+ ('a2', BInt, -1)])
+ BFunc11 = new_function_type((BInt,), BStruct11)
+ f = cast(BFunc11, _testfunc(11))
+ s = f(40)
+ assert repr(s) == "<cdata 'struct test11' owning 8 bytes>"
+ assert s.a1 == 40
+ assert s.a2 == 40 * 40
+ #
+ BStruct12 = new_struct_type("test12")
+ complete_struct_or_union(BStruct12, [('a1', BDouble, -1),
+ ])
+ BFunc12 = new_function_type((BInt,), BStruct12)
+ f = cast(BFunc12, _testfunc(12))
+ s = f(40)
+ assert repr(s) == "<cdata 'struct test12' owning 8 bytes>"
+ assert s.a1 == 40.0
+ #
+ BStruct13 = new_struct_type("test13")
+ complete_struct_or_union(BStruct13, [('a1', BInt, -1),
+ ('a2', BInt, -1),
+ ('a3', BInt, -1)])
+ BFunc13 = new_function_type((BInt,), BStruct13)
+ f = cast(BFunc13, _testfunc(13))
+ s = f(40)
+ assert repr(s) == "<cdata 'struct test13' owning 12 bytes>"
+ assert s.a1 == 40
+ assert s.a2 == 40 * 40
+ assert s.a3 == 40 * 40 * 40
+ #
+ BStruct14 = new_struct_type("test14")
+ complete_struct_or_union(BStruct14, [('a1', BFloat, -1),
+ ])
+ BFunc14 = new_function_type((BInt,), BStruct14)
+ f = cast(BFunc14, _testfunc(14))
+ s = f(40)
+ assert repr(s) == "<cdata 'struct test14' owning 4 bytes>"
+ assert s.a1 == 40.0
+ #
+ BStruct15 = new_struct_type("test15")
+ complete_struct_or_union(BStruct15, [('a1', BFloat, -1),
+ ('a2', BInt, -1)])
+ BFunc15 = new_function_type((BInt,), BStruct15)
+ f = cast(BFunc15, _testfunc(15))
+ s = f(40)
+ assert repr(s) == "<cdata 'struct test15' owning 8 bytes>"
+ assert s.a1 == 40.0
+ assert s.a2 == 40 * 40
+ #
+ BStruct16 = new_struct_type("test16")
+ complete_struct_or_union(BStruct16, [('a1', BFloat, -1),
+ ('a2', BFloat, -1)])
+ BFunc16 = new_function_type((BInt,), BStruct16)
+ f = cast(BFunc16, _testfunc(16))
+ s = f(40)
+ assert repr(s) == "<cdata 'struct test16' owning 8 bytes>"
+ assert s.a1 == 40.0
+ assert s.a2 == -40.0
+ #
+ BStruct17 = new_struct_type("test17")
+ complete_struct_or_union(BStruct17, [('a1', BInt, -1),
+ ('a2', BFloat, -1)])
+ BFunc17 = new_function_type((BInt,), BStruct17)
+ f = cast(BFunc17, _testfunc(17))
+ s = f(40)
+ assert repr(s) == "<cdata 'struct test17' owning 8 bytes>"
+ assert s.a1 == 40
+ assert s.a2 == 40.0 * 40.0
+ #
+ BStruct17Ptr = new_pointer_type(BStruct17)
+ BFunc18 = new_function_type((BStruct17Ptr,), BInt)
+ f = cast(BFunc18, _testfunc(18))
+ x = f([[40, 2.5]])
+ assert x == 42
+ x = f([{'a2': 43.1}])
+ assert x == 43
+
+def test_cast_with_functionptr():
+ BFunc = new_function_type((), new_void_type())
+ BFunc2 = new_function_type((), new_primitive_type("short"))
+ BCharP = new_pointer_type(new_primitive_type("char"))
+ BIntP = new_pointer_type(new_primitive_type("int"))
+ BStruct = new_struct_type("foo")
+ BStructPtr = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('a1', BFunc, -1)])
+ newp(BStructPtr, [cast(BFunc, 0)])
+ newp(BStructPtr, [cast(BCharP, 0)])
+ py.test.raises(TypeError, newp, BStructPtr, [cast(BIntP, 0)])
+ py.test.raises(TypeError, newp, BStructPtr, [cast(BFunc2, 0)])
+
+def test_wchar():
+ BWChar = new_primitive_type("wchar_t")
+ BInt = new_primitive_type("int")
+ pyuni4 = {1: True, 2: False}[len(u'\U00012345')]
+ wchar4 = {2: False, 4: True}[sizeof(BWChar)]
+ assert str(cast(BWChar, 0x45)) == "<cdata 'wchar_t' %s'E'>" % (
+ mandatory_u_prefix,)
+ assert str(cast(BWChar, 0x1234)) == "<cdata 'wchar_t' %s'\u1234'>" % (
+ mandatory_u_prefix,)
+ if wchar4:
+ if not _hacked_pypy_uni4():
+ x = cast(BWChar, 0x12345)
+ assert str(x) == "<cdata 'wchar_t' %s'\U00012345'>" % (
+ mandatory_u_prefix,)
+ assert int(x) == 0x12345
+ else:
+ assert not pyuni4
+ #
+ BWCharP = new_pointer_type(BWChar)
+ BStruct = new_struct_type("foo_s")
+ BStructPtr = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('a1', BWChar, -1),
+ ('a2', BWCharP, -1)])
+ s = newp(BStructPtr)
+ s.a1 = u'\x00'
+ assert s.a1 == u'\x00'
+ py.test.raises(TypeError, "s.a1 = b'a'")
+ py.test.raises(TypeError, "s.a1 = bytechr(0xFF)")
+ s.a1 = u'\u1234'
+ assert s.a1 == u'\u1234'
+ if pyuni4:
+ assert wchar4
+ s.a1 = u'\U00012345'
+ assert s.a1 == u'\U00012345'
+ elif wchar4:
+ if not _hacked_pypy_uni4():
+ s.a1 = cast(BWChar, 0x12345)
+ assert s.a1 == u'\ud808\udf45'
+ s.a1 = u'\ud807\udf44'
+ assert s.a1 == u'\U00011f44'
+ else:
+ py.test.raises(TypeError, "s.a1 = u'\U00012345'")
+ #
+ BWCharArray = new_array_type(BWCharP, None)
+ a = newp(BWCharArray, u'hello \u1234 world')
+ assert len(a) == 14 # including the final null
+ assert string(a) == u'hello \u1234 world'
+ a[13] = u'!'
+ assert string(a) == u'hello \u1234 world!'
+ assert str(a) == repr(a)
+ assert a[6] == u'\u1234'
+ a[6] = u'-'
+ assert string(a) == u'hello - world!'
+ assert str(a) == repr(a)
+ #
+ if wchar4 and not _hacked_pypy_uni4():
+ u = u'\U00012345\U00012346\U00012347'
+ a = newp(BWCharArray, u)
+ assert len(a) == 4
+ assert string(a) == u
+ assert len(list(a)) == 4
+ expected = [u'\U00012345', u'\U00012346', u'\U00012347', unichr(0)]
+ assert list(a) == expected
+ got = [a[i] for i in range(4)]
+ assert got == expected
+ py.test.raises(IndexError, 'a[4]')
+ #
+ w = cast(BWChar, 'a')
+ assert repr(w) == "<cdata 'wchar_t' %s'a'>" % mandatory_u_prefix
+ assert str(w) == repr(w)
+ assert string(w) == u'a'
+ assert int(w) == ord('a')
+ w = cast(BWChar, 0x1234)
+ assert repr(w) == "<cdata 'wchar_t' %s'\u1234'>" % mandatory_u_prefix
+ assert str(w) == repr(w)
+ assert string(w) == u'\u1234'
+ assert int(w) == 0x1234
+ w = cast(BWChar, u'\u8234')
+ assert repr(w) == "<cdata 'wchar_t' %s'\u8234'>" % mandatory_u_prefix
+ assert str(w) == repr(w)
+ assert string(w) == u'\u8234'
+ assert int(w) == 0x8234
+ w = cast(BInt, u'\u1234')
+ assert repr(w) == "<cdata 'int' 4660>"
+ if wchar4 and not _hacked_pypy_uni4():
+ w = cast(BWChar, u'\U00012345')
+ assert repr(w) == "<cdata 'wchar_t' %s'\U00012345'>" % (
+ mandatory_u_prefix,)
+ assert str(w) == repr(w)
+ assert string(w) == u'\U00012345'
+ assert int(w) == 0x12345
+ w = cast(BInt, u'\U00012345')
+ assert repr(w) == "<cdata 'int' 74565>"
+ py.test.raises(TypeError, cast, BInt, u'')
+ py.test.raises(TypeError, cast, BInt, u'XX')
+ assert int(cast(BInt, u'a')) == ord('a')
+ #
+ a = newp(BWCharArray, u'hello - world')
+ p = cast(BWCharP, a)
+ assert string(p) == u'hello - world'
+ p[6] = u'\u2345'
+ assert string(p) == u'hello \u2345 world'
+ #
+ s = newp(BStructPtr, [u'\u1234', p])
+ assert s.a1 == u'\u1234'
+ assert s.a2 == p
+ assert str(s.a2) == repr(s.a2)
+ assert string(s.a2) == u'hello \u2345 world'
+ #
+ q = cast(BWCharP, 0)
+ assert str(q) == repr(q)
+ py.test.raises(RuntimeError, string, q)
+ #
+ def cb(p):
+ assert repr(p).startswith("<cdata 'wchar_t *' 0x")
+ return len(string(p))
+ BFunc = new_function_type((BWCharP,), BInt, False)
+ f = callback(BFunc, cb, -42)
+ assert f(u'a\u1234b') == 3
+ #
+ if wchar4 and not pyuni4 and not _hacked_pypy_uni4():
+ # try out-of-range wchar_t values
+ x = cast(BWChar, 1114112)
+ py.test.raises(ValueError, string, x)
+ x = cast(BWChar, -1)
+ py.test.raises(ValueError, string, x)
+
+def test_keepalive_struct():
+ # exception to the no-keepalive rule: p=newp(BStructPtr) returns a
+ # pointer owning the memory, and p[0] returns a pointer to the
+ # struct that *also* owns the memory
+ BStruct = new_struct_type("foo")
+ BStructPtr = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('a1', new_primitive_type("int"), -1),
+ ('a2', new_primitive_type("int"), -1),
+ ('a3', new_primitive_type("int"), -1)])
+ p = newp(BStructPtr)
+ assert repr(p) == "<cdata 'struct foo *' owning 12 bytes>"
+ q = p[0]
+ assert repr(q) == "<cdata 'struct foo' owning 12 bytes>"
+ q.a1 = 123456
+ assert p.a1 == 123456
+ r = cast(BStructPtr, p)
+ assert repr(r[0]).startswith("<cdata 'struct foo &' 0x")
+ del p
+ import gc; gc.collect()
+ assert q.a1 == 123456
+ assert repr(q) == "<cdata 'struct foo' owning 12 bytes>"
+ assert q.a1 == 123456
+
+def test_nokeepalive_struct():
+ BStruct = new_struct_type("foo")
+ BStructPtr = new_pointer_type(BStruct)
+ BStructPtrPtr = new_pointer_type(BStructPtr)
+ complete_struct_or_union(BStruct, [('a1', new_primitive_type("int"), -1)])
+ p = newp(BStructPtr)
+ pp = newp(BStructPtrPtr)
+ pp[0] = p
+ s = pp[0][0]
+ assert repr(s).startswith("<cdata 'struct foo &' 0x")
+
+def test_owning_repr():
+ BInt = new_primitive_type("int")
+ BArray = new_array_type(new_pointer_type(BInt), None) # int[]
+ p = newp(BArray, 7)
+ assert repr(p) == "<cdata 'int[]' owning 28 bytes>"
+ assert sizeof(p) == 28
+ #
+ BArray = new_array_type(new_pointer_type(BInt), 7) # int[7]
+ p = newp(BArray, None)
+ assert repr(p) == "<cdata 'int[7]' owning 28 bytes>"
+ assert sizeof(p) == 28
+
+def test_cannot_dereference_void():
+ BVoidP = new_pointer_type(new_void_type())
+ p = cast(BVoidP, 123456)
+ py.test.raises(TypeError, "p[0]")
+ p = cast(BVoidP, 0)
+ if 'PY_DOT_PY' in globals(): py.test.skip("NULL crashes early on py.py")
+ py.test.raises(TypeError, "p[0]")
+
+def test_iter():
+ BInt = new_primitive_type("int")
+ BIntP = new_pointer_type(BInt)
+ BArray = new_array_type(BIntP, None) # int[]
+ p = newp(BArray, 7)
+ assert list(p) == list(iter(p)) == [0] * 7
+ #
+ py.test.raises(TypeError, iter, cast(BInt, 5))
+ py.test.raises(TypeError, iter, cast(BIntP, 123456))
+
+def test_cmp():
+ BInt = new_primitive_type("int")
+ BIntP = new_pointer_type(BInt)
+ BVoidP = new_pointer_type(new_void_type())
+ p = newp(BIntP, 123)
+ q = cast(BInt, 124)
+ py.test.raises(TypeError, "p < q")
+ py.test.raises(TypeError, "p <= q")
+ assert (p == q) is False
+ assert (p != q) is True
+ py.test.raises(TypeError, "p > q")
+ py.test.raises(TypeError, "p >= q")
+ r = cast(BVoidP, p)
+ assert (p < r) is False
+ assert (p <= r) is True
+ assert (p == r) is True
+ assert (p != r) is False
+ assert (p > r) is False
+ assert (p >= r) is True
+ s = newp(BIntP, 125)
+ assert (p == s) is False
+ assert (p != s) is True
+ assert (p < s) is (p <= s) is (s > p) is (s >= p)
+ assert (p > s) is (p >= s) is (s < p) is (s <= p)
+ assert (p < s) ^ (p > s)
+
+def test_buffer():
+ BShort = new_primitive_type("short")
+ s = newp(new_pointer_type(BShort), 100)
+ assert sizeof(s) == size_of_ptr()
+ assert sizeof(BShort) == 2
+ assert len(readbuf(buffer(s))) == 2
+ #
+ BChar = new_primitive_type("char")
+ BCharArray = new_array_type(new_pointer_type(BChar), None)
+ c = newp(BCharArray, b"hi there")
+ buf = buffer(c)
+ assert readbuf(buf) == b"hi there\x00"
+ assert len(buf) == len(b"hi there\x00")
+ assert buf[0] == bufchar('h')
+ assert buf[2] == bufchar(' ')
+ assert list(buf) == list(map(bufchar, "hi there\x00"))
+ buf[2] = bufchar('-')
+ assert c[2] == b'-'
+ assert readbuf(buf) == b"hi-there\x00"
+ c[2] = b'!'
+ assert buf[2] == bufchar('!')
+ assert readbuf(buf) == b"hi!there\x00"
+ c[2] = b'-'
+ buf[:2] = b'HI'
+ assert string(c) == b'HI-there'
+ assert buf[:4:2] == b'H-'
+ if '__pypy__' not in sys.builtin_module_names:
+ # XXX pypy doesn't support the following assignment so far
+ buf[:4:2] = b'XY'
+ assert string(c) == b'XIYthere'
+
+def test_getcname():
+ BUChar = new_primitive_type("unsigned char")
+ BArray = new_array_type(new_pointer_type(BUChar), 123)
+ assert getcname(BArray, "<-->") == "unsigned char<-->[123]"
+
+def test_errno():
+ BVoid = new_void_type()
+ BFunc5 = new_function_type((), BVoid)
+ f = cast(BFunc5, _testfunc(5))
+ set_errno(50)
+ f()
+ assert get_errno() == 65
+ f(); f()
+ assert get_errno() == 95
+
+def test_errno_callback():
+ if globals().get('PY_DOT_PY') == '2.5':
+ py.test.skip("cannot run this test on py.py with Python 2.5")
+ def cb():
+ e = get_errno()
+ set_errno(e - 6)
+ BVoid = new_void_type()
+ BFunc5 = new_function_type((), BVoid)
+ f = callback(BFunc5, cb)
+ f()
+ assert get_errno() == 89
+ f(); f()
+ assert get_errno() == 77
+
+def test_abi():
+ assert isinstance(FFI_DEFAULT_ABI, int)
+
+def test_cast_to_array():
+ # not valid in C! extension to get a non-owning <cdata 'int[3]'>
+ BInt = new_primitive_type("int")
+ BIntP = new_pointer_type(BInt)
+ BArray = new_array_type(BIntP, 3)
+ x = cast(BArray, 0)
+ assert repr(x) == "<cdata 'int[3]' NULL>"
+
+def test_cast_invalid():
+ BStruct = new_struct_type("foo")
+ complete_struct_or_union(BStruct, [])
+ p = cast(new_pointer_type(BStruct), 123456)
+ s = p[0]
+ py.test.raises(TypeError, cast, BStruct, s)
+
+def test_bug_float_convertion():
+ BDouble = new_primitive_type("double")
+ BDoubleP = new_pointer_type(BDouble)
+ py.test.raises(TypeError, newp, BDoubleP, "foobar")
+
+def test_bug_delitem():
+ BChar = new_primitive_type("char")
+ BCharP = new_pointer_type(BChar)
+ x = newp(BCharP)
+ py.test.raises(TypeError, "del x[0]")
+
+def test_bug_delattr():
+ BLong = new_primitive_type("long")
+ BStruct = new_struct_type("foo")
+ complete_struct_or_union(BStruct, [('a1', BLong, -1)])
+ x = newp(new_pointer_type(BStruct))
+ py.test.raises(AttributeError, "del x.a1")
+
+def test_variable_length_struct():
+ py.test.skip("later")
+ BLong = new_primitive_type("long")
+ BArray = new_array_type(new_pointer_type(BLong), None)
+ BStruct = new_struct_type("foo")
+ BStructP = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('a1', BLong, -1),
+ ('a2', BArray, -1)])
+ assert sizeof(BStruct) == size_of_long()
+ assert alignof(BStruct) == alignof(BLong)
+ #
+ py.test.raises(TypeError, newp, BStructP, None)
+ x = newp(BStructP, 5)
+ assert sizeof(x) == 6 * size_of_long()
+ x[4] = 123
+ assert x[4] == 123
+ py.test.raises(IndexError, "x[5]")
+ assert len(x.a2) == 5
+ #
+ py.test.raises(TypeError, newp, BStructP, [123])
+ x = newp(BStructP, [123, 5])
+ assert x.a1 == 123
+ assert len(x.a2) == 5
+ assert list(x.a2) == [0] * 5
+ #
+ x = newp(BStructP, {'a2': 5})
+ assert x.a1 == 0
+ assert len(x.a2) == 5
+ assert list(x.a2) == [0] * 5
+ #
+ x = newp(BStructP, [123, (4, 5)])
+ assert x.a1 == 123
+ assert len(x.a2) == 2
+ assert list(x.a2) == [4, 5]
+ #
+ x = newp(BStructP, {'a2': (4, 5)})
+ assert x.a1 == 0
+ assert len(x.a2) == 2
+ assert list(x.a2) == [4, 5]
+
+def test_autocast_int():
+ BInt = new_primitive_type("int")
+ BIntPtr = new_pointer_type(BInt)
+ BLongLong = new_primitive_type("long long")
+ BULongLong = new_primitive_type("unsigned long long")
+ BULongLongPtr = new_pointer_type(BULongLong)
+ x = newp(BIntPtr, cast(BInt, 42))
+ assert x[0] == 42
+ x = newp(BIntPtr, cast(BLongLong, 42))
+ assert x[0] == 42
+ x = newp(BIntPtr, cast(BULongLong, 42))
+ assert x[0] == 42
+ x = newp(BULongLongPtr, cast(BInt, 42))
+ assert x[0] == 42
+ py.test.raises(OverflowError, newp, BULongLongPtr, cast(BInt, -42))
+ x = cast(BInt, cast(BInt, 42))
+ assert int(x) == 42
+ x = cast(BInt, cast(BLongLong, 42))
+ assert int(x) == 42
+ x = cast(BInt, cast(BULongLong, 42))
+ assert int(x) == 42
+ x = cast(BULongLong, cast(BInt, 42))
+ assert int(x) == 42
+ x = cast(BULongLong, cast(BInt, -42))
+ assert int(x) == 2 ** 64 - 42
+ x = cast(BIntPtr, cast(BInt, 42))
+ assert int(cast(BInt, x)) == 42
+
+def test_autocast_float():
+ BFloat = new_primitive_type("float")
+ BDouble = new_primitive_type("float")
+ BFloatPtr = new_pointer_type(BFloat)
+ x = newp(BFloatPtr, cast(BDouble, 12.5))
+ assert x[0] == 12.5
+ x = cast(BFloat, cast(BDouble, 12.5))
+ assert float(x) == 12.5
+
+def test_longdouble():
+ py_py = 'PY_DOT_PY' in globals()
+ BLongDouble = new_primitive_type("long double")
+ BLongDoublePtr = new_pointer_type(BLongDouble)
+ BLongDoubleArray = new_array_type(BLongDoublePtr, None)
+ a = newp(BLongDoubleArray, 1)
+ x = a[0]
+ if not py_py:
+ assert repr(x).startswith("<cdata 'long double' 0.0")
+ assert float(x) == 0.0
+ assert int(x) == 0
+ #
+ b = newp(BLongDoubleArray, [1.23])
+ x = b[0]
+ if not py_py:
+ assert repr(x).startswith("<cdata 'long double' 1.23")
+ assert float(x) == 1.23
+ assert int(x) == 1
+ #
+ BFunc19 = new_function_type((BLongDouble,), BLongDouble)
+ f = cast(BFunc19, _testfunc(19))
+ start = 8
+ for i in range(107):
+ start = f(start)
+ if sizeof(BLongDouble) > sizeof(new_primitive_type("double")):
+ if not py_py:
+ assert repr(start).startswith("<cdata 'long double' 6.15")
+ assert repr(start).endswith("E+902>")
+ #
+ c = newp(BLongDoubleArray, [start])
+ x = c[0]
+ if not py_py:
+ assert repr(x).endswith("E+902>")
+ assert float(x) == float("inf")
+
+def test_get_array_of_length_zero():
+ for length in [0, 5, 10]:
+ BLong = new_primitive_type("long")
+ BLongP = new_pointer_type(BLong)
+ BArray0 = new_array_type(BLongP, length)
+ BStruct = new_struct_type("foo")
+ BStructPtr = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('a1', BArray0, -1)])
+ p = newp(BStructPtr, None)
+ if length == 0:
+ assert repr(p.a1).startswith("<cdata 'long *' 0x")
+ else:
+ assert repr(p.a1).startswith("<cdata 'long[%d]' 0x" % length)
diff --git a/pypy/module/_cffi_backend/test/_test_lib.c b/pypy/module/_cffi_backend/test/_test_lib.c
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/test/_test_lib.c
@@ -0,0 +1,172 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+
+static char _testfunc0(char a, char b)
+{
+ return a + b;
+}
+static long _testfunc1(int a, long b)
+{
+ return (long)a + b;
+}
+static long long _testfunc2(long long a, long long b)
+{
+ return a + b;
+}
+static double _testfunc3(float a, double b)
+{
+ return a + b;
+}
+static float _testfunc4(float a, double b)
+{
+ return (float)(a + b);
+}
+static void _testfunc5(void)
+{
+ errno = errno + 15;
+}
+static int *_testfunc6(int *x)
+{
+ static int y;
+ y = *x - 1000;
+ return &y;
+}
+struct _testfunc7_s { unsigned char a1; short a2; };
+static short _testfunc7(struct _testfunc7_s inlined)
+{
+ return inlined.a1 + inlined.a2;
+}
+static int _testfunc9(int num, ...)
+{
+ va_list vargs;
+ int i, total = 0;
+ va_start(vargs, num);
+ for (i=0; i<num; i++) {
+ int value = va_arg(vargs, int);
+ if (value == 0)
+ value = -66666666;
+ total += value;
+ }
+ va_end(vargs);
+ return total;
+}
+
+static struct _testfunc7_s _testfunc10(int n)
+{
+ struct _testfunc7_s result;
+ result.a1 = n;
+ result.a2 = n * n;
+ return result;
+}
+
+struct _testfunc11_s { int a1, a2; };
+static struct _testfunc11_s _testfunc11(int n)
+{
+ struct _testfunc11_s result;
+ result.a1 = n;
+ result.a2 = n * n;
+ return result;
+}
+
+struct _testfunc12_s { double a1; };
+static struct _testfunc12_s _testfunc12(int n)
+{
+ struct _testfunc12_s result;
+ result.a1 = n;
+ return result;
+}
+
+struct _testfunc13_s { int a1, a2, a3; };
+static struct _testfunc13_s _testfunc13(int n)
+{
+ struct _testfunc13_s result;
+ result.a1 = n;
+ result.a2 = n * n;
+ result.a3 = n * n * n;
+ return result;
+}
+
+struct _testfunc14_s { float a1; };
+static struct _testfunc14_s _testfunc14(int n)
+{
+ struct _testfunc14_s result;
+ result.a1 = (float)n;
+ return result;
+}
+
+struct _testfunc15_s { float a1; int a2; };
+static struct _testfunc15_s _testfunc15(int n)
+{
+ struct _testfunc15_s result;
+ result.a1 = (float)n;
+ result.a2 = n * n;
+ return result;
+}
+
+struct _testfunc16_s { float a1, a2; };
+static struct _testfunc16_s _testfunc16(int n)
+{
+ struct _testfunc16_s result;
+ result.a1 = (float)n;
+ result.a2 = -(float)n;
+ return result;
+}
+
+struct _testfunc17_s { int a1; float a2; };
+static struct _testfunc17_s _testfunc17(int n)
+{
+ struct _testfunc17_s result;
+ result.a1 = n;
+ result.a2 = (float)n * (float)n;
+ return result;
+}
+
+static int _testfunc18(struct _testfunc17_s *ptr)
+{
+ return ptr->a1 + (int)ptr->a2;
+}
+
+static long double _testfunc19(long double x)
+{
+ int i;
+ for (i=0; i<28; i++)
+ x += x;
+ return x;
+}
+
+static short _testfunc20(struct _testfunc7_s *ptr)
+{
+ return ptr->a1 + ptr->a2;
+}
+
+void *gettestfunc(int num)
+{
+ void *f;
+ switch (num) {
+ case 0: f = &_testfunc0; break;
+ case 1: f = &_testfunc1; break;
+ case 2: f = &_testfunc2; break;
+ case 3: f = &_testfunc3; break;
+ case 4: f = &_testfunc4; break;
+ case 5: f = &_testfunc5; break;
+ case 6: f = &_testfunc6; break;
+ case 7: f = &_testfunc7; break;
+ case 8: f = stderr; break;
+ case 9: f = &_testfunc9; break;
+ case 10: f = &_testfunc10; break;
+ case 11: f = &_testfunc11; break;
+ case 12: f = &_testfunc12; break;
+ case 13: f = &_testfunc13; break;
+ case 14: f = &_testfunc14; break;
+ case 15: f = &_testfunc15; break;
+ case 16: f = &_testfunc16; break;
+ case 17: f = &_testfunc17; break;
+ case 18: f = &_testfunc18; break;
+ case 19: f = &_testfunc19; break;
+ case 20: f = &_testfunc20; break;
+ default:
+ return NULL;
+ }
+ return f;
+}
diff --git a/pypy/module/_cffi_backend/test/test_c.py b/pypy/module/_cffi_backend/test/test_c.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/test/test_c.py
@@ -0,0 +1,107 @@
+from __future__ import with_statement
+"""
+This file is OBSCURE. Really. The purpose is to avoid copying and changing
+'test_c.py' from cffi/c/.
+"""
+import py, sys, ctypes
+if sys.version_info < (2, 6):
+ py.test.skip("requires the b'' literal syntax")
+
+from pypy.tool.udir import udir
+from pypy.conftest import gettestobjspace, option
+from pypy.interpreter import gateway
+from pypy.module._cffi_backend.test import _backend_test_c
+from pypy.module._cffi_backend import Module
+from pypy.translator.platform import host
+from pypy.translator.tool.cbuild import ExternalCompilationInfo
+
+
+class AppTestC(object):
+ """Populated below, hack hack hack."""
+
+ def setup_class(cls):
+ space = gettestobjspace(usemodules=('_cffi_backend',))
+ cls.space = space
+ testfuncs_w = []
+ keepalive_funcs = []
+
+ def find_and_load_library_for_test(space, w_name, w_is_global=0):
+ if space.is_w(w_name, space.w_None):
+ path = None
+ else:
+ import ctypes.util
+ path = ctypes.util.find_library(space.str_w(w_name))
+ return space.appexec([space.wrap(path), w_is_global],
+ """(path, is_global):
+ import _cffi_backend
+ return _cffi_backend.load_library(path, is_global)""")
+
+ test_lib_c = tmpdir.join('_test_lib.c')
+ src_test_lib_c = py.path.local(__file__).dirpath().join('_test_lib.c')
+ src_test_lib_c.copy(test_lib_c)
+ eci = ExternalCompilationInfo()
+ test_lib = host.compile([test_lib_c], eci, standalone=False)
+
+ cdll = ctypes.CDLL(str(test_lib))
+ cdll.gettestfunc.restype = ctypes.c_void_p
+
+ def testfunc_for_test(space, w_num):
+ if hasattr(space, 'int_w'):
+ w_num = space.int_w(w_num)
+ addr = cdll.gettestfunc(w_num)
+ return space.wrap(addr)
+
+ if option.runappdirect:
+ def interp2app(func):
+ def run(*args):
+ return func(space, *args)
+ return run
+ else:
+ interp2app = gateway.interp2app
+
+ w_func = space.wrap(interp2app(find_and_load_library_for_test))
+ w_testfunc = space.wrap(interp2app(testfunc_for_test))
+ space.appexec([space.wrap(str(tmpdir)), w_func, w_testfunc,
+ space.wrap(sys.version[:3])],
+ """(path, func, testfunc, underlying_version):
+ import sys
+ sys.path.append(path)
+ import _all_test_c
+ _all_test_c.PY_DOT_PY = underlying_version
+ _all_test_c.find_and_load_library = func
+ _all_test_c._testfunc = testfunc
+ """)
+
+
+all_names = ', '.join(Module.interpleveldefs.keys())
+
+lst = []
+for name, value in _backend_test_c.__dict__.items():
+ if name.startswith('test_'):
+ lst.append(value)
+lst.sort(key=lambda func: func.func_code.co_firstlineno)
+
+tmpdir = udir.join('test_c').ensure(dir=1)
+
+tmpname = tmpdir.join('_test_c.py')
+with tmpname.open('w') as f:
+ for func in lst:
+ print >> f, 'def %s(self):' % (func.__name__,)
+ print >> f, ' import _all_test_c'
+ print >> f, ' _all_test_c.%s()' % (func.__name__,)
+
+tmpname2 = tmpdir.join('_all_test_c.py')
+with tmpname2.open('w') as f:
+ print >> f, 'import sys'
+ print >> f, 'from _cffi_backend import %s' % all_names
+ print >> f, 'class py:'
+ print >> f, ' class test:'
+ print >> f, ' raises = staticmethod(raises)'
+ print >> f, ' skip = staticmethod(skip)'
+ print >> f, py.path.local(__file__).join('..', '_backend_test_c.py').read()
+
+
+mod = tmpname.pyimport()
+for key, value in mod.__dict__.items():
+ if key.startswith('test_'):
+ setattr(AppTestC, key, value)
diff --git a/pypy/module/_cffi_backend/test/test_file.py b/pypy/module/_cffi_backend/test/test_file.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/test/test_file.py
@@ -0,0 +1,13 @@
+import urllib2, py
+
+
+def test_same_file():
+ # '_backend_test_c.py' is a copy of 'c/test_c.py' from the CFFI repo,
+ # with the header lines (up to '# _____') stripped.
+ url = 'https://bitbucket.org/cffi/cffi/raw/default/c/test_c.py'
+ source = urllib2.urlopen(url).read()
+ #
+ dest = py.path.local(__file__).join('..', '_backend_test_c.py').read()
+ #
+ source = source[source.index('# _____________'):]
+ assert source == dest
diff --git a/pypy/module/_cffi_backend/test/test_ztranslation.py b/pypy/module/_cffi_backend/test/test_ztranslation.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/test/test_ztranslation.py
@@ -0,0 +1,8 @@
+from pypy.objspace.fake.checkmodule import checkmodule
+
+# side-effect: FORMAT_LONGDOUBLE must be built before test_checkmodule()
+from pypy.module._cffi_backend import misc
+
+
+def test_checkmodule():
+ checkmodule('_cffi_backend')
diff --git a/pypy/module/_hashlib/interp_hashlib.py b/pypy/module/_hashlib/interp_hashlib.py
--- a/pypy/module/_hashlib/interp_hashlib.py
+++ b/pypy/module/_hashlib/interp_hashlib.py
@@ -96,6 +96,9 @@
block_size = rffi.getintfield(digest_type, 'c_block_size')
return space.wrap(block_size)
+ def get_name(self, space):
+ return space.wrap(self.name)
+
def _digest(self, space):
with lltype.scoped_alloc(ropenssl.EVP_MD_CTX.TO) as ctx:
with self.lock:
@@ -118,6 +121,7 @@
digest_size=GetSetProperty(W_Hash.get_digest_size),
digestsize=GetSetProperty(W_Hash.get_digest_size),
block_size=GetSetProperty(W_Hash.get_block_size),
+ name=GetSetProperty(W_Hash.get_name),
)
W_Hash.acceptable_as_base_class = False
diff --git a/pypy/module/_hashlib/test/test_hashlib.py b/pypy/module/_hashlib/test/test_hashlib.py
--- a/pypy/module/_hashlib/test/test_hashlib.py
+++ b/pypy/module/_hashlib/test/test_hashlib.py
@@ -20,6 +20,7 @@
'sha512': 64,
}.items():
h = hashlib.new(name)
+ assert h.name == name
assert h.digest_size == expected_size
assert h.digestsize == expected_size
#
diff --git a/pypy/module/_minimal_curses/fficurses.py b/pypy/module/_minimal_curses/fficurses.py
--- a/pypy/module/_minimal_curses/fficurses.py
+++ b/pypy/module/_minimal_curses/fficurses.py
@@ -9,10 +9,12 @@
from pypy.module._minimal_curses import interp_curses
from pypy.translator.tool.cbuild import ExternalCompilationInfo
from sys import platform
+import os.path
_CYGWIN = platform == 'cygwin'
+_NCURSES_CURSES = os.path.isfile("/usr/include/ncurses/curses.h")
-if _CYGWIN:
+if _CYGWIN or _NCURSES_CURSES:
eci = ExternalCompilationInfo(
includes = ['ncurses/curses.h', 'ncurses/term.h'],
libraries = ['curses'],
diff --git a/pypy/module/imp/importing.py b/pypy/module/imp/importing.py
--- a/pypy/module/imp/importing.py
+++ b/pypy/module/imp/importing.py
@@ -602,8 +602,10 @@
try:
if find_info.modtype == PY_SOURCE:
- load_source_module(space, w_modulename, w_mod, find_info.filename,
- find_info.stream.readall())
+ load_source_module(
+ space, w_modulename, w_mod,
+ find_info.filename, find_info.stream.readall(),
+ find_info.stream.try_to_find_file_descriptor())
return w_mod
elif find_info.modtype == PY_COMPILED:
magic = _r_long(find_info.stream)
@@ -878,7 +880,7 @@
@jit.dont_look_inside
-def load_source_module(space, w_modulename, w_mod, pathname, source,
+def load_source_module(space, w_modulename, w_mod, pathname, source, fd,
write_pyc=True):
"""
Load a source module from a given file and return its module
@@ -887,8 +889,8 @@
w = space.wrap
if space.config.objspace.usepycfiles:
+ src_stat = os.fstat(fd)
cpathname = pathname + 'c'
- src_stat = os.stat(pathname)
mtime = int(src_stat[stat.ST_MTIME])
mode = src_stat[stat.ST_MODE]
stream = check_compiled_module(space, cpathname, mtime)
diff --git a/pypy/module/imp/interp_imp.py b/pypy/module/imp/interp_imp.py
--- a/pypy/module/imp/interp_imp.py
+++ b/pypy/module/imp/interp_imp.py
@@ -101,7 +101,8 @@
importing._prepare_module(space, w_mod, filename, None)
importing.load_source_module(
- space, w_modulename, w_mod, filename, stream.readall())
+ space, w_modulename, w_mod,
+ filename, stream.readall(), stream.try_to_find_file_descriptor())
if space.is_w(w_file, space.w_None):
stream.close()
return w_mod
diff --git a/pypy/module/imp/test/test_import.py b/pypy/module/imp/test/test_import.py
--- a/pypy/module/imp/test/test_import.py
+++ b/pypy/module/imp/test/test_import.py
@@ -104,11 +104,10 @@
filename = str(p.join("x.py"))
stream = streamio.open_file_as_stream(filename, "r")
try:
- importing.load_source_module(space,
- w_modname,
- w(importing.Module(space, w_modname)),
- filename,
- stream.readall())
+ importing.load_source_module(
+ space, w_modname, w(importing.Module(space, w_modname)),
+ filename, stream.readall(),
+ stream.try_to_find_file_descriptor())
finally:
stream.close()
if space.config.objspace.usepycfiles:
@@ -618,6 +617,19 @@
sys.path.insert(0, sys.path.pop())
del sys.modules['itertools']
+ def test_invalid_pathname(self):
+ import imp
+ import pkg
+ import os
+
+ info = ('.py', 'r', imp.PY_SOURCE)
+ pathname = os.path.join(os.path.dirname(pkg.__file__), 'a.py')
+
+ module = imp.load_module('a', open(pathname),
+ 'invalid_path_name', ('.py', 'r', imp.PY_SOURCE))
+ assert module.__name__ == 'a'
+ assert module.__file__ == 'invalid_path_name'
+
class TestAbi:
def test_abi_tag(self):
@@ -783,11 +795,10 @@
pathname = _testfilesource()
stream = streamio.open_file_as_stream(pathname, "r")
try:
- w_ret = importing.load_source_module(space,
- w_modulename,
- w_mod,
- pathname,
- stream.readall())
+ w_ret = importing.load_source_module(
+ space, w_modulename, w_mod,
+ pathname, stream.readall(),
+ stream.try_to_find_file_descriptor())
finally:
stream.close()
assert w_mod is w_ret
@@ -806,12 +817,11 @@
pathname = _testfilesource()
stream = streamio.open_file_as_stream(pathname, "r")
try:
- w_ret = importing.load_source_module(space,
- w_modulename,
- w_mod,
- pathname,
- stream.readall(),
- write_pyc=False)
+ w_ret = importing.load_source_module(
+ space, w_modulename, w_mod,
+ pathname, stream.readall(),
+ stream.try_to_find_file_descriptor(),
+ write_pyc=False)
finally:
stream.close()
cpathname = udir.join('test.pyc')
@@ -826,11 +836,10 @@
try:
space.setattr(space.sys, space.wrap('dont_write_bytecode'),
space.w_True)
- w_ret = importing.load_source_module(space,
- w_modulename,
- w_mod,
- pathname,
- stream.readall())
+ w_ret = importing.load_source_module(
+ space, w_modulename, w_mod,
+ pathname, stream.readall(),
+ stream.try_to_find_file_descriptor())
finally:
space.setattr(space.sys, space.wrap('dont_write_bytecode'),
space.w_False)
@@ -846,11 +855,10 @@
pathname = _testfilesource(source="<Syntax Error>")
stream = streamio.open_file_as_stream(pathname, "r")
try:
- w_ret = importing.load_source_module(space,
- w_modulename,
- w_mod,
- pathname,
- stream.readall())
+ w_ret = importing.load_source_module(
+ space, w_modulename, w_mod,
+ pathname, stream.readall(),
+ stream.try_to_find_file_descriptor())
except OperationError:
# OperationError("Syntax Error")
pass
@@ -867,11 +875,10 @@
pathname = _testfilesource(source="a = unknown_name")
stream = streamio.open_file_as_stream(pathname, "r")
try:
- w_ret = importing.load_source_module(space,
- w_modulename,
- w_mod,
- pathname,
- stream.readall())
+ w_ret = importing.load_source_module(
+ space, w_modulename, w_mod,
+ pathname, stream.readall(),
+ stream.try_to_find_file_descriptor())
except OperationError:
# OperationError("NameError", "global name 'unknown_name' is not defined")
pass
diff --git a/pypy/module/micronumpy/interp_boxes.py b/pypy/module/micronumpy/interp_boxes.py
--- a/pypy/module/micronumpy/interp_boxes.py
+++ b/pypy/module/micronumpy/interp_boxes.py
@@ -229,7 +229,7 @@
except KeyError:
raise OperationError(space.w_IndexError,
space.wrap("Field %s does not exist" % item))
- return dtype.itemtype.read(self.arr, 1, self.ofs, ofs, dtype)
+ return dtype.itemtype.read(self.arr, self.ofs, ofs, dtype)
@unwrap_spec(item=str)
def descr_setitem(self, space, item, w_value):
@@ -238,7 +238,7 @@
except KeyError:
raise OperationError(space.w_IndexError,
space.wrap("Field %s does not exist" % item))
- dtype.itemtype.store(self.arr, 1, self.ofs, ofs,
+ dtype.itemtype.store(self.arr, self.ofs, ofs,
dtype.coerce(space, w_value))
class W_CharacterBox(W_FlexibleBox):
diff --git a/pypy/module/micronumpy/interp_dtype.py b/pypy/module/micronumpy/interp_dtype.py
--- a/pypy/module/micronumpy/interp_dtype.py
+++ b/pypy/module/micronumpy/interp_dtype.py
@@ -44,13 +44,13 @@
return self.itemtype.coerce(space, self, w_item)
def getitem(self, arr, i):
- return self.itemtype.read(arr, 1, i, 0)
+ return self.itemtype.read(arr, i, 0)
def getitem_bool(self, arr, i):
- return self.itemtype.read_bool(arr, 1, i, 0)
+ return self.itemtype.read_bool(arr, i, 0)
def setitem(self, arr, i, box):
- self.itemtype.store(arr, 1, i, 0, box)
+ self.itemtype.store(arr, i, 0, box)
def fill(self, storage, box, start, stop):
self.itemtype.fill(storage, self.get_size(), box, start, stop, 0)
diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py
--- a/pypy/module/micronumpy/interp_numarray.py
+++ b/pypy/module/micronumpy/interp_numarray.py
@@ -13,11 +13,11 @@
find_shape_and_elems, get_shape_from_iterable, calc_new_strides, to_coords)
from pypy.rlib import jit
from pypy.rlib.rstring import StringBuilder
+from pypy.rlib.rawstorage import free_raw_storage
from pypy.rpython.lltypesystem import lltype, rffi
from pypy.tool.sourcetools import func_with_new_name
from pypy.module.micronumpy.interp_support import unwrap_axis_arg
-
count_driver = jit.JitDriver(
greens=['shapelen'],
virtualizables=['frame'],
@@ -1209,7 +1209,7 @@
return signature.ArraySignature(self.dtype)
def __del__(self):
- lltype.free(self.storage, flavor='raw', track_allocation=False)
+ free_raw_storage(self.storage, track_allocation=False)
def _find_shape(space, w_size):
if space.isinstance_w(w_size, space.w_int):
diff --git a/pypy/module/micronumpy/test/test_zjit.py b/pypy/module/micronumpy/test/test_zjit.py
--- a/pypy/module/micronumpy/test/test_zjit.py
+++ b/pypy/module/micronumpy/test/test_zjit.py
@@ -83,8 +83,8 @@
def test_add(self):
result = self.run("add")
- self.check_simple_loop({'getinteriorfield_raw': 2, 'float_add': 1,
- 'setinteriorfield_raw': 1, 'int_add': 1,
+ self.check_simple_loop({'raw_load': 2, 'float_add': 1,
+ 'raw_store': 1, 'int_add': 1,
'int_ge': 1, 'guard_false': 1, 'jump': 1,
'arraylen_gc': 1})
assert result == 3 + 3
@@ -98,8 +98,8 @@
def test_floatadd(self):
result = self.run("float_add")
assert result == 3 + 3
- self.check_simple_loop({"getinteriorfield_raw": 1, "float_add": 1,
- "setinteriorfield_raw": 1, "int_add": 1,
+ self.check_simple_loop({"raw_load": 1, "float_add": 1,
+ "raw_store": 1, "int_add": 1,
"int_ge": 1, "guard_false": 1, "jump": 1,
'arraylen_gc': 1})
@@ -113,7 +113,7 @@
def test_sum(self):
result = self.run("sum")
assert result == 2 * sum(range(30))
- self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 2,
+ self.check_simple_loop({"raw_load": 2, "float_add": 2,
"int_add": 1, "int_ge": 1, "guard_false": 1,
"jump": 1, 'arraylen_gc': 1})
@@ -129,8 +129,8 @@
assert result == 30
# XXX note - the bridge here is fairly crucial and yet it's pretty
# bogus. We need to improve the situation somehow.
- self.check_simple_loop({'getinteriorfield_raw': 2,
- 'setinteriorfield_raw': 1,
+ self.check_simple_loop({'raw_load': 2,
+ 'raw_store': 1,
'arraylen_gc': 2,
'guard_true': 1,
'int_lt': 1,
@@ -152,7 +152,7 @@
for i in range(30):
expected *= i * 2
assert result == expected
- self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 1,
+ self.check_simple_loop({"raw_load": 2, "float_add": 1,
"float_mul": 1, "int_add": 1,
"int_ge": 1, "guard_false": 1, "jump": 1,
'arraylen_gc': 1})
@@ -169,7 +169,7 @@
result = self.run("max")
assert result == 256
py.test.skip("not there yet, getting though")
- self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 1,
+ self.check_simple_loop({"raw_load": 2, "float_add": 1,
"float_mul": 1, "int_add": 1,
"int_lt": 1, "guard_true": 1, "jump": 1})
@@ -182,7 +182,7 @@
min(b)
""")
assert result == -24
- self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 1,
+ self.check_simple_loop({"raw_load": 2, "float_add": 1,
"float_mul": 1, "int_add": 1,
"int_lt": 1, "guard_true": 1, "jump": 1})
@@ -197,7 +197,7 @@
def test_any(self):
result = self.run("any")
assert result == 1
- self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 1,
+ self.check_simple_loop({"raw_load": 2, "float_add": 1,
"int_and": 1, "int_add": 1,
'cast_float_to_int': 1,
"int_ge": 1, "jump": 1,
@@ -219,12 +219,12 @@
# optimization then you end up with 2 float_adds, so we can still be
# sure it was optimized correctly.
py.test.skip("too fragile")
- self.check_resops({'setinteriorfield_raw': 4, 'getfield_gc': 22,
+ self.check_resops({'raw_store': 4, 'getfield_gc': 22,
'getarrayitem_gc': 4, 'getarrayitem_gc_pure': 2,
'getfield_gc_pure': 8,
'guard_class': 8, 'int_add': 8, 'float_mul': 2,
'jump': 2, 'int_ge': 4,
- 'getinteriorfield_raw': 4, 'float_add': 2,
+ 'raw_load': 4, 'float_add': 2,
'guard_false': 4, 'arraylen_gc': 2, 'same_as': 2})
def define_ufunc():
@@ -238,9 +238,9 @@
def test_ufunc(self):
result = self.run("ufunc")
assert result == -6
- self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 1,
+ self.check_simple_loop({"raw_load": 2, "float_add": 1,
"float_neg": 1,
- "setinteriorfield_raw": 1, "int_add": 1,
+ "raw_store": 1, "int_add": 1,
"int_ge": 1, "guard_false": 1, "jump": 1,
'arraylen_gc': 1})
@@ -280,9 +280,9 @@
def test_slice(self):
result = self.run("slice")
assert result == 18
- self.check_simple_loop({'getinteriorfield_raw': 2,
+ self.check_simple_loop({'raw_load': 2,
'float_add': 1,
- 'setinteriorfield_raw': 1,
+ 'raw_store': 1,
'int_add': 3,
'int_ge': 1, 'guard_false': 1,
'jump': 1,
@@ -298,12 +298,12 @@
def test_take(self):
result = self.run("take")
assert result == 3
- self.check_simple_loop({'getinteriorfield_raw': 2,
+ self.check_simple_loop({'raw_load': 2,
'cast_float_to_int': 1,
'int_lt': 1,
'int_ge': 2,
'guard_false': 3,
- 'setinteriorfield_raw': 1,
+ 'raw_store': 1,
'int_mul': 1,
'int_add': 3,
'jump': 1,
@@ -321,9 +321,9 @@
assert result == 8
# int_add might be 1 here if we try slightly harder with
# reusing indexes or some optimization
- self.check_simple_loop({'float_add': 1, 'getinteriorfield_raw': 2,
+ self.check_simple_loop({'float_add': 1, 'raw_load': 2,
'guard_false': 1, 'int_add': 1, 'int_ge': 1,
- 'jump': 1, 'setinteriorfield_raw': 1,
+ 'jump': 1, 'raw_store': 1,
'arraylen_gc': 1})
def define_multidim_slice():
@@ -370,8 +370,8 @@
result = self.run("setslice")
assert result == 11.0
self.check_trace_count(1)
- self.check_simple_loop({'getinteriorfield_raw': 2, 'float_add': 1,
- 'setinteriorfield_raw': 1, 'int_add': 2,
+ self.check_simple_loop({'raw_load': 2, 'float_add': 1,
+ 'raw_store': 1, 'int_add': 2,
'int_eq': 1, 'guard_false': 1, 'jump': 1,
'arraylen_gc': 1})
@@ -387,8 +387,8 @@
result = self.run("virtual_slice")
assert result == 4
self.check_trace_count(1)
- self.check_simple_loop({'getinteriorfield_raw': 2, 'float_add': 1,
- 'setinteriorfield_raw': 1, 'int_add': 1,
+ self.check_simple_loop({'raw_load': 2, 'float_add': 1,
+ 'raw_store': 1, 'int_add': 1,
'int_ge': 1, 'guard_false': 1, 'jump': 1,
'arraylen_gc': 1})
def define_flat_iter():
@@ -403,8 +403,8 @@
result = self.run("flat_iter")
assert result == 6
self.check_trace_count(1)
- self.check_simple_loop({'getinteriorfield_raw': 2, 'float_add': 1,
- 'setinteriorfield_raw': 1, 'int_add': 2,
+ self.check_simple_loop({'raw_load': 2, 'float_add': 1,
+ 'raw_store': 1, 'int_add': 2,
'int_ge': 1, 'guard_false': 1,
'arraylen_gc': 1, 'jump': 1})
@@ -419,8 +419,8 @@
result = self.run("flat_getitem")
assert result == 10.0
self.check_trace_count(1)
- self.check_simple_loop({'getinteriorfield_raw': 1,
- 'setinteriorfield_raw': 1,
+ self.check_simple_loop({'raw_load': 1,
+ 'raw_store': 1,
'int_lt': 1,
'int_ge': 1,
'int_add': 3,
@@ -442,8 +442,8 @@
assert result == 1.0
self.check_trace_count(1)
# XXX not ideal, but hey, let's ignore it for now
- self.check_simple_loop({'getinteriorfield_raw': 1,
- 'setinteriorfield_raw': 1,
+ self.check_simple_loop({'raw_load': 1,
+ 'raw_store': 1,
'int_lt': 1,
'int_gt': 1,
'int_add': 4,
@@ -471,14 +471,14 @@
self.check_simple_loop({'arraylen_gc': 9,
'float_add': 1,
'float_mul': 1,
- 'getinteriorfield_raw': 3,
+ 'raw_load': 3,
'guard_false': 3,
'guard_true': 3,
'int_add': 6,
'int_lt': 6,
'int_sub': 3,
'jump': 1,
- 'setinteriorfield_raw': 1})
+ 'raw_store': 1})
def define_count_nonzero():
return """
@@ -490,7 +490,7 @@
result = self.run("count_nonzero")
assert result == 9
self.check_simple_loop({'setfield_gc': 3,
- 'getinteriorfield_raw': 1,
+ 'raw_load': 1,
'guard_false': 1,
'jump': 1,
'int_ge': 1,
diff --git a/pypy/module/micronumpy/types.py b/pypy/module/micronumpy/types.py
--- a/pypy/module/micronumpy/types.py
+++ b/pypy/module/micronumpy/types.py
@@ -5,7 +5,9 @@
from pypy.interpreter.error import OperationError
from pypy.module.micronumpy import interp_boxes
from pypy.objspace.std.floatobject import float2string
-from pypy.rlib import rfloat, libffi, clibffi
+from pypy.rlib import rfloat, clibffi
+from pypy.rlib.rawstorage import (alloc_raw_storage, raw_storage_setitem,
+ raw_storage_getitem)
from pypy.rlib.objectmodel import specialize, we_are_translated
from pypy.rlib.rarithmetic import widen, byteswap
from pypy.rpython.lltypesystem import lltype, rffi
@@ -14,8 +16,6 @@
from pypy.rlib import jit
-VOID_STORAGE = lltype.Array(lltype.Char, hints={'nolength': True,
- 'render_as_void': True})
degToRad = math.pi / 180.0
log2 = math.log(2)
log2e = 1. / log2
@@ -73,10 +73,7 @@
raise NotImplementedError
def malloc(self, size):
- # XXX find out why test_zjit explodes with tracking of allocations
- return lltype.malloc(VOID_STORAGE, size,
- zero=True, flavor="raw",
- track_allocation=False, add_memory_pressure=True)
+ return alloc_raw_storage(size, track_allocation=False, zero=True)
def __repr__(self):
return self.__class__.__name__
@@ -116,34 +113,25 @@
def default_fromstring(self, space):
raise NotImplementedError
- def _read(self, storage, width, i, offset):
- if we_are_translated():
- return libffi.array_getitem(clibffi.cast_type_to_ffitype(self.T),
- width, storage, i, offset)
- else:
- return libffi.array_getitem_T(self.T, width, storage, i, offset)
+ def _read(self, storage, i, offset):
+ return raw_storage_getitem(self.T, storage, i + offset)
- def read(self, arr, width, i, offset, dtype=None):
- return self.box(self._read(arr.storage, width, i, offset))
+ def read(self, arr, i, offset, dtype=None):
+ return self.box(self._read(arr.storage, i, offset))
- def read_bool(self, arr, width, i, offset):
- return bool(self.for_computation(self._read(arr.storage, width, i, offset)))
+ def read_bool(self, arr, i, offset):
+ return bool(self.for_computation(self._read(arr.storage, i, offset)))
- def _write(self, storage, width, i, offset, value):
- if we_are_translated():
- libffi.array_setitem(clibffi.cast_type_to_ffitype(self.T),
- width, storage, i, offset, value)
- else:
- libffi.array_setitem_T(self.T, width, storage, i, offset, value)
+ def _write(self, storage, i, offset, value):
+ raw_storage_setitem(storage, i + offset, value)
-
- def store(self, arr, width, i, offset, box):
- self._write(arr.storage, width, i, offset, self.unbox(box))
+ def store(self, arr, i, offset, box):
+ self._write(arr.storage, i, offset, self.unbox(box))
def fill(self, storage, width, box, start, stop, offset):
value = self.unbox(box)
for i in xrange(start, stop, width):
- self._write(storage, 1, i, offset, value)
+ self._write(storage, i, offset, value)
def runpack_str(self, s):
return self.box(runpack(self.format_code, s))
@@ -245,21 +233,13 @@
class NonNativePrimitive(Primitive):
_mixin_ = True
- def _read(self, storage, width, i, offset):
- if we_are_translated():
- res = libffi.array_getitem(clibffi.cast_type_to_ffitype(self.T),
- width, storage, i, offset)
- else:
- res = libffi.array_getitem_T(self.T, width, storage, i, offset)
+ def _read(self, storage, i, offset):
+ res = raw_storage_getitem(self.T, storage, i + offset)
return byteswap(res)
- def _write(self, storage, width, i, offset, value):
+ def _write(self, storage, i, offset, value):
value = byteswap(value)
- if we_are_translated():
- libffi.array_setitem(clibffi.cast_type_to_ffitype(self.T),
- width, storage, i, offset, value)
- else:
- libffi.array_setitem_T(self.T, width, storage, i, offset, value)
+ raw_storage_setitem(storage, i + offset, value)
def pack_str(self, box):
return struct.pack(self.format_code, byteswap(self.unbox(box)))
@@ -868,22 +848,14 @@
class NonNativeFloat(NonNativePrimitive, Float):
_mixin_ = True
- def _read(self, storage, width, i, offset):
- if we_are_translated():
- res = libffi.array_getitem(clibffi.cast_type_to_ffitype(self.T),
- width, storage, i, offset)
- else:
- res = libffi.array_getitem_T(self.T, width, storage, i, offset)
- #return byteswap(res)
+ def _read(self, storage, i, offset):
+ res = raw_storage_getitem(self.T, storage, i + offset)
+ #return byteswap(res) XXX
return res
- def _write(self, storage, width, i, offset, value):
+ def _write(self, storage, i, offset, value):
#value = byteswap(value) XXX
- if we_are_translated():
- libffi.array_setitem(clibffi.cast_type_to_ffitype(self.T),
- width, storage, i, offset, value)
- else:
- libffi.array_setitem_T(self.T, width, storage, i, offset, value)
+ raw_storage_setitem(storage, i + offset, value)
def pack_str(self, box):
# XXX byteswap
@@ -952,7 +924,7 @@
def get_element_size(self):
return self.size
- def read(self, arr, width, i, offset, dtype=None):
+ def read(self, arr, i, offset, dtype=None):
if dtype is None:
dtype = arr.dtype
return interp_boxes.W_VoidBox(arr, i + offset, dtype)
@@ -980,11 +952,11 @@
ofs, itemtype = self.offsets_and_fields[i]
w_item = items_w[i]
w_box = itemtype.coerce(space, subdtype, w_item)
- itemtype.store(arr, 1, 0, ofs, w_box)
+ itemtype.store(arr, 0, ofs, w_box)
return interp_boxes.W_VoidBox(arr, 0, arr.dtype)
@jit.unroll_safe
- def store(self, arr, _, i, ofs, box):
+ def store(self, arr, i, ofs, box):
assert isinstance(box, interp_boxes.W_VoidBox)
for k in range(self.get_element_size()):
arr.storage[k + i] = box.arr.storage[k + box.ofs]
@@ -999,7 +971,7 @@
first = False
else:
pieces.append(", ")
- pieces.append(tp.str_format(tp.read(box.arr, 1, box.ofs, ofs)))
+ pieces.append(tp.str_format(tp.read(box.arr, box.ofs, ofs)))
pieces.append(")")
return "".join(pieces)
diff --git a/pypy/module/pypyjit/policy.py b/pypy/module/pypyjit/policy.py
--- a/pypy/module/pypyjit/policy.py
+++ b/pypy/module/pypyjit/policy.py
@@ -105,7 +105,8 @@
'imp', 'sys', 'array', '_ffi', 'itertools', 'operator',
'posix', '_socket', '_sre', '_lsprof', '_weakref',
'__pypy__', 'cStringIO', '_collections', 'struct',
- 'mmap', 'marshal', '_codecs', 'rctime', 'cppyy']:
+ 'mmap', 'marshal', '_codecs', 'rctime', 'cppyy',
+ '_cffi_backend']:
if modname == 'pypyjit' and 'interp_resop' in rest:
return False
return True
diff --git a/pypy/module/pypyjit/test_pypy_c/test__ffi.py b/pypy/module/pypyjit/test_pypy_c/test__ffi.py
--- a/pypy/module/pypyjit/test_pypy_c/test__ffi.py
+++ b/pypy/module/pypyjit/test_pypy_c/test__ffi.py
@@ -1,4 +1,4 @@
-import sys
+import sys, py
from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC
class Test__ffi(BaseTestPyPyC):
@@ -27,6 +27,7 @@
log = self.run(main, [libm_name])
pow_addr, res = log.result
assert res == 8.0 * 300
+ py.test.xfail() # XXX re-optimize _ffi for the JIT?
loop, = log.loops_by_filename(self.filepath)
if 'ConstClass(pow)' in repr(loop): # e.g. OS/X
pow_addr = 'ConstClass(pow)'
@@ -134,6 +135,7 @@
ops = loop.allops()
opnames = log.opnames(ops)
assert opnames.count('new_with_vtable') == 1 # only the virtualref
+ py.test.xfail() # XXX re-optimize _ffi for the JIT?
assert opnames.count('call_release_gil') == 1
idx = opnames.index('call_release_gil')
call = ops[idx]
@@ -158,6 +160,7 @@
return struct.getfield('x')
#
log = self.run(main, [])
+ py.test.xfail() # XXX re-optimize _ffi for the JIT?
loop, = log.loops_by_filename(self.filepath)
assert loop.match_by_id('getfield', """
guard_not_invalidated(descr=...)
@@ -167,3 +170,42 @@
setfield_raw(i44, i57, descr=<FieldS dynamic 0>)
""")
+
+ def test__cffi_call(self):
+ from pypy.rlib.test.test_clibffi import get_libm_name
+ def main(libm_name):
+ try:
+ import _cffi_backend
+ except ImportError:
+ sys.stderr.write('SKIP: cannot import _cffi_backend\n')
+ return 0
+
+ libm = _cffi_backend.load_library(libm_name)
+ BDouble = _cffi_backend.new_primitive_type("double")
+ BPow = _cffi_backend.new_function_type([BDouble, BDouble], BDouble)
+ pow = libm.load_function(BPow, 'pow')
+ i = 0
+ res = 0
+ while i < 300:
+ tmp = pow(2, 3) # ID: cfficall
+ res += tmp
+ i += 1
+ BLong = _cffi_backend.new_primitive_type("long")
+ pow_addr = int(_cffi_backend.cast(BLong, pow))
+ return pow_addr, res
+ #
+ libm_name = get_libm_name(sys.platform)
+ log = self.run(main, [libm_name])
+ pow_addr, res = log.result
+ assert res == 8.0 * 300
+ loop, = log.loops_by_filename(self.filepath)
+ if 'ConstClass(pow)' in repr(loop): # e.g. OS/X
+ pow_addr = 'ConstClass(pow)'
+ assert loop.match_by_id('cfficall', """
+ ...
+ f1 = call_release_gil(..., descr=<Callf 8 ff EF=6 OS=62>)
+ ...
+ """)
+ # so far just check that call_release_gil() is produced.
+ # later, also check that the arguments to call_release_gil()
+ # are constants, and that the numerous raw_mallocs are removed
diff --git a/pypy/module/signal/interp_signal.py b/pypy/module/signal/interp_signal.py
--- a/pypy/module/signal/interp_signal.py
+++ b/pypy/module/signal/interp_signal.py
@@ -364,6 +364,15 @@
@jit.dont_look_inside
@unwrap_spec(which=int, first=float, interval=float)
def setitimer(space, which, first, interval=0):
+ """setitimer(which, seconds[, interval])
+
+ Sets given itimer (one of ITIMER_REAL, ITIMER_VIRTUAL
+ or ITIMER_PROF) to fire after value seconds and after
+ that every interval seconds.
+ The itimer can be cleared by setting seconds to zero.
+
+ Returns old values as a tuple: (delay, interval).
+ """
with lltype.scoped_alloc(itimervalP.TO, 1) as new:
timeval_from_double(first, new[0].c_it_value)
@@ -381,6 +390,10 @@
@jit.dont_look_inside
@unwrap_spec(which=int)
def getitimer(space, which):
+ """getitimer(which)
+
+ Returns current value of given itimer.
+ """
with lltype.scoped_alloc(itimervalP.TO, 1) as old:
c_getitimer(which, old)
diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_numbers.py b/pypy/module/test_lib_pypy/ctypes_tests/test_numbers.py
--- a/pypy/module/test_lib_pypy/ctypes_tests/test_numbers.py
+++ b/pypy/module/test_lib_pypy/ctypes_tests/test_numbers.py
@@ -187,6 +187,14 @@
# probably be changed:
raises(TypeError, c_int, c_long(42))
+ def test_subclass(self):
+ class enum(c_int):
+ def __new__(cls, value):
+ dont_call_me
+ class S(Structure):
+ _fields_ = [('t', enum)]
+ assert isinstance(S().t, enum)
+
## def test_perf(self):
## check_perf()
diff --git a/pypy/module/test_lib_pypy/test_greenlet.py b/pypy/module/test_lib_pypy/test_greenlet.py
--- a/pypy/module/test_lib_pypy/test_greenlet.py
+++ b/pypy/module/test_lib_pypy/test_greenlet.py
@@ -134,6 +134,40 @@
res = g1.switch()
assert res == "ok"
+ def test_throw_GreenletExit(self):
+ from greenlet import greenlet
+ gmain = greenlet.getcurrent()
+ l = [0]
+ #
+ def func():
+ l[0] += 1
+ gmain.switch()
+ l[0] += 1
+ #
+ g = greenlet(func)
+ g.switch()
+ assert l[0] == 1
+ g.throw()
+ assert l[0] == 1
+
+ def test_throw_GreenletExit_result(self):
+ from greenlet import greenlet
+ gmain = greenlet.getcurrent()
+ l = [0]
+ #
+ def func():
+ l[0] += 1
+ gmain.switch()
+ l[0] += 1
+ #
+ g = greenlet(func)
+ g.switch()
+ assert l[0] == 1
+ ge1 = greenlet.GreenletExit(1, 2, 3)
+ ge2 = g.throw(ge1)
+ assert l[0] == 1
+ assert ge1 is ge2
+
def test_nondefault_parent(self):
from greenlet import greenlet
#
diff --git a/pypy/objspace/flow/flowcontext.py b/pypy/objspace/flow/flowcontext.py
--- a/pypy/objspace/flow/flowcontext.py
+++ b/pypy/objspace/flow/flowcontext.py
@@ -4,10 +4,12 @@
from pypy.interpreter.error import OperationError
from pypy.interpreter import pyframe, nestedscope
from pypy.interpreter.argument import ArgumentsForTranslation
+from pypy.interpreter.astcompiler.consts import CO_GENERATOR
+from pypy.interpreter.pycode import PyCode, cpython_code_signature
from pypy.objspace.flow import operation
from pypy.objspace.flow.model import *
-from pypy.objspace.flow.framestate import FrameState
-from pypy.rlib import jit
+from pypy.objspace.flow.framestate import (FrameState, recursively_unflatten,
+ recursively_flatten)
from pypy.tool.stdlib_opcode import host_bytecode_spec
class StopFlowing(Exception):
@@ -28,13 +30,6 @@
self.framestate = framestate
self.dead = False
- def patchframe(self, frame):
- if self.dead:
- raise StopFlowing
- self.framestate.restoreframe(frame)
- return BlockRecorder(self)
-
-
class EggBlock(Block):
# make slots optional, for debugging
if hasattr(Block, '__slots__'):
@@ -45,21 +40,6 @@
self.prevblock = prevblock
self.booloutcome = booloutcome
- def patchframe(self, frame):
- parentblocks = []
- block = self
- while isinstance(block, EggBlock):
- block = block.prevblock
- parentblocks.append(block)
- # parentblocks = [Egg, Egg, ..., Egg, Spam] not including self
- block.patchframe(frame)
- recorder = BlockRecorder(self)
- prevblock = self
- for block in parentblocks:
- recorder = Replayer(block, prevblock.booloutcome, recorder)
- prevblock = block
- return recorder
-
def extravars(self, last_exception=None, last_exc_value=None):
self.last_exception = last_exception
@@ -93,7 +73,6 @@
self.crnt_block.operations.append(operation)
def bytecode_trace(self, ec, frame):
- assert frame is ec.crnt_frame, "seeing an unexpected frame!"
ec.crnt_offset = frame.last_instr # save offset for opcode
if self.enterspamblock:
# If we have a SpamBlock, the first call to bytecode_trace()
@@ -110,7 +89,7 @@
# the same block. We will continue, to figure out where the next
# such operation *would* appear, and we make a join point just
# before.
- self.last_join_point = FrameState(frame)
+ self.last_join_point = frame.getstate()
def guessbool(self, ec, w_condition, cases=[False,True],
replace_last_variable_except_in_first_case = None):
@@ -184,43 +163,24 @@
class FlowExecutionContext(ExecutionContext):
- def __init__(self, space, code, globals, constargs={}, outer_func=None,
- name=None, is_generator=False):
- ExecutionContext.__init__(self, space)
- self.code = code
-
- self.w_globals = w_globals = space.wrap(globals)
-
- self.crnt_offset = -1
- self.crnt_frame = None
- if outer_func and outer_func.closure:
- self.closure = [nestedscope.Cell(Constant(value))
More information about the pypy-commit
mailing list