Author: mattip <matti.pi...@gmail.com> Branch: fortran-order Changeset: r80079:fb278777555d Date: 2015-10-09 09:28 +0300 http://bitbucket.org/pypy/pypy/changeset/fb278777555d/
Log: merge default into branch diff too long, truncating to 2000 out of 3790 lines diff --git a/lib_pypy/cffi/cparser.py b/lib_pypy/cffi/cparser.py --- a/lib_pypy/cffi/cparser.py +++ b/lib_pypy/cffi/cparser.py @@ -26,6 +26,9 @@ _r_words = re.compile(r"\w+|\S") _parser_cache = None _r_int_literal = re.compile(r"-?0?x?[0-9a-f]+[lu]*$", re.IGNORECASE) +_r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b") +_r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b") +_r_cdecl = re.compile(r"\b__cdecl\b") def _get_parser(): global _parser_cache @@ -44,6 +47,14 @@ macrovalue = macrovalue.replace('\\\n', '').strip() macros[macroname] = macrovalue csource = _r_define.sub('', csource) + # BIG HACK: replace WINAPI or __stdcall with "volatile const". + # It doesn't make sense for the return type of a function to be + # "volatile volatile const", so we abuse it to detect __stdcall... + # Hack number 2 is that "int(volatile *fptr)();" is not valid C + # syntax, so we place the "volatile" before the opening parenthesis. + csource = _r_stdcall2.sub(' volatile volatile const(', csource) + csource = _r_stdcall1.sub(' volatile volatile const ', csource) + csource = _r_cdecl.sub(' ', csource) # Replace "[...]" with "[__dotdotdotarray__]" csource = _r_partial_array.sub('[__dotdotdotarray__]', csource) # Replace "...}" with "__dotdotdotNUM__}". This construction should @@ -449,7 +460,14 @@ if not ellipsis and args == [model.void_type]: args = [] result, quals = self._get_type_and_quals(typenode.type) - return model.RawFunctionType(tuple(args), result, ellipsis) + # the 'quals' on the result type are ignored. HACK: we absure them + # to detect __stdcall functions: we textually replace "__stdcall" + # with "volatile volatile const" above. + abi = None + if hasattr(typenode.type, 'quals'): # else, probable syntax error anyway + if typenode.type.quals[-3:] == ['volatile', 'volatile', 'const']: + abi = '__stdcall' + return model.RawFunctionType(tuple(args), result, ellipsis, abi) def _as_func_arg(self, type, quals): if isinstance(type, model.ArrayType): diff --git a/lib_pypy/cffi/model.py b/lib_pypy/cffi/model.py --- a/lib_pypy/cffi/model.py +++ b/lib_pypy/cffi/model.py @@ -1,4 +1,4 @@ -import types +import types, sys import weakref from .lock import allocate_lock @@ -193,18 +193,21 @@ class BaseFunctionType(BaseType): - _attrs_ = ('args', 'result', 'ellipsis') + _attrs_ = ('args', 'result', 'ellipsis', 'abi') - def __init__(self, args, result, ellipsis): + def __init__(self, args, result, ellipsis, abi=None): self.args = args self.result = result self.ellipsis = ellipsis + self.abi = abi # reprargs = [arg._get_c_name() for arg in self.args] if self.ellipsis: reprargs.append('...') reprargs = reprargs or ['void'] replace_with = self._base_pattern % (', '.join(reprargs),) + if abi is not None: + replace_with = replace_with[:1] + abi + ' ' + replace_with[1:] self.c_name_with_marker = ( self.result.c_name_with_marker.replace('&', replace_with)) @@ -222,7 +225,7 @@ "type, not a pointer-to-function type" % (self,)) def as_function_pointer(self): - return FunctionPtrType(self.args, self.result, self.ellipsis) + return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi) class FunctionPtrType(BaseFunctionType): @@ -233,11 +236,18 @@ args = [] for tp in self.args: args.append(tp.get_cached_btype(ffi, finishlist)) + abi_args = () + if self.abi == "__stdcall": + if not self.ellipsis: # __stdcall ignored for variadic funcs + try: + abi_args = (ffi._backend.FFI_STDCALL,) + except AttributeError: + pass return global_cache(self, ffi, 'new_function_type', - tuple(args), result, self.ellipsis) + tuple(args), result, self.ellipsis, *abi_args) def as_raw_function(self): - return RawFunctionType(self.args, self.result, self.ellipsis) + return RawFunctionType(self.args, self.result, self.ellipsis, self.abi) class PointerType(BaseType): diff --git a/lib_pypy/cffi/parse_c_type.h b/lib_pypy/cffi/parse_c_type.h --- a/lib_pypy/cffi/parse_c_type.h +++ b/lib_pypy/cffi/parse_c_type.h @@ -5,7 +5,7 @@ #define _CFFI_OP(opcode, arg) (_cffi_opcode_t)(opcode | (((uintptr_t)(arg)) << 8)) #define _CFFI_GETOP(cffi_opcode) ((unsigned char)(uintptr_t)cffi_opcode) -#define _CFFI_GETARG(cffi_opcode) (((uintptr_t)cffi_opcode) >> 8) +#define _CFFI_GETARG(cffi_opcode) (((intptr_t)cffi_opcode) >> 8) #define _CFFI_OP_PRIMITIVE 1 #define _CFFI_OP_POINTER 3 diff --git a/lib_pypy/cffi/recompiler.py b/lib_pypy/cffi/recompiler.py --- a/lib_pypy/cffi/recompiler.py +++ b/lib_pypy/cffi/recompiler.py @@ -607,7 +607,11 @@ call_arguments.append('x%d' % i) repr_arguments = ', '.join(arguments) repr_arguments = repr_arguments or 'void' - name_and_arguments = '_cffi_d_%s(%s)' % (name, repr_arguments) + if tp.abi: + abi = tp.abi + ' ' + else: + abi = '' + name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments) prnt('static %s' % (tp.result.get_c_name(name_and_arguments),)) prnt('{') call_arguments = ', '.join(call_arguments) @@ -710,7 +714,8 @@ if difference: repr_arguments = ', '.join(arguments) repr_arguments = repr_arguments or 'void' - name_and_arguments = '_cffi_f_%s(%s)' % (name, repr_arguments) + name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name, + repr_arguments) prnt('static %s' % (tp_result.get_c_name(name_and_arguments),)) prnt('{') if result_decl: @@ -1135,7 +1140,13 @@ else: self.cffi_types[index] = CffiOp(OP_NOOP, realindex) index += 1 - self.cffi_types[index] = CffiOp(OP_FUNCTION_END, int(tp.ellipsis)) + flags = int(tp.ellipsis) + if tp.abi is not None: + if tp.abi == '__stdcall': + flags |= 2 + else: + raise NotImplementedError("abi=%r" % (tp.abi,)) + self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags) def _emit_bytecode_PointerType(self, tp, index): self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype]) diff --git a/lib_pypy/cffi/vengine_gen.py b/lib_pypy/cffi/vengine_gen.py --- a/lib_pypy/cffi/vengine_gen.py +++ b/lib_pypy/cffi/vengine_gen.py @@ -159,7 +159,11 @@ arglist = ', '.join(arglist) or 'void' wrappername = '_cffi_f_%s' % name self.export_symbols.append(wrappername) - funcdecl = ' %s(%s)' % (wrappername, arglist) + if tp.abi: + abi = tp.abi + ' ' + else: + abi = '' + funcdecl = ' %s%s(%s)' % (abi, wrappername, arglist) context = 'result of %s' % name prnt(tpresult.get_c_name(funcdecl, context)) prnt('{') diff --git a/pypy/doc/extending.rst b/pypy/doc/extending.rst --- a/pypy/doc/extending.rst +++ b/pypy/doc/extending.rst @@ -83,7 +83,7 @@ RPython Mixed Modules -===================== +--------------------- This is the internal way to write built-in extension modules in PyPy. It cannot be used by any 3rd-party module: the extension modules are diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -53,3 +53,10 @@ Fix performance regression on operations mixing numpy scalars and Python floats, cf. issue #2148. + +.. branch: cffi-stdcall +Win32: support '__stdcall' in CFFI. + +.. branch: callfamily + +Refactorings of annotation and rtyping of function calls. diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py --- a/pypy/module/_cffi_backend/__init__.py +++ b/pypy/module/_cffi_backend/__init__.py @@ -1,9 +1,16 @@ import sys from pypy.interpreter.mixedmodule import MixedModule -from rpython.rlib import rdynload +from rpython.rlib import rdynload, clibffi VERSION = "1.3.0" +FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI +try: + FFI_STDCALL = clibffi.FFI_STDCALL + has_stdcall = True +except AttributeError: + has_stdcall = False + class Module(MixedModule): @@ -44,8 +51,8 @@ '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 + 'FFI_DEFAULT_ABI': 'space.wrap(%d)' % FFI_DEFAULT_ABI, + 'FFI_CDECL': 'space.wrap(%d)' % FFI_DEFAULT_ABI, # win32 name # CFFI 1.0 'FFI': 'ffi_obj.W_FFIObject', @@ -53,6 +60,9 @@ if sys.platform == 'win32': interpleveldefs['getwinerror'] = 'cerrno.getwinerror' + if has_stdcall: + interpleveldefs['FFI_STDCALL'] = 'space.wrap(%d)' % FFI_STDCALL + def get_dict_rtld_constants(): found = {} diff --git a/pypy/module/_cffi_backend/ctypefunc.py b/pypy/module/_cffi_backend/ctypefunc.py --- a/pypy/module/_cffi_backend/ctypefunc.py +++ b/pypy/module/_cffi_backend/ctypefunc.py @@ -12,6 +12,7 @@ from rpython.rtyper.lltypesystem import lltype, llmemory, rffi from pypy.interpreter.error import OperationError, oefmt +from pypy.module import _cffi_backend from pypy.module._cffi_backend import ctypearray, cdataobj, cerrno from pypy.module._cffi_backend.ctypeobj import W_CType from pypy.module._cffi_backend.ctypeptr import W_CTypePtrBase, W_CTypePointer @@ -23,20 +24,22 @@ class W_CTypeFunc(W_CTypePtrBase): - _attrs_ = ['fargs', 'ellipsis', 'cif_descr'] - _immutable_fields_ = ['fargs[*]', 'ellipsis', 'cif_descr'] + _attrs_ = ['fargs', 'ellipsis', 'abi', 'cif_descr'] + _immutable_fields_ = ['fargs[*]', 'ellipsis', 'abi', 'cif_descr'] kind = "function" cif_descr = lltype.nullptr(CIF_DESCRIPTION) - def __init__(self, space, fargs, fresult, ellipsis): + def __init__(self, space, fargs, fresult, ellipsis, + abi=_cffi_backend.FFI_DEFAULT_ABI): assert isinstance(ellipsis, bool) - extra = self._compute_extra_text(fargs, fresult, ellipsis) + extra, xpos = self._compute_extra_text(fargs, fresult, ellipsis, abi) size = rffi.sizeof(rffi.VOIDP) - W_CTypePtrBase.__init__(self, space, size, extra, 2, fresult, + W_CTypePtrBase.__init__(self, space, size, extra, xpos, fresult, could_cast_anything=False) self.fargs = fargs self.ellipsis = ellipsis + self.abi = abi # fresult is stored in self.ctitem if not ellipsis: @@ -44,7 +47,7 @@ # 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. - builder = CifDescrBuilder(fargs, fresult) + builder = CifDescrBuilder(fargs, fresult, abi) try: builder.rawallocate(self) except OperationError, e: @@ -76,7 +79,7 @@ ctypefunc.fargs = fvarargs ctypefunc.ctitem = self.ctitem #ctypefunc.cif_descr = NULL --- already provided as the default - CifDescrBuilder(fvarargs, self.ctitem).rawallocate(ctypefunc) + CifDescrBuilder(fvarargs, self.ctitem, self.abi).rawallocate(ctypefunc) return ctypefunc @rgc.must_be_light_finalizer @@ -84,8 +87,13 @@ if self.cif_descr: lltype.free(self.cif_descr, flavor='raw') - def _compute_extra_text(self, fargs, fresult, ellipsis): + def _compute_extra_text(self, fargs, fresult, ellipsis, abi): + from pypy.module._cffi_backend import newtype argnames = ['(*)('] + xpos = 2 + if _cffi_backend.has_stdcall and abi == _cffi_backend.FFI_STDCALL: + argnames[0] = '(__stdcall *)(' + xpos += len('__stdcall ') for i, farg in enumerate(fargs): if i > 0: argnames.append(', ') @@ -95,7 +103,7 @@ argnames.append(', ') argnames.append('...') argnames.append(')') - return ''.join(argnames) + return ''.join(argnames), xpos def _fget(self, attrchar): if attrchar == 'a': # args @@ -106,7 +114,7 @@ if attrchar == 'E': # ellipsis return self.space.wrap(self.ellipsis) if attrchar == 'A': # abi - return self.space.wrap(clibffi.FFI_DEFAULT_ABI) # XXX + return self.space.wrap(self.abi) return W_CTypePtrBase._fget(self, attrchar) def call(self, funcaddr, args_w): @@ -181,11 +189,6 @@ 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) - # ____________________________________________________________ @@ -260,9 +263,10 @@ class CifDescrBuilder(object): rawmem = lltype.nullptr(rffi.CCHARP.TO) - def __init__(self, fargs, fresult): + def __init__(self, fargs, fresult, fabi): self.fargs = fargs self.fresult = fresult + self.fabi = fabi def fb_alloc(self, size): size = llmemory.raw_malloc_usage(size) @@ -421,7 +425,7 @@ cif_descr.exchange_size = exchange_offset def fb_extra_fields(self, cif_descr): - cif_descr.abi = clibffi.FFI_DEFAULT_ABI # XXX + cif_descr.abi = self.fabi cif_descr.nargs = len(self.fargs) cif_descr.rtype = self.rtype cif_descr.atypes = self.atypes diff --git a/pypy/module/_cffi_backend/ctypeobj.py b/pypy/module/_cffi_backend/ctypeobj.py --- a/pypy/module/_cffi_backend/ctypeobj.py +++ b/pypy/module/_cffi_backend/ctypeobj.py @@ -143,7 +143,7 @@ # obscure hack when untranslated, maybe, approximate, don't use if isinstance(align, llmemory.FieldOffset): align = rffi.sizeof(align.TYPE.y) - if (1 << (8*align-2)) > sys.maxint: + if sys.platform != 'win32' and (1 << (8*align-2)) > sys.maxint: align /= 2 else: # a different hack when translated, to avoid seeing constants diff --git a/pypy/module/_cffi_backend/newtype.py b/pypy/module/_cffi_backend/newtype.py --- a/pypy/module/_cffi_backend/newtype.py +++ b/pypy/module/_cffi_backend/newtype.py @@ -4,10 +4,11 @@ from rpython.rlib.objectmodel import specialize, r_dict, compute_identity_hash from rpython.rlib.rarithmetic import ovfcheck, intmask -from rpython.rlib import jit, rweakref +from rpython.rlib import jit, rweakref, clibffi from rpython.rtyper.lltypesystem import lltype, rffi from rpython.rtyper.tool import rffi_platform +from pypy.module import _cffi_backend from pypy.module._cffi_backend import (ctypeobj, ctypeprim, ctypeptr, ctypearray, ctypestruct, ctypevoid, ctypeenum) @@ -592,8 +593,9 @@ # ____________________________________________________________ -@unwrap_spec(w_fresult=ctypeobj.W_CType, ellipsis=int) -def new_function_type(space, w_fargs, w_fresult, ellipsis=0): +@unwrap_spec(w_fresult=ctypeobj.W_CType, ellipsis=int, abi=int) +def new_function_type(space, w_fargs, w_fresult, ellipsis=0, + abi=_cffi_backend.FFI_DEFAULT_ABI): fargs = [] for w_farg in space.fixedview(w_fargs): if not isinstance(w_farg, ctypeobj.W_CType): @@ -602,28 +604,28 @@ if isinstance(w_farg, ctypearray.W_CTypeArray): w_farg = w_farg.ctptr fargs.append(w_farg) - return _new_function_type(space, fargs, w_fresult, bool(ellipsis)) + return _new_function_type(space, fargs, w_fresult, bool(ellipsis), abi) -def _func_key_hash(unique_cache, fargs, fresult, ellipsis): +def _func_key_hash(unique_cache, fargs, fresult, ellipsis, abi): x = compute_identity_hash(fresult) for w_arg in fargs: y = compute_identity_hash(w_arg) x = intmask((1000003 * x) ^ y) - x ^= ellipsis + x ^= (ellipsis - abi) if unique_cache.for_testing: # constant-folded to False in translation; x &= 3 # but for test, keep only 2 bits of hash return x # can't use @jit.elidable here, because it might call back to random # space functions via force_lazy_struct() -def _new_function_type(space, fargs, fresult, ellipsis=False): +def _new_function_type(space, fargs, fresult, ellipsis, abi): try: - return _get_function_type(space, fargs, fresult, ellipsis) + return _get_function_type(space, fargs, fresult, ellipsis, abi) except KeyError: - return _build_function_type(space, fargs, fresult, ellipsis) + return _build_function_type(space, fargs, fresult, ellipsis, abi) @jit.elidable -def _get_function_type(space, fargs, fresult, ellipsis): +def _get_function_type(space, fargs, fresult, ellipsis, abi): # This function is elidable because if called again with exactly the # same arguments (and if it didn't raise KeyError), it would give # the same result, at least as long as this result is still live. @@ -633,18 +635,19 @@ # one such dict, but in case of hash collision, there might be # more. unique_cache = space.fromcache(UniqueCache) - func_hash = _func_key_hash(unique_cache, fargs, fresult, ellipsis) + func_hash = _func_key_hash(unique_cache, fargs, fresult, ellipsis, abi) for weakdict in unique_cache.functions: ctype = weakdict.get(func_hash) if (ctype is not None and ctype.ctitem is fresult and ctype.fargs == fargs and - ctype.ellipsis == ellipsis): + ctype.ellipsis == ellipsis and + ctype.abi == abi): return ctype raise KeyError @jit.dont_look_inside -def _build_function_type(space, fargs, fresult, ellipsis): +def _build_function_type(space, fargs, fresult, ellipsis, abi): from pypy.module._cffi_backend import ctypefunc # if ((fresult.size < 0 and @@ -658,9 +661,9 @@ raise oefmt(space.w_TypeError, "invalid result type: '%s'", fresult.name) # - fct = ctypefunc.W_CTypeFunc(space, fargs, fresult, ellipsis) + fct = ctypefunc.W_CTypeFunc(space, fargs, fresult, ellipsis, abi) unique_cache = space.fromcache(UniqueCache) - func_hash = _func_key_hash(unique_cache, fargs, fresult, ellipsis) + func_hash = _func_key_hash(unique_cache, fargs, fresult, ellipsis, abi) for weakdict in unique_cache.functions: if weakdict.get(func_hash) is None: weakdict.set(func_hash, fct) diff --git a/pypy/module/_cffi_backend/realize_c_type.py b/pypy/module/_cffi_backend/realize_c_type.py --- a/pypy/module/_cffi_backend/realize_c_type.py +++ b/pypy/module/_cffi_backend/realize_c_type.py @@ -5,6 +5,7 @@ from rpython.rtyper.lltypesystem import lltype, rffi from pypy.interpreter.error import oefmt from pypy.interpreter.baseobjspace import W_Root +from pypy.module import _cffi_backend from pypy.module._cffi_backend.ctypeobj import W_CType from pypy.module._cffi_backend import cffi_opcode, newtype, ctypestruct from pypy.module._cffi_backend import parse_c_type @@ -164,16 +165,28 @@ OP_FUNCTION_END = cffi_opcode.OP_FUNCTION_END while getop(opcodes[base_index + num_args]) != OP_FUNCTION_END: num_args += 1 - ellipsis = (getarg(opcodes[base_index + num_args]) & 1) != 0 + # + ellipsis = (getarg(opcodes[base_index + num_args]) & 0x01) != 0 + abi = (getarg(opcodes[base_index + num_args]) & 0xFE) + if abi == 0: + abi = _cffi_backend.FFI_DEFAULT_ABI + elif abi == 2: + if _cffi_backend.has_stdcall: + abi = _cffi_backend.FFI_STDCALL + else: + abi = _cffi_backend.FFI_DEFAULT_ABI + else: + raise oefmt(ffi.w_FFIError, "abi number %d not supported", abi) + # fargs = [realize_c_type(ffi, opcodes, base_index + i) for i in range(num_args)] - return fargs, fret, ellipsis + return fargs, fret, ellipsis, abi def unwrap_as_fnptr(self, ffi): if self._ctfuncptr is None: - fargs, fret, ellipsis = self._unpack(ffi) + fargs, fret, ellipsis, abi = self._unpack(ffi) self._ctfuncptr = newtype._new_function_type( - ffi.space, fargs, fret, ellipsis) + ffi.space, fargs, fret, ellipsis, abi) return self._ctfuncptr def unwrap_as_fnptr_in_elidable(self): @@ -190,7 +203,7 @@ # type ptr-to-struct. This is how recompiler.py produces # trampoline functions for PyPy. if self.nostruct_ctype is None: - fargs, fret, ellipsis = self._unpack(ffi) + fargs, fret, ellipsis, abi = self._unpack(ffi) # 'locs' will be a string of the same length as the final fargs, # containing 'A' where a struct argument was detected, and 'R' # in first position if a struct return value was detected @@ -207,7 +220,7 @@ locs = ['R'] + locs fret = newtype.new_void_type(ffi.space) ctfuncptr = newtype._new_function_type( - ffi.space, fargs, fret, ellipsis) + ffi.space, fargs, fret, ellipsis, abi) if locs == ['\x00'] * len(locs): locs = None else: @@ -218,7 +231,7 @@ locs[0] == 'R') def unexpected_fn_type(self, ffi): - fargs, fret, ellipsis = self._unpack(ffi) + fargs, fret, ellipsis, abi = self._unpack(ffi) argnames = [farg.name for farg in fargs] if ellipsis: argnames.append('...') diff --git a/pypy/module/_cffi_backend/src/parse_c_type.c b/pypy/module/_cffi_backend/src/parse_c_type.c --- a/pypy/module/_cffi_backend/src/parse_c_type.c +++ b/pypy/module/_cffi_backend/src/parse_c_type.c @@ -51,6 +51,9 @@ TOK_UNSIGNED, TOK_VOID, TOK_VOLATILE, + + TOK_CDECL, + TOK_STDCALL, }; typedef struct { @@ -165,6 +168,8 @@ switch (*p) { case '_': if (tok->size == 5 && !memcmp(p, "_Bool", 5)) tok->kind = TOK__BOOL; + if (tok->size == 7 && !memcmp(p,"__cdecl",7)) tok->kind = TOK_CDECL; + if (tok->size == 9 && !memcmp(p,"__stdcall",9))tok->kind = TOK_STDCALL; break; case 'c': if (tok->size == 4 && !memcmp(p, "char", 4)) tok->kind = TOK_CHAR; @@ -236,7 +241,7 @@ type). The 'outer' argument is the index of the opcode outside this "sequel". */ - int check_for_grouping; + int check_for_grouping, abi=0; _cffi_opcode_t result, *p_current; header: @@ -253,6 +258,12 @@ /* ignored for now */ next_token(tok); goto header; + case TOK_CDECL: + case TOK_STDCALL: + /* must be in a function; checked below */ + abi = tok->kind; + next_token(tok); + goto header; default: break; } @@ -269,6 +280,11 @@ while (tok->kind == TOK_OPEN_PAREN) { next_token(tok); + if (tok->kind == TOK_CDECL || tok->kind == TOK_STDCALL) { + abi = tok->kind; + next_token(tok); + } + if ((check_for_grouping--) == 1 && (tok->kind == TOK_STAR || tok->kind == TOK_CONST || tok->kind == TOK_VOLATILE || @@ -286,7 +302,14 @@ } else { /* function type */ - int arg_total, base_index, arg_next, has_ellipsis=0; + int arg_total, base_index, arg_next, flags=0; + + if (abi == TOK_STDCALL) { + flags = 2; + /* note that an ellipsis below will overwrite this flags, + which is the goal: variadic functions are always cdecl */ + } + abi = 0; if (tok->kind == TOK_VOID && get_following_char(tok) == ')') { next_token(tok); @@ -315,7 +338,7 @@ _cffi_opcode_t oarg; if (tok->kind == TOK_DOTDOTDOT) { - has_ellipsis = 1; + flags = 1; /* ellipsis */ next_token(tok); break; } @@ -339,8 +362,7 @@ next_token(tok); } } - tok->output[arg_next] = _CFFI_OP(_CFFI_OP_FUNCTION_END, - has_ellipsis); + tok->output[arg_next] = _CFFI_OP(_CFFI_OP_FUNCTION_END, flags); } if (tok->kind != TOK_CLOSE_PAREN) @@ -348,6 +370,9 @@ next_token(tok); } + if (abi != 0) + return parse_error(tok, "expected '('"); + while (tok->kind == TOK_OPEN_BRACKET) { *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), tok->output_index); p_current = tok->output + tok->output_index; diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py --- a/pypy/module/_cffi_backend/test/_backend_test_c.py +++ b/pypy/module/_cffi_backend/test/_backend_test_c.py @@ -2316,9 +2316,6 @@ 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") @@ -3427,3 +3424,16 @@ "be 'foo *', but the types are different (check " "that you are not e.g. mixing up different ffi " "instances)") + +def test_stdcall_function_type(): + assert FFI_CDECL == FFI_DEFAULT_ABI + try: + stdcall = FFI_STDCALL + except NameError: + stdcall = FFI_DEFAULT_ABI + BInt = new_primitive_type("int") + BFunc = new_function_type((BInt, BInt), BInt, False, stdcall) + if stdcall != FFI_DEFAULT_ABI: + assert repr(BFunc) == "<ctype 'int(__stdcall *)(int, int)'>" + else: + assert repr(BFunc) == "<ctype 'int(*)(int, int)'>" diff --git a/pypy/module/_cffi_backend/test/test_parse_c_type.py b/pypy/module/_cffi_backend/test/test_parse_c_type.py --- a/pypy/module/_cffi_backend/test/test_parse_c_type.py +++ b/pypy/module/_cffi_backend/test/test_parse_c_type.py @@ -338,3 +338,17 @@ # not supported (really obscure): # "char[+5]" # "char['A']" + +def test_stdcall_cdecl(): + assert parse("int __stdcall(int)") == [Prim(cffi_opcode.PRIM_INT), + '->', Func(0), NoOp(4), FuncEnd(2), + Prim(cffi_opcode.PRIM_INT)] + assert parse("int __stdcall func(int)") == parse("int __stdcall(int)") + assert parse("int (__stdcall *)()") == [Prim(cffi_opcode.PRIM_INT), + NoOp(3), '->', Pointer(1), + Func(0), FuncEnd(2), 0] + assert parse("int (__stdcall *p)()") == parse("int (__stdcall*)()") + parse_error("__stdcall int", "identifier expected", 0) + parse_error("__cdecl int", "identifier expected", 0) + parse_error("int __stdcall", "expected '('", 13) + parse_error("int __cdecl", "expected '('", 11) diff --git a/pypy/module/itertools/__init__.py b/pypy/module/itertools/__init__.py --- a/pypy/module/itertools/__init__.py +++ b/pypy/module/itertools/__init__.py @@ -10,7 +10,6 @@ repeat(elem [,n]) --> elem, elem, elem, ... endlessly or up to n times Iterators terminating on the shortest input sequence: - izip(p, q, ...) --> (p[0], q[0]), (p[1], q[1]), ... ifilter(pred, seq) --> elements of seq where pred(elem) is True ifilterfalse(pred, seq) --> elements of seq where pred(elem) is False islice(seq, [start,] stop [, step]) --> elements from @@ -22,6 +21,14 @@ takewhile(pred, seq) --> seq[0], seq[1], until pred fails dropwhile(pred, seq) --> seq[n], seq[n+1], starting when pred fails groupby(iterable[, keyfunc]) --> sub-iterators grouped by value of keyfunc(v) + izip(p, q, ...) --> (p[0], q[0]), (p[1], q[1]), ... + izip_longest(p, q, ...) --> (p[0], q[0]), (p[1], q[1]), ... + + Combinatoric generators: + product(p, q, ... [repeat=1]) --> cartesian product + permutations(p[, r]) + combinations(p, r) + combinations_with_replacement(p, r) """ interpleveldefs = { diff --git a/pypy/module/itertools/interp_itertools.py b/pypy/module/itertools/interp_itertools.py --- a/pypy/module/itertools/interp_itertools.py +++ b/pypy/module/itertools/interp_itertools.py @@ -649,33 +649,38 @@ class W_IZipLongest(W_IMap): _error_name = "izip_longest" + _immutable_fields_ = ["w_fillvalue"] + + def _fetch(self, index): + w_iter = self.iterators_w[index] + if w_iter is not None: + space = self.space + try: + return space.next(w_iter) + except OperationError, e: + if not e.match(space, space.w_StopIteration): + raise + self.active -= 1 + if self.active <= 0: + # It was the last active iterator + raise + self.iterators_w[index] = None + return self.w_fillvalue def next_w(self): - space = self.space + # common case: 2 arguments + if len(self.iterators_w) == 2: + objects = [self._fetch(0), self._fetch(1)] + else: + objects = self._get_objects() + return self.space.newtuple(objects) + + def _get_objects(self): + # the loop is out of the way of the JIT nb = len(self.iterators_w) - if nb == 0: - raise OperationError(space.w_StopIteration, space.w_None) - - objects_w = [None] * nb - for index in range(nb): - w_value = self.w_fillvalue - w_it = self.iterators_w[index] - if w_it is not None: - try: - w_value = space.next(w_it) - except OperationError, e: - if not e.match(space, space.w_StopIteration): - raise - - self.active -= 1 - if self.active == 0: - # It was the last active iterator - raise - self.iterators_w[index] = None - - objects_w[index] = w_value - return space.newtuple(objects_w) + raise OperationError(self.space.w_StopIteration, self.space.w_None) + return [self._fetch(index) for index in range(nb)] def W_IZipLongest___new__(space, w_subtype, __args__): arguments_w, kwds_w = __args__.unpack() diff --git a/pypy/module/pypyjit/__init__.py b/pypy/module/pypyjit/__init__.py --- a/pypy/module/pypyjit/__init__.py +++ b/pypy/module/pypyjit/__init__.py @@ -15,6 +15,7 @@ 'set_compile_hook': 'interp_resop.set_compile_hook', 'set_abort_hook': 'interp_resop.set_abort_hook', 'get_stats_snapshot': 'interp_resop.get_stats_snapshot', + 'get_stats_asmmemmgr': 'interp_resop.get_stats_asmmemmgr', # those things are disabled because they have bugs, but if # they're found to be useful, fix test_ztranslation_jit_stats # in the backend first. get_stats_snapshot still produces diff --git a/pypy/module/pypyjit/interp_resop.py b/pypy/module/pypyjit/interp_resop.py --- a/pypy/module/pypyjit/interp_resop.py +++ b/pypy/module/pypyjit/interp_resop.py @@ -333,6 +333,13 @@ return space.wrap(W_JitInfoSnapshot(space, w_times, w_counters, w_counter_times)) +def get_stats_asmmemmgr(space): + """Returns the raw memory currently used by the JIT backend, + as a pair (total_memory_allocated, memory_in_use).""" + m1 = jit_hooks.stats_asmmemmgr_allocated(None) + m2 = jit_hooks.stats_asmmemmgr_used(None) + return space.newtuple([space.wrap(m1), space.wrap(m2)]) + def enable_debug(space): """ Set the jit debugging - completely necessary for some stats to work, most notably assembler counters. diff --git a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py --- a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py +++ b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py @@ -273,16 +273,16 @@ guard_not_invalidated(descr=...) f100 = float_mul(f98, 0.500000) i101 = int_add(i79, 1) - i102 = arraylen_gc(p85, descr=<ArrayP 8>) + i102 = arraylen_gc(p85, descr=<ArrayP .>) i103 = int_lt(i102, i101) cond_call(i103, ConstClass(_ll_list_resize_hint_really_look_inside_iff__listPtr_Signed_Bool), p76, i101, 1, descr=<Callv 0 rii EF=5>) guard_no_exception(descr=...) - p104 = getfield_gc_r(p76, descr=<FieldP list.items 16>) - p105 = new_with_vtable(descr=<SizeDescr 24>) - setfield_gc(p105, f100, descr=<FieldF pypy.module.micronumpy.boxes.W_Float64Box.inst_value 16>) - setarrayitem_gc(p104, i79, p105, descr=<ArrayP 8>) + p104 = getfield_gc_r(p76, descr=<FieldP list.items .*>) + p105 = new_with_vtable(descr=<SizeDescr .*>) + setfield_gc(p105, f100, descr=<FieldF pypy.module.micronumpy.boxes.W_Float64Box.inst_value .*>) + setarrayitem_gc(p104, i79, p105, descr=<ArrayP .>) i106 = getfield_raw_i(#, descr=<FieldS pypysig_long_struct.c_value 0>) - setfield_gc(p76, i101, descr=<FieldS list.length 8>) + setfield_gc(p76, i101, descr=<FieldS list.length .*>) i107 = int_lt(i106, 0) guard_false(i107, descr=...) jump(..., descr=...) diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_function.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_function.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_function.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_function.py @@ -1,6 +1,6 @@ # Generated by pypy/tool/import_cffi.py import py -from cffi import FFI +from cffi import FFI, CDefError import math, os, sys import ctypes.util from cffi.backend_ctypes import CTypesBackend @@ -428,3 +428,59 @@ res = m.QueryPerformanceFrequency(p_freq) assert res != 0 assert p_freq[0] != 0 + + def test_explicit_cdecl_stdcall(self): + if sys.platform != 'win32': + py.test.skip("Windows-only test") + if self.Backend is CTypesBackend: + py.test.skip("not with the ctypes backend") + # + ffi = FFI(backend=self.Backend()) + ffi.cdef(""" + BOOL QueryPerformanceFrequency(LONGLONG *lpFrequency); + """) + m = ffi.dlopen("Kernel32.dll") + tp = ffi.typeof(m.QueryPerformanceFrequency) + assert str(tp) == "<ctype 'int(*)(long long *)'>" + # + ffi = FFI(backend=self.Backend()) + ffi.cdef(""" + BOOL __cdecl QueryPerformanceFrequency(LONGLONG *lpFrequency); + """) + m = ffi.dlopen("Kernel32.dll") + tpc = ffi.typeof(m.QueryPerformanceFrequency) + assert tpc is tp + # + ffi = FFI(backend=self.Backend()) + ffi.cdef(""" + BOOL WINAPI QueryPerformanceFrequency(LONGLONG *lpFrequency); + """) + m = ffi.dlopen("Kernel32.dll") + tps = ffi.typeof(m.QueryPerformanceFrequency) + assert tps is not tpc + assert str(tps) == "<ctype 'int(__stdcall *)(long long *)'>" + # + ffi = FFI(backend=self.Backend()) + ffi.cdef("typedef int (__cdecl *fnc_t)(int);") + ffi.cdef("typedef int (__stdcall *fns_t)(int);") + tpc = ffi.typeof("fnc_t") + tps = ffi.typeof("fns_t") + assert str(tpc) == "<ctype 'int(*)(int)'>" + assert str(tps) == "<ctype 'int(__stdcall *)(int)'>" + # + fnc = ffi.cast("fnc_t", 0) + fns = ffi.cast("fns_t", 0) + ffi.new("fnc_t[]", [fnc]) + py.test.raises(TypeError, ffi.new, "fnc_t[]", [fns]) + py.test.raises(TypeError, ffi.new, "fns_t[]", [fnc]) + ffi.new("fns_t[]", [fns]) + + def test_stdcall_only_on_windows(self): + if sys.platform == 'win32': + py.test.skip("not-Windows-only test") + ffi = FFI(backend=self.Backend()) + ffi.cdef("double __stdcall sin(double x);") # stdcall ignored + m = ffi.dlopen(lib_m) + assert "double(*)(double)" in str(ffi.typeof(m.sin)) + x = m.sin(1.23) + assert x == math.sin(1.23) diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_parsing.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_parsing.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_parsing.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_parsing.py @@ -365,3 +365,17 @@ assert C.TWO == 2 assert C.NIL == 0 assert C.NEG == -1 + +def test_stdcall(): + ffi = FFI() + tp = ffi.typeof("int(*)(int __stdcall x(int)," + " long (__cdecl*y)(void)," + " short(WINAPI *z)(short))") + if sys.platform == 'win32': + stdcall = '__stdcall ' + else: + stdcall = '' + assert str(tp) == ( + "<ctype 'int(*)(int(%s*)(int), " + "long(*)(), " + "short(%s*)(short))'>" % (stdcall, stdcall)) diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py @@ -1221,25 +1221,6 @@ lib = ffi.verify('#include <math.h>', libraries=lib_m) assert lib.sin(1.23) == math.sin(1.23) -def test_callback_calling_convention(): - py.test.skip("later") - if sys.platform != 'win32': - py.test.skip("Windows only") - ffi = FFI() - ffi.cdef(""" - int call1(int(*__cdecl cb)(int)); - int call2(int(*__stdcall cb)(int)); - """) - lib = ffi.verify(""" - int call1(int(*__cdecl cb)(int)) { - return cb(42) + 1; - } - int call2(int(*__stdcall cb)(int)) { - return cb(-42) - 6; - } - """) - xxx - def test_opaque_integer_as_function_result(): #import platform #if platform.machine().startswith('sparc'): @@ -2261,3 +2242,180 @@ assert foo_s.fields[0][1].type is ffi.typeof("int") assert foo_s.fields[1][0] == 'b' assert foo_s.fields[1][1].type is ffi.typeof("void *") + +def test_win32_calling_convention_0(): + ffi = FFI() + ffi.cdef(""" + int call1(int(__cdecl *cb)(int)); + int (*const call2)(int(__stdcall *cb)(int)); + """) + lib = ffi.verify(r""" + #ifndef _MSC_VER + # define __stdcall /* nothing */ + #endif + int call1(int(*cb)(int)) { + int i, result = 0; + //printf("call1: cb = %p\n", cb); + for (i = 0; i < 1000; i++) + result += cb(i); + //printf("result = %d\n", result); + return result; + } + int call2(int(__stdcall *cb)(int)) { + int i, result = 0; + //printf("call2: cb = %p\n", cb); + for (i = 0; i < 1000; i++) + result += cb(-i); + //printf("result = %d\n", result); + return result; + } + """) + @ffi.callback("int(int)") + def cb1(x): + return x * 2 + @ffi.callback("int __stdcall(int)") + def cb2(x): + return x * 3 + print 'cb1 =', cb1 + res = lib.call1(cb1) + assert res == 500*999*2 + print 'cb2 =', cb2 + print ffi.typeof(lib.call2) + print 'call2 =', lib.call2 + res = lib.call2(cb2) + print '...' + assert res == -500*999*3 + print 'done' + if sys.platform == 'win32': + assert '__stdcall' in str(ffi.typeof(cb2)) + assert '__stdcall' not in str(ffi.typeof(cb1)) + py.test.raises(TypeError, lib.call1, cb2) + py.test.raises(TypeError, lib.call2, cb1) + else: + assert '__stdcall' not in str(ffi.typeof(cb2)) + assert ffi.typeof(cb2) is ffi.typeof(cb1) + +def test_win32_calling_convention_1(): + ffi = FFI() + ffi.cdef(""" + int __cdecl call1(int(__cdecl *cb)(int)); + int __stdcall call2(int(__stdcall *cb)(int)); + int (__cdecl *const cb1)(int); + int (__stdcall *const cb2)(int); + """) + lib = ffi.verify(r""" + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + int __cdecl cb1(int x) { return x * 2; } + int __stdcall cb2(int x) { return x * 3; } + + int __cdecl call1(int(__cdecl *cb)(int)) { + int i, result = 0; + //printf("here1\n"); + //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); + for (i = 0; i < 1000; i++) + result += cb(i); + //printf("result = %d\n", result); + return result; + } + int __stdcall call2(int(__stdcall *cb)(int)) { + int i, result = 0; + //printf("here1\n"); + //printf("cb = %p, cb2 = %p\n", cb, (void *)cb2); + for (i = 0; i < 1000; i++) + result += cb(-i); + //printf("result = %d\n", result); + return result; + } + """) + assert lib.call1(lib.cb1) == 500*999*2 + assert lib.call2(lib.cb2) == -500*999*3 + +def test_win32_calling_convention_2(): + # any mistake in the declaration of plain function (including the + # precise argument types and, here, the calling convention) are + # automatically corrected. But this does not apply to the 'cb' + # function pointer argument. + ffi = FFI() + ffi.cdef(""" + int __stdcall call1(int(__cdecl *cb)(int)); + int __cdecl call2(int(__stdcall *cb)(int)); + int (__cdecl *const cb1)(int); + int (__stdcall *const cb2)(int); + """) + lib = ffi.verify(r""" + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + int __cdecl call1(int(__cdecl *cb)(int)) { + int i, result = 0; + for (i = 0; i < 1000; i++) + result += cb(i); + return result; + } + int __stdcall call2(int(__stdcall *cb)(int)) { + int i, result = 0; + for (i = 0; i < 1000; i++) + result += cb(-i); + return result; + } + int __cdecl cb1(int x) { return x * 2; } + int __stdcall cb2(int x) { return x * 3; } + """) + assert lib.call1(lib.cb1) == 500*999*2 + assert lib.call2(lib.cb2) == -500*999*3 + +def test_win32_calling_convention_3(): + ffi = FFI() + ffi.cdef(""" + struct point { int x, y; }; + + int (*const cb1)(struct point); + int (__stdcall *const cb2)(struct point); + + struct point __stdcall call1(int(*cb)(struct point)); + struct point call2(int(__stdcall *cb)(struct point)); + """) + lib = ffi.verify(r""" + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + struct point { int x, y; }; + int cb1(struct point pt) { return pt.x + 10 * pt.y; } + int __stdcall cb2(struct point pt) { return pt.x + 100 * pt.y; } + struct point __stdcall call1(int(__cdecl *cb)(struct point)) { + int i; + struct point result = { 0, 0 }; + //printf("here1\n"); + //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); + for (i = 0; i < 1000; i++) { + struct point p = { i, -i }; + int r = cb(p); + result.x += r; + result.y -= r; + } + return result; + } + struct point __cdecl call2(int(__stdcall *cb)(struct point)) { + int i; + struct point result = { 0, 0 }; + for (i = 0; i < 1000; i++) { + struct point p = { -i, i }; + int r = cb(p); + result.x += r; + result.y -= r; + } + return result; + } + """) + if sys.platform == 'win32': + py.test.raises(TypeError, lib.call1, lib.cb2) + py.test.raises(TypeError, lib.call2, lib.cb1) + pt = lib.call1(lib.cb1) + assert (pt.x, pt.y) == (-9*500*999, 9*500*999) + pt = lib.call2(lib.cb2) + assert (pt.x, pt.y) == (99*500*999, -99*500*999) diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_parse_c_type.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_parse_c_type.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_parse_c_type.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_parse_c_type.py @@ -342,3 +342,17 @@ # not supported (really obscure): # "char[+5]" # "char['A']" + +def test_stdcall_cdecl(): + assert parse("int __stdcall(int)") == [Prim(lib._CFFI_PRIM_INT), + '->', Func(0), NoOp(4), FuncEnd(2), + Prim(lib._CFFI_PRIM_INT)] + assert parse("int __stdcall func(int)") == parse("int __stdcall(int)") + assert parse("int (__stdcall *)()") == [Prim(lib._CFFI_PRIM_INT), + NoOp(3), '->', Pointer(1), + Func(0), FuncEnd(2), 0] + assert parse("int (__stdcall *p)()") == parse("int (__stdcall*)()") + parse_error("__stdcall int", "identifier expected", 0) + parse_error("__cdecl int", "identifier expected", 0) + parse_error("int __stdcall", "expected '('", 13) + parse_error("int __cdecl", "expected '('", 11) diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_realize_c_type.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_realize_c_type.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_realize_c_type.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_realize_c_type.py @@ -1,5 +1,5 @@ # Generated by pypy/tool/import_cffi.py -import py +import py, sys from cffi import cffi_opcode @@ -47,3 +47,29 @@ def test_all_primitives(): for name in cffi_opcode.PRIMITIVE_TO_INDEX: check(name, name) + + +def check_func(input, expected_output=None): + import _cffi_backend + ffi = _cffi_backend.FFI() + ct = ffi.typeof(ffi.callback(input, lambda: None)) + assert isinstance(ct, ffi.CType) + if sys.platform != 'win32': + expected_output = expected_output.replace('__stdcall *', '*') + assert ct.cname == expected_output + +def test_funcptr_stdcall(): + check_func("int(int)", "int(*)(int)") + check_func("int foobar(int)", "int(*)(int)") + check_func("int __stdcall(int)", "int(__stdcall *)(int)") + check_func("int __stdcall foobar(int)", "int(__stdcall *)(int)") + check_func("void __cdecl(void)", "void(*)()") + check_func("void __cdecl foobar(void)", "void(*)()") + check_func("void __stdcall(void)", "void(__stdcall *)()") + check_func("void __stdcall foobar(long, short)", + "void(__stdcall *)(long, short)") + check_func("void(void __cdecl(void), void __stdcall(void))", + "void(*)(void(*)(), void(__stdcall *)())") + +def test_variadic_overrides_stdcall(): + check("void (__stdcall*)(int, ...)", "void(*)(int, ...)") diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py @@ -1281,3 +1281,200 @@ """) assert lib.aaa == 42 py.test.raises(AttributeError, "lib.aaa = 43") + +def test_win32_calling_convention_0(): + ffi = FFI() + ffi.cdef(""" + int call1(int(__cdecl *cb)(int)); + int (*const call2)(int(__stdcall *cb)(int)); + """) + lib = verify(ffi, 'test_win32_calling_convention_0', r""" + #ifndef _MSC_VER + # define __stdcall /* nothing */ + #endif + int call1(int(*cb)(int)) { + int i, result = 0; + //printf("call1: cb = %p\n", cb); + for (i = 0; i < 1000; i++) + result += cb(i); + //printf("result = %d\n", result); + return result; + } + int call2(int(__stdcall *cb)(int)) { + int i, result = 0; + //printf("call2: cb = %p\n", cb); + for (i = 0; i < 1000; i++) + result += cb(-i); + //printf("result = %d\n", result); + return result; + } + """) + @ffi.callback("int(int)") + def cb1(x): + return x * 2 + @ffi.callback("int __stdcall(int)") + def cb2(x): + return x * 3 + res = lib.call1(cb1) + assert res == 500*999*2 + assert res == ffi.addressof(lib, 'call1')(cb1) + res = lib.call2(cb2) + assert res == -500*999*3 + assert res == ffi.addressof(lib, 'call2')(cb2) + if sys.platform == 'win32': + assert '__stdcall' in str(ffi.typeof(cb2)) + assert '__stdcall' not in str(ffi.typeof(cb1)) + py.test.raises(TypeError, lib.call1, cb2) + py.test.raises(TypeError, lib.call2, cb1) + else: + assert '__stdcall' not in str(ffi.typeof(cb2)) + assert ffi.typeof(cb2) is ffi.typeof(cb1) + +def test_win32_calling_convention_1(): + ffi = FFI() + ffi.cdef(""" + int __cdecl call1(int(__cdecl *cb)(int)); + int __stdcall call2(int(__stdcall *cb)(int)); + int (__cdecl *const cb1)(int); + int (__stdcall *const cb2)(int); + """) + lib = verify(ffi, 'test_win32_calling_convention_1', r""" + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + int __cdecl cb1(int x) { return x * 2; } + int __stdcall cb2(int x) { return x * 3; } + + int __cdecl call1(int(__cdecl *cb)(int)) { + int i, result = 0; + //printf("here1\n"); + //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); + for (i = 0; i < 1000; i++) + result += cb(i); + //printf("result = %d\n", result); + return result; + } + int __stdcall call2(int(__stdcall *cb)(int)) { + int i, result = 0; + //printf("here1\n"); + //printf("cb = %p, cb2 = %p\n", cb, (void *)cb2); + for (i = 0; i < 1000; i++) + result += cb(-i); + //printf("result = %d\n", result); + return result; + } + """) + print '<<< cb1 =', ffi.addressof(lib, 'cb1') + ptr_call1 = ffi.addressof(lib, 'call1') + assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2 + assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2 + print '<<< cb2 =', ffi.addressof(lib, 'cb2') + ptr_call2 = ffi.addressof(lib, 'call2') + assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3 + assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3 + print '<<< done' + +def test_win32_calling_convention_2(): + # any mistake in the declaration of plain function (including the + # precise argument types and, here, the calling convention) are + # automatically corrected. But this does not apply to the 'cb' + # function pointer argument. + ffi = FFI() + ffi.cdef(""" + int __stdcall call1(int(__cdecl *cb)(int)); + int __cdecl call2(int(__stdcall *cb)(int)); + int (__cdecl *const cb1)(int); + int (__stdcall *const cb2)(int); + """) + lib = verify(ffi, 'test_win32_calling_convention_2', """ + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + int __cdecl call1(int(__cdecl *cb)(int)) { + int i, result = 0; + for (i = 0; i < 1000; i++) + result += cb(i); + return result; + } + int __stdcall call2(int(__stdcall *cb)(int)) { + int i, result = 0; + for (i = 0; i < 1000; i++) + result += cb(-i); + return result; + } + int __cdecl cb1(int x) { return x * 2; } + int __stdcall cb2(int x) { return x * 3; } + """) + ptr_call1 = ffi.addressof(lib, 'call1') + ptr_call2 = ffi.addressof(lib, 'call2') + if sys.platform == 'win32': + py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2')) + py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2')) + py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1')) + py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1')) + assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2 + assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2 + assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3 + assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3 + +def test_win32_calling_convention_3(): + ffi = FFI() + ffi.cdef(""" + struct point { int x, y; }; + + int (*const cb1)(struct point); + int (__stdcall *const cb2)(struct point); + + struct point __stdcall call1(int(*cb)(struct point)); + struct point call2(int(__stdcall *cb)(struct point)); + """) + lib = verify(ffi, 'test_win32_calling_convention_3', r""" + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + struct point { int x, y; }; + int cb1(struct point pt) { return pt.x + 10 * pt.y; } + int __stdcall cb2(struct point pt) { return pt.x + 100 * pt.y; } + struct point __stdcall call1(int(__cdecl *cb)(struct point)) { + int i; + struct point result = { 0, 0 }; + //printf("here1\n"); + //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); + for (i = 0; i < 1000; i++) { + struct point p = { i, -i }; + int r = cb(p); + result.x += r; + result.y -= r; + } + return result; + } + struct point __cdecl call2(int(__stdcall *cb)(struct point)) { + int i; + struct point result = { 0, 0 }; + for (i = 0; i < 1000; i++) { + struct point p = { -i, i }; + int r = cb(p); + result.x += r; + result.y -= r; + } + return result; + } + """) + ptr_call1 = ffi.addressof(lib, 'call1') + ptr_call2 = ffi.addressof(lib, 'call2') + if sys.platform == 'win32': + py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2')) + py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2')) + py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1')) + py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1')) + pt = lib.call1(ffi.addressof(lib, 'cb1')) + assert (pt.x, pt.y) == (-9*500*999, 9*500*999) + pt = ptr_call1(ffi.addressof(lib, 'cb1')) + assert (pt.x, pt.y) == (-9*500*999, 9*500*999) + pt = lib.call2(ffi.addressof(lib, 'cb2')) + assert (pt.x, pt.y) == (99*500*999, -99*500*999) + pt = ptr_call2(ffi.addressof(lib, 'cb2')) + assert (pt.x, pt.y) == (99*500*999, -99*500*999) diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_verify1.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_verify1.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_verify1.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_verify1.py @@ -1201,25 +1201,6 @@ lib = ffi.verify('#include <math.h>', libraries=lib_m) assert lib.sin(1.23) == math.sin(1.23) -def test_callback_calling_convention(): - py.test.skip("later") - if sys.platform != 'win32': - py.test.skip("Windows only") - ffi = FFI() - ffi.cdef(""" - int call1(int(*__cdecl cb)(int)); - int call2(int(*__stdcall cb)(int)); - """) - lib = ffi.verify(""" - int call1(int(*__cdecl cb)(int)) { - return cb(42) + 1; - } - int call2(int(*__stdcall cb)(int)) { - return cb(-42) - 6; - } - """) - xxx - def test_opaque_integer_as_function_result(): #import platform #if platform.machine().startswith('sparc'): diff --git a/rpython/annotator/annrpython.py b/rpython/annotator/annrpython.py --- a/rpython/annotator/annrpython.py +++ b/rpython/annotator/annrpython.py @@ -213,8 +213,6 @@ v = graph.getreturnvar() if v.annotation is None: self.setbinding(v, annmodel.s_ImpossibleValue) - # policy-dependent computation - self.bookkeeper.compute_at_fixpoint() def validate(self): """Check that the annotation results are valid""" @@ -292,6 +290,18 @@ graph, block, index = position_key self.reflowpendingblock(graph, block) + def call_sites(self): + newblocks = self.added_blocks + if newblocks is None: + newblocks = self.annotated # all of them + for block in newblocks: + for op in block.operations: + if op.opname in ('simple_call', 'call_args'): + yield op + + # some blocks are partially annotated + if op.result.annotation is None: + break # ignore the unannotated part #___ simplification (should be moved elsewhere?) _______ @@ -309,6 +319,7 @@ graphs[graph] = True for graph in graphs: simplify.eliminate_empty_blocks(graph) + self.bookkeeper.compute_at_fixpoint() if block_subset is None: perform_normalizations(self) @@ -396,8 +407,7 @@ i = 0 while i < len(block.operations): op = block.operations[i] - self.bookkeeper.enter((graph, block, i)) - try: + with self.bookkeeper.at_position((graph, block, i)): new_ops = op.transform(self) if new_ops is not None: block.operations[i:i+1] = new_ops @@ -406,8 +416,6 @@ new_ops[-1].result = op.result op = new_ops[0] self.consider_op(op) - finally: - self.bookkeeper.leave() i += 1 except BlockedInference as e: diff --git a/rpython/annotator/bookkeeper.py b/rpython/annotator/bookkeeper.py --- a/rpython/annotator/bookkeeper.py +++ b/rpython/annotator/bookkeeper.py @@ -5,6 +5,7 @@ from __future__ import absolute_import import sys, types, inspect, weakref +from contextlib import contextmanager from rpython.flowspace.model import Constant from rpython.annotator.model import (SomeOrderedDict, @@ -88,34 +89,29 @@ del TLS.bookkeeper del self.position_key + @contextmanager + def at_position(self, pos): + """A context manager calling `self.enter()` and `self.leave()`""" + if hasattr(self, 'position_key') and pos is None: + yield + return + self.enter(pos) + try: + yield + finally: + self.leave() + def compute_at_fixpoint(self): # getbookkeeper() needs to work during this function, so provide # one with a dummy position - self.enter(None) - try: - def call_sites(): - newblocks = self.annotator.added_blocks - if newblocks is None: - newblocks = self.annotator.annotated # all of them - annotation = self.annotator.annotation - for block in newblocks: - for op in block.operations: - if op.opname in ('simple_call', 'call_args'): - yield op - - # some blocks are partially annotated - if annotation(op.result) is None: - break # ignore the unannotated part - - for call_op in call_sites(): + with self.at_position(None): + for call_op in self.annotator.call_sites(): self.consider_call_site(call_op) for pbc, args_s in self.emulated_pbc_calls.itervalues(): args = simple_args(args_s) pbc.consider_call_site(args, s_ImpossibleValue, None) self.emulated_pbc_calls = {} - finally: - self.leave() def check_no_flags_on_instances(self): # sanity check: no flags attached to heap stored instances @@ -501,10 +497,6 @@ """Analyse a call to a SomePBC() with the given args (list of annotations). """ - descs = list(pbc.descriptions) - first = descs[0] - first.mergecallfamilies(*descs[1:]) - if emulated is None: whence = self.position_key # fish the existing annotation for the result variable, @@ -522,12 +514,9 @@ op = None s_previous_result = s_ImpossibleValue - def schedule(graph, inputcells): - return self.annotator.recursivecall(graph, whence, inputcells) - results = [] - for desc in descs: - results.append(desc.pycall(schedule, args, s_previous_result, op)) + for desc in pbc.descriptions: + results.append(desc.pycall(whence, args, s_previous_result, op)) s_result = unionof(*results) return s_result @@ -552,10 +541,7 @@ "replace" can be set to a list of old unique_key values to forget now, because the given "unique_key" replaces them. """ - emulate_enter = not hasattr(self, 'position_key') - if emulate_enter: - self.enter(None) - try: + with self.at_position(None): emulated_pbc_calls = self.emulated_pbc_calls prev = [unique_key] prev.extend(replace) @@ -570,9 +556,6 @@ else: emulated = callback return self.pbc_call(pbc, args, emulated=emulated) - finally: - if emulate_enter: - self.leave() def _find_current_op(self, opname=None, arity=None, pos=None, s_type=None): """ Find operation that is currently being annotated. Do some diff --git a/rpython/annotator/description.py b/rpython/annotator/description.py --- a/rpython/annotator/description.py +++ b/rpython/annotator/description.py @@ -53,6 +53,22 @@ table.append(row) self.total_calltable_size += 1 + def find_row(self, bookkeeper, descs, args, op): + shape = rawshape(args) + with bookkeeper.at_position(None): + row = build_calltable_row(descs, args, op) + index = self.calltable_lookup_row(shape, row) + return shape, index + +def build_calltable_row(descs, args, op): + # see comments in CallFamily + row = {} + for desc in descs: + graph = desc.get_graph(args, op) + assert isinstance(graph, FunctionGraph) + row[desc.rowkey()] = graph + return row + class FrozenAttrFamily(object): """A family of FrozenDesc objects that have any common 'getattr' sites. @@ -295,22 +311,23 @@ else: return self.specializer(self, inputcells) - def pycall(self, schedule, args, s_previous_result, op=None): + def pycall(self, whence, args, s_previous_result, op=None): inputcells = self.parse_arguments(args) result = self.specialize(inputcells, op) if isinstance(result, FunctionGraph): graph = result # common case + annotator = self.bookkeeper.annotator # if that graph has a different signature, we need to re-parse # the arguments. # recreate the args object because inputcells may have been changed new_args = args.unmatch_signature(self.signature, inputcells) inputcells = self.parse_arguments(new_args, graph) - result = schedule(graph, inputcells) + result = annotator.recursivecall(graph, whence, inputcells) signature = getattr(self.pyobj, '_signature_', None) if signature: sigresult = enforce_signature_return(self, signature[1], result) if sigresult is not None: - self.bookkeeper.annotator.addpendingblock( + annotator.addpendingblock( graph, graph.returnblock, [sigresult]) result = sigresult # Some specializations may break the invariant of returning @@ -320,6 +337,10 @@ result = unionof(result, s_previous_result) return result + def get_graph(self, args, op): + inputs_s = self.parse_arguments(args) + return self.specialize(inputs_s, op) + def get_call_parameters(self, args_s): args = simple_args(args_s) inputcells = self.parse_arguments(args) @@ -347,37 +368,15 @@ @staticmethod def consider_call_site(descs, args, s_result, op): + family = descs[0].getcallfamily() shape = rawshape(args) - row = FunctionDesc.row_to_consider(descs, args, op) - family = descs[0].getcallfamily() + row = build_calltable_row(descs, args, op) family.calltable_add_row(shape, row) - - @staticmethod - def variant_for_call_site(bookkeeper, family, descs, args, op): - shape = rawshape(args) - bookkeeper.enter(None) - try: - row = FunctionDesc.row_to_consider(descs, args, op) - finally: - bookkeeper.leave() - index = family.calltable_lookup_row(shape, row) - return shape, index + descs[0].mergecallfamilies(*descs[1:]) def rowkey(self): return self - @staticmethod - def row_to_consider(descs, args, op): - # see comments in CallFamily - row = {} - for desc in descs: - def enlist(graph, ignore): - row[desc.rowkey()] = graph - return s_ImpossibleValue # meaningless - desc.pycall(enlist, args, s_ImpossibleValue, op) - assert row - return row - def get_s_signatures(self, shape): family = self.getcallfamily() table = family.calltables.get(shape) @@ -624,7 +623,7 @@ "specialization" % (self.name,)) return self.getclassdef(None) - def pycall(self, schedule, args, s_previous_result, op=None): + def pycall(self, whence, args, s_previous_result, op=None): from rpython.annotator.model import SomeInstance, SomeImpossibleValue if self.specialize: if self.specialize == 'specialize:ctr_location': @@ -777,6 +776,8 @@ @staticmethod def consider_call_site(descs, args, s_result, op): + descs[0].getcallfamily() + descs[0].mergecallfamilies(*descs[1:]) from rpython.annotator.model import SomeInstance, SomePBC, s_None if len(descs) == 1: # call to a single class, look at the result annotation @@ -890,13 +891,20 @@ def getuniquegraph(self): return self.funcdesc.getuniquegraph() - def pycall(self, schedule, args, s_previous_result, op=None): + def func_args(self, args): from rpython.annotator.model import SomeInstance if self.selfclassdef is None: raise Exception("calling %r" % (self,)) s_instance = SomeInstance(self.selfclassdef, flags=self.flags) - args = args.prepend(s_instance) - return self.funcdesc.pycall(schedule, args, s_previous_result, op) + return args.prepend(s_instance) + + def pycall(self, whence, args, s_previous_result, op=None): + func_args = self.func_args(args) + return self.funcdesc.pycall(whence, func_args, s_previous_result, op) + + def get_graph(self, args, op): + func_args = self.func_args(args) + return self.funcdesc.get_graph(func_args, op) def bind_under(self, classdef, name): self.bookkeeper.warning("rebinding an already bound %r" % (self,)) @@ -913,9 +921,10 @@ def consider_call_site(descs, args, s_result, op): cnt, keys, star = rawshape(args) shape = cnt + 1, keys, star # account for the extra 'self' - row = FunctionDesc.row_to_consider(descs, args, op) + row = build_calltable_row(descs, args, op) family = descs[0].getcallfamily() family.calltable_add_row(shape, row) + descs[0].mergecallfamilies(*descs[1:]) def rowkey(self): # we are computing call families and call tables that always contain @@ -1064,19 +1073,28 @@ return '<MethodOfFrozenDesc %r of %r>' % (self.funcdesc, self.frozendesc) - def pycall(self, schedule, args, s_previous_result, op=None): + def func_args(self, args): from rpython.annotator.model import SomePBC s_self = SomePBC([self.frozendesc]) - args = args.prepend(s_self) - return self.funcdesc.pycall(schedule, args, s_previous_result, op) + return args.prepend(s_self) + + def pycall(self, whence, args, s_previous_result, op=None): + func_args = self.func_args(args) + return self.funcdesc.pycall(whence, func_args, s_previous_result, op) + + def get_graph(self, args, op): + func_args = self.func_args(args) + return self.funcdesc.get_graph(func_args, op) + @staticmethod def consider_call_site(descs, args, s_result, op): cnt, keys, star = rawshape(args) shape = cnt + 1, keys, star # account for the extra 'self' - row = FunctionDesc.row_to_consider(descs, args, op) + row = build_calltable_row(descs, args, op) family = descs[0].getcallfamily() family.calltable_add_row(shape, row) + descs[0].mergecallfamilies(*descs[1:]) def rowkey(self): return self.funcdesc diff --git a/rpython/annotator/specialize.py b/rpython/annotator/specialize.py --- a/rpython/annotator/specialize.py +++ b/rpython/annotator/specialize.py @@ -364,12 +364,6 @@ def specialize_argtype(funcdesc, args_s, *argindices): key = tuple([args_s[i].knowntype for i in argindices]) - for cls in key: - try: - assert '_must_specialize_' not in cls.classdesc.pyobj.__dict__, ( - "%s has the tag _must_specialize_" % (cls,)) - except AttributeError: - pass return maybe_star_args(funcdesc, key, args_s) def specialize_arglistitemtype(funcdesc, args_s, i): diff --git a/rpython/annotator/test/test_annrpython.py b/rpython/annotator/test/test_annrpython.py --- a/rpython/annotator/test/test_annrpython.py +++ b/rpython/annotator/test/test_annrpython.py @@ -1097,102 +1097,6 @@ assert acc1 is acc2 assert acc1.attrs.keys() == ['v1'] - def test_simple_pbc_call(self): - def f1(x,y=0): - pass - def f2(x): - pass - def f3(x): - pass - def g(f): - f(1) - def h(): - f1(1) - f1(1,2) - g(f2) - g(f3) - - a = self.RPythonAnnotator() - s = a.build_types(h, []) - - fdesc1 = a.bookkeeper.getdesc(f1) - fdesc2 = a.bookkeeper.getdesc(f2) - fdesc3 = a.bookkeeper.getdesc(f3) - - fam1 = fdesc1.getcallfamily() - fam2 = fdesc2.getcallfamily() - fam3 = fdesc3.getcallfamily() - - assert fam1 is not fam2 - assert fam1 is not fam3 - assert fam3 is fam2 - - gf1 = graphof(a, f1) - gf2 = graphof(a, f2) - gf3 = graphof(a, f3) - - assert fam1.calltables == {(2, (), False): [{fdesc1: gf1}], - (1, (), False): [{fdesc1: gf1}]} - assert fam2.calltables == {(1, (), False): [{fdesc2: gf2, fdesc3: gf3}]} - - def test_pbc_call_ins(self): - class A(object): - def m(self): - pass - class B(A): - def n(self): - pass - class C(A): - def __init__(self): - pass - def m(self): - pass - def f(x): - b = B() - c = C() - b.n() - if x: - a = b - else: - a = c - a.m() - - a = self.RPythonAnnotator() - s = a.build_types(f, [bool]) - - clsdef = a.bookkeeper.getuniqueclassdef - bookkeeper = a.bookkeeper - - def getmdesc(bmeth): - return bookkeeper.immutablevalue(bmeth).any_description() - - mdescA_m = getmdesc(A().m) - mdescC_m = getmdesc(C().m) - mdescB_n = getmdesc(B().n) - - assert mdescA_m.name == 'm' == mdescC_m.name - assert mdescB_n.name == 'n' - - famA_m = mdescA_m.getcallfamily() - famC_m = mdescC_m.getcallfamily() - famB_n = mdescB_n.getcallfamily() - - assert famA_m is famC_m - assert famB_n is not famA_m - - gfB_n = graphof(a, B.n.im_func) - gfA_m = graphof(a, A.m.im_func) - gfC_m = graphof(a, C.m.im_func) - - assert famB_n.calltables == {(1, (), False): [{mdescB_n.funcdesc: gfB_n}] } - assert famA_m.calltables == {(1, (), False): [{mdescA_m.funcdesc: gfA_m, mdescC_m.funcdesc: gfC_m }] } - - mdescCinit = getmdesc(C().__init__) - famCinit = mdescCinit.getcallfamily() - gfCinit = graphof(a, C.__init__.im_func) - - assert famCinit.calltables == {(1, (), False): [{mdescCinit.funcdesc: gfCinit}] } - def test_isinstance_unsigned_1(self): def f(x): return isinstance(x, r_uint) @@ -2153,6 +2057,7 @@ s_f = a.bookkeeper.immutablevalue(f) a.bookkeeper.emulate_pbc_call('f', s_f, [annmodel.SomeInteger(), annmodel.SomeInteger()]) a.complete() + a.simplify() assert a.binding(graphof(a, f).getreturnvar()).knowntype == int fdesc = a.bookkeeper.getdesc(f) @@ -3969,28 +3874,6 @@ e = py.test.raises(Exception, a.build_types, f, [int]) assert "field '_my_lst' was migrated" in str(e.value) - def test_call_classes_with_noarg_init(self): - class A: - foo = 21 - class B(A): - foo = 22 - class C(A): - def __init__(self): - self.foo = 42 - class D(A): - def __init__(self): - self.foo = 43 - def f(i): - if i == 1: - cls = B - elif i == 2: - cls = D - else: - cls = C - return cls().foo - a = self.RPythonAnnotator() - py.test.raises(Exception, a.build_types, f, [int]) - def test_range_variable_step(self): def g(n): return range(0, 10, n) diff --git a/rpython/annotator/test/test_annsimplifyrpython.py b/rpython/annotator/test/test_annsimplifyrpython.py --- a/rpython/annotator/test/test_annsimplifyrpython.py +++ b/rpython/annotator/test/test_annsimplifyrpython.py @@ -1,5 +1,8 @@ -import rpython.annotator.test.test_annrpython -parent = rpython.annotator.test.test_annrpython.TestAnnotateTestCase +import py + + +from rpython.annotator.test.test_annrpython import graphof +from rpython.annotator.test.test_annrpython import TestAnnotateTestCase as parent class TestAnnotateAndSimplifyTestCase(parent): @@ -12,3 +15,122 @@ parent.RPythonAnnotator.complete(self) if self.translator is not None: self.simplify() + + def test_simple_pbc_call(self): + def f1(x,y=0): + pass + def f2(x): + pass + def f3(x): + pass + def g(f): + f(1) + def h(): + f1(1) + f1(1,2) + g(f2) + g(f3) + + a = self.RPythonAnnotator() + s = a.build_types(h, []) + + fdesc1 = a.bookkeeper.getdesc(f1) + fdesc2 = a.bookkeeper.getdesc(f2) + fdesc3 = a.bookkeeper.getdesc(f3) + _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit