Source

pypy / lib_pypy / _ctypes / function.py

Full commit
Amaury Forgeot d… 8285c02 

Antonio Cuni ff00970 
Amaury Forgeot d… 8285c02 
Antonio Cuni f5f3c36 
Amaury Forgeot d… 8285c02 
Daniel Roberts 0077cf5 
Antonio Cuni 11670ac 
Daniel Roberts 0077cf5 

Anders Hammarqui… 78112cf 
Daniel Roberts 0077cf5 
tav b16c379 
Daniel Roberts 0077cf5 




tav b16c379 










Antonio Cuni a7c2737 
Daniel Roberts 0077cf5 





Amaury Forgeot d… 869b900 





Antonio Cuni acde19d 
Daniel Roberts 0077cf5 













Antonio Cuni 220cb30 
Daniel Roberts 0077cf5 




Hakan Ardo 2447f54 
Daniel Roberts 0077cf5 








Antonio Cuni 11670ac 
Daniel Roberts 0077cf5 



Antonio Cuni 7ab0354 
Daniel Roberts 0077cf5 
Anders Hammarqui… 78112cf 

Daniel Roberts 0077cf5 

tav b16c379 
Daniel Roberts 0077cf5 

Hakan Ardo 2447f54 
tav b16c379 
Hakan Ardo 2447f54 





Antonio Cuni 91741f4 
Antonio Cuni d242142 
Antonio Cuni 7039913 

Antonio Cuni 26cd788 
Daniel Roberts 0077cf5 

tav b16c379 











































Antonio Cuni a7c2737 
Daniel Roberts 0077cf5 

tav b16c379 
Daniel Roberts 0077cf5 
Anders Hammarqui… 78112cf 
Daniel Roberts 0077cf5 



tav b16c379 


Daniel Roberts 0077cf5 
Antonio Cuni a7c2737 
Daniel Roberts 0077cf5 


Antonio Cuni a7c2737 
Daniel Roberts 0077cf5 

Hakan Ardo 2447f54 












Daniel Roberts 0077cf5 
Amaury Forgeot d… 869b900 

Daniel Roberts 0077cf5 

Amaury Forgeot d… 70472b8 
Hakan Ardo 2447f54 
Daniel Roberts 0077cf5 




Antonio Cuni fc183ee 







Daniel Roberts 0077cf5 




tav 04456b4 

Antonio Cuni a7c2737 
tav 04456b4 

tav b16c379 

tav 04456b4 

tav b16c379 
Antonio Cuni fc183ee 
Antonio Cuni f5f3c36 




tav 04456b4 

Antonio Cuni a7c2737 

tav b16c379 
Daniel Roberts 0077cf5 

Amaury Forgeot d… b699704 

Amaury Forgeot d… 8285c02 


Daniel Roberts 0077cf5 
tav 04456b4 



Daniel Roberts 0077cf5 
tav b16c379 
tav 04456b4 
Amaury Forgeot d… 8285c02 
tav 04456b4 

tav b16c379 


tav 04456b4 
tav b16c379 
Daniel Roberts 0077cf5 
Antonio Cuni fc183ee 
tav 04456b4 
Daniel Roberts 0077cf5 
tav 04456b4 

tav b16c379 
Daniel Roberts 0077cf5 
tav 04456b4 
tav b16c379 






tav 04456b4 
Daniel Roberts 0077cf5 
tav 04456b4 
Daniel Roberts 0077cf5 








tav 04456b4 
tav b16c379 
Daniel Roberts 0077cf5 
tav b16c379 
Amaury Forgeot d… ab15ce4 

tav b16c379 

Amaury Forgeot d… ab15ce4 

tav b16c379 
Amaury Forgeot d… ab15ce4 




Amaury Forgeot d… 8285c02 
Amaury Forgeot d… ab15ce4 

Amaury Forgeot d… 8285c02 
Amaury Forgeot d… ab15ce4 
Amaury Forgeot d… dff8741 
Amaury Forgeot d… 8285c02 

Antonio Cuni a7c2737 
Amaury Forgeot d… dff8741 
Daniel Roberts 0077cf5 









tav b16c379 

Anders Hammarqui… 78112cf 

tav b16c379 
Anders Hammarqui… 78112cf 



Daniel Roberts 0077cf5 


tav b16c379 



Antonio Cuni 634a87b 



Daniel Roberts 0077cf5 

Antonio Cuni 634a87b 
tav b16c379 
Antonio Cuni b9d43da 

Antonio Cuni e57e796 
Hakan Ardo 2447f54 
tav b16c379 

Antonio Cuni 634a87b 




tav b16c379 


Daniel Roberts 0077cf5 
Antonio Cuni b9d43da 
Amaury Forgeot d… 6981cda 
Amaury Forgeot d… 8285c02 




Antonio Cuni ff00970 
Amaury Forgeot d… 8285c02 




Antonio Cuni b9d43da 

Amaury Forgeot d… 6981cda 
Antonio Cuni e57e796 












Antonio Cuni f5f3c36 
Antonio Cuni fc183ee 
Antonio Cuni f5f3c36 

Antonio Cuni 11670ac 
Amaury Forgeot d… 6981cda 
Daniel Roberts 0077cf5 
Antonio Cuni d242142 
Daniel Roberts 0077cf5 




Antonio Cuni f5f3c36 
Antonio Cuni 26cd788 
Daniel Roberts 0077cf5 






Antonio Cuni 634a87b 



Amaury Forgeot d… 8285c02 
Daniel Roberts 0077cf5 

Antonio Cuni f5f3c36 

Antonio Cuni e395eef 

Daniel Roberts 0077cf5 


Amaury Forgeot d… 8285c02 
tav b16c379 



Daniel Roberts 0077cf5 





Amaury Forgeot d… 949f18d 



Daniel Roberts 0077cf5 



Antonio Cuni 8cf4b50 






Hakan Ardo 2447f54 




Antonio Cuni 8cf4b50 
Antonio Cuni 8b232fc 





Hakan Ardo 2447f54 








Amaury Forgeot d… 6981cda 
Antonio Cuni 8cf4b50 
Hakan Ardo 2447f54 
tav b16c379 
Antonio Cuni 8cf4b50 
tav b16c379 
Antonio Cuni 8cf4b50 
tav b16c379 

Antonio Cuni 634a87b 
tav b16c379 



Daniel Roberts 0077cf5 
tav b16c379 




tav 04456b4 


tav b16c379 
tav 04456b4 
tav b16c379 
tav 04456b4 
tav b16c379 




Daniel Roberts 0077cf5 
Antonio Cuni 8cf4b50 


tav b16c379 












Antonio Cuni a7c2737 


tav b16c379 


Antonio Cuni a7c2737 
tav b16c379 



Antonio Cuni a7c2737 



Daniel Roberts 0077cf5 
tav b16c379 


Antonio Cuni a7c2737 
tav b16c379 

Antonio Cuni a7c2737 

tav b16c379 
Daniel Roberts 0077cf5 
Antonio Cuni 8cf4b50 

Hakan Ardo 2447f54 
Daniel Roberts 0077cf5 
Antonio Cuni 8cf4b50 
Hakan Ardo 2447f54 
Daniel Roberts 0077cf5 
Antonio Cuni 8cf4b50 

Antonio Cuni a7c2737 
Daniel Roberts 0077cf5 
Antonio Cuni 0348eb2 
Antonio Cuni 6c9297e 


Antonio Cuni 81b4de5 
Antonio Cuni 6c9297e 
Antonio Cuni 4b3cb10 




Antonio Cuni 7b629fa 
Antonio Cuni 4dc222c 
Antonio Cuni 36b0190 
Antonio Cuni 7b629fa 
Antonio Cuni 2a072ec 
Antonio Cuni 7b629fa 


Antonio Cuni 6c9297e 
Antonio Cuni af6744a 
Amaury Forgeot d… 8285c02 




Antonio Cuni 6af28bf 









Amaury Forgeot d… 8285c02 
Amaury Forgeot d… 6981cda 
Antonio Cuni 634a87b 
Amaury Forgeot d… 8285c02 

Antonio Cuni fffe710 
Amaury Forgeot d… 8285c02 



Antonio Cuni 634a87b 






Amaury Forgeot d… 8285c02 
Antonio Cuni 6af28bf 
Amaury Forgeot d… 8285c02 
Antonio Cuni 6af28bf 
Amaury Forgeot d… 6981cda 
Amaury Forgeot d… 8285c02 
Daniel Roberts 0077cf5 

Amaury Forgeot d… 6981cda 
Daniel Roberts 0077cf5 











Antonio Cuni 91741f4 

Antonio Cuni 7039913 
Antonio Cuni 7ab0354 


Antonio Cuni 6b4e7c6 
Antonio Cuni 7039913 
Antonio Cuni 6b4e7c6 

Antonio Cuni 91741f4 
Antonio Cuni d242142 
Antonio Cuni 91741f4 
Antonio Cuni 7ab0354 


Antonio Cuni 7039913 


Antonio Cuni e57e796 
Antonio Cuni 7039913 

Antonio Cuni 0045269 









Antonio Cuni 1c05561 


Antonio Cuni 92925fa 
Antonio Cuni 1c05561 
Antonio Cuni f93078f 


Antonio Cuni 92925fa 
Antonio Cuni f93078f 
Antonio Cuni 91741f4 

Antonio Cuni d242142 
Antonio Cuni 91741f4 

Antonio Cuni d242142 

Antonio Cuni e57e796 
Antonio Cuni af450a4 
Antonio Cuni 7ab0354 

Antonio Cuni d3e6983 
Antonio Cuni 91741f4 
Antonio Cuni 7039913 
Antonio Cuni d242142 
Antonio Cuni 7039913 
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682

from _ctypes.basics import _CData, _CDataMeta, cdata_from_address
from _ctypes.primitive import SimpleType, _SimpleCData
from _ctypes.basics import ArgumentError, keepalive_key
from _ctypes.basics import is_struct_shape
from _ctypes.builtin import set_errno, set_last_error
import _rawffi
import _ffi
import sys
import traceback
import warnings


# XXX this file needs huge refactoring I fear

PARAMFLAG_FIN   = 0x1
PARAMFLAG_FOUT  = 0x2
PARAMFLAG_FLCID = 0x4
PARAMFLAG_COMBINED = PARAMFLAG_FIN | PARAMFLAG_FOUT | PARAMFLAG_FLCID

VALID_PARAMFLAGS = (
    0,
    PARAMFLAG_FIN,
    PARAMFLAG_FIN | PARAMFLAG_FOUT,
    PARAMFLAG_FIN | PARAMFLAG_FLCID
    )

WIN64 = sys.platform == 'win32' and sys.maxint == 2**63 - 1


def get_com_error(errcode, riid, pIunk):
    "Win32 specific: build a COM Error exception"
    # XXX need C support code
    from _ctypes import COMError
    return COMError(errcode, None, None)

def call_function(func, args):
    "Only for debugging so far: So that we can call CFunction instances"
    funcptr = CFuncPtr(func)
    funcptr.restype = int
    return funcptr(*args)


class CFuncPtrType(_CDataMeta):
    # XXX write down here defaults and such things

    def _sizeofinstances(self):
        return _rawffi.sizeof('P')

    def _alignmentofinstances(self):
        return _rawffi.alignment('P')

    def _is_pointer_like(self):
        return True

    from_address = cdata_from_address


class CFuncPtr(_CData):
    __metaclass__ = CFuncPtrType

    _argtypes_ = None
    _restype_ = None
    _errcheck_ = None
    _flags_ = 0
    _ffiargshape = 'P'
    _ffishape = 'P'
    _fficompositesize = None
    _ffiarray = _rawffi.Array('P')
    _needs_free = False
    callable = None
    _ptr = None
    _buffer = None
    _address = None
    # win32 COM properties
    _paramflags = None
    _com_index = None
    _com_iid = None
    _is_fastpath = False

    __restype_set = False

    def _getargtypes(self):
        return self._argtypes_

    def _setargtypes(self, argtypes):
        self._ptr = None
        if argtypes is None:
            self._argtypes_ = ()
        else:
            for i, argtype in enumerate(argtypes):
                if not hasattr(argtype, 'from_param'):
                    raise TypeError(
                        "item %d in _argtypes_ has no from_param method" % (
                            i + 1,))
            #
            if all([hasattr(argtype, '_ffiargshape') for argtype in argtypes]):
                fastpath_cls = make_fastpath_subclass(self.__class__)
                fastpath_cls.enable_fastpath_maybe(self)
            self._argtypes_ = list(argtypes)
    argtypes = property(_getargtypes, _setargtypes)

    def _getparamflags(self):
        return self._paramflags

    def _setparamflags(self, paramflags):
        if paramflags is None or not self._argtypes_:
            self._paramflags = None
            return
        if not isinstance(paramflags, tuple):
            raise TypeError("paramflags must be a tuple or None")
        if len(paramflags) != len(self._argtypes_):
            raise ValueError("paramflags must have the same length as argtypes")
        for idx, paramflag in enumerate(paramflags):
            paramlen = len(paramflag)
            name = default = None
            if paramlen == 1:
                flag = paramflag[0]
            elif paramlen == 2:
                flag, name = paramflag
            elif paramlen == 3:
                flag, name, default = paramflag
            else:
                raise TypeError(
                    "paramflags must be a sequence of (int [,string [,value]]) "
                    "tuples"
                    )
            if not isinstance(flag, int):
                raise TypeError(
                    "paramflags must be a sequence of (int [,string [,value]]) "
                    "tuples"
                    )
            _flag = flag & PARAMFLAG_COMBINED
            if _flag == PARAMFLAG_FOUT:
                typ = self._argtypes_[idx]
                if getattr(typ, '_ffiargshape', None) not in ('P', 'z', 'Z'):
                    raise TypeError(
                        "'out' parameter %d must be a pointer type, not %s"
                        % (idx+1, type(typ).__name__)
                        )
            elif _flag not in VALID_PARAMFLAGS:
                raise TypeError("paramflag value %d not supported" % flag)
        self._paramflags = paramflags

    paramflags = property(_getparamflags, _setparamflags)


    def _getrestype(self):
        return self._restype_

    def _setrestype(self, restype):
        self.__restype_set = True
        self._ptr = None
        if restype is int:
            from ctypes import c_int
            restype = c_int
        if not (isinstance(restype, _CDataMeta) or restype is None or
                callable(restype)):
            raise TypeError("restype must be a type, a callable, or None")
        self._restype_ = restype
        
    def _delrestype(self):
        self._ptr = None
        del self._restype_
        
    restype = property(_getrestype, _setrestype, _delrestype)

    def _geterrcheck(self):
        return getattr(self, '_errcheck_', None)
    def _seterrcheck(self, errcheck):
        if not callable(errcheck):
            raise TypeError("The errcheck attribute must be callable")
        self._errcheck_ = errcheck
    def _delerrcheck(self):
        try:
            del self._errcheck_
        except AttributeError:
            pass
    errcheck = property(_geterrcheck, _seterrcheck, _delerrcheck)

    def _ffishapes(self, args, restype):
        if args is None:
            args = []
        argtypes = [arg._ffiargshape for arg in args]
        if restype is not None:
            if not isinstance(restype, SimpleType):
                raise TypeError("invalid result type for callback function")
            restype = restype._ffiargshape
        else:
            restype = 'O' # void
        return argtypes, restype

    def _set_address(self, address):
        if not self._buffer:
            self._buffer = _rawffi.Array('P')(1)
        self._buffer[0] = address

    def _get_address(self):
        return self._buffer[0]

    def __init__(self, *args):
        self.name = None
        self._objects = {keepalive_key(0):self}
        self._needs_free = True

        # Empty function object -- this is needed for casts
        if not args:
            self._set_address(0)
            return

        argsl = list(args)
        argument = argsl.pop(0)

        # Direct construction from raw address
        if isinstance(argument, (int, long)) and not argsl:
            self._set_address(argument)
            restype = self._restype_
            if restype is None:
                import ctypes
                restype = ctypes.c_int
            self._ptr = self._getfuncptr_fromaddress(self._argtypes_, restype)
            return

        
        # A callback into python
        if callable(argument) and not argsl:
            self.callable = argument
            ffiargs, ffires = self._ffishapes(self._argtypes_, self._restype_)
            if self._restype_ is None:
                ffires = None
            self._ptr = _rawffi.CallbackPtr(self._wrap_callable(argument,
                                                                self.argtypes),
                                            ffiargs, ffires, self._flags_)
            self._buffer = self._ptr.byptr()
            return

        # Function exported from a shared library
        if isinstance(argument, tuple) and len(argument) == 2:
            import ctypes
            self.name, dll = argument
            if isinstance(dll, str):
                self.dll = ctypes.CDLL(self.dll)
            else:
                self.dll = dll
            if argsl:
                self.paramflags = argsl.pop(0)
                if argsl:
                    raise TypeError("Unknown constructor %s" % (args,))
            # We need to check dll anyway
            ptr = self._getfuncptr([], ctypes.c_int)
            self._set_address(ptr.getaddr())
            return

        # A COM function call, by index
        if (sys.platform == 'win32' and isinstance(argument, (int, long))
            and argsl):
            ffiargs, ffires = self._ffishapes(self._argtypes_, self._restype_)
            self._com_index =  argument + 0x1000
            self.name = argsl.pop(0)
            if argsl:
                self.paramflags = argsl.pop(0)
                if argsl:
                    self._com_iid = argsl.pop(0)
                    if argsl:
                        raise TypeError("Unknown constructor %s" % (args,))
            return

        raise TypeError("Unknown constructor %s" % (args,))

    def _wrap_callable(self, to_call, argtypes):
        def f(*args):
            if argtypes:
                args = [argtype._CData_retval(argtype.from_address(arg)._buffer)
                        for argtype, arg in zip(argtypes, args)]
            return to_call(*args)
        return f
    
    def __call__(self, *args, **kwargs):
        argtypes = self._argtypes_
        if self.callable is not None:
            if len(args) == len(argtypes):
                pass
            elif self._flags_ & _rawffi.FUNCFLAG_CDECL:
                if len(args) < len(argtypes):
                    plural = len(argtypes) > 1 and "s" or ""
                    raise TypeError(
                        "This function takes at least %d argument%s (%s given)"
                        % (len(argtypes), plural, len(args)))
                else:
                    # For cdecl functions, we allow more actual arguments
                    # than the length of the argtypes tuple.
                    args = args[:len(self._argtypes_)]
            else:
                plural = len(self._argtypes_) > 1 and "s" or ""
                raise TypeError(
                    "This function takes %d argument%s (%s given)"
                    % (len(self._argtypes_), plural, len(args)))

            # check that arguments are convertible
            ## XXX Not as long as ctypes.cast is a callback function with
            ## py_object arguments...
            ## self._convert_args(self._argtypes_, args, {})

            try:
                res = self.callable(*args)
            except:
                exc_info = sys.exc_info()
                traceback.print_tb(exc_info[2], file=sys.stderr)
                print >>sys.stderr, "%s: %s" % (exc_info[0].__name__, exc_info[1])
                return 0
            if self._restype_ is not None:
                return res
            return

        if argtypes is None:
            warnings.warn('C function without declared arguments called',
                          RuntimeWarning, stacklevel=2)
            argtypes = []
            
        if not self.__restype_set:
            warnings.warn('C function without declared return type called',
                          RuntimeWarning, stacklevel=2)

        if self._com_index:
            from ctypes import cast, c_void_p, POINTER
            if not args:
                raise ValueError(
                    "native COM method call without 'this' parameter"
                    )
            thisarg = cast(args[0], POINTER(POINTER(c_void_p)))
            newargs, argtypes, outargs = self._convert_args(argtypes, args[1:], kwargs)
            newargs.insert(0, args[0].value)
            argtypes.insert(0, c_void_p)
        else:
            thisarg = None
            newargs, argtypes, outargs = self._convert_args(argtypes, args, kwargs)

        funcptr = self._getfuncptr(argtypes, self._restype_, thisarg)
        result = self._call_funcptr(funcptr, *newargs)
        result = self._do_errcheck(result, args)

        if not outargs:
            return result

        simple_cdata = type(c_void_p()).__bases__[0]
        outargs = [x.value if type(x).__bases__[0] is simple_cdata else x
                   for x in outargs]

        if len(outargs) == 1:
            return outargs[0]
        return tuple(outargs)

    def _call_funcptr(self, funcptr, *newargs):

        if self._flags_ & _rawffi.FUNCFLAG_USE_ERRNO:
            set_errno(_rawffi.get_errno())
        if self._flags_ & _rawffi.FUNCFLAG_USE_LASTERROR:
            set_last_error(_rawffi.get_last_error())
        try:
            result = funcptr(*newargs)
        finally:
            if self._flags_ & _rawffi.FUNCFLAG_USE_ERRNO:
                set_errno(_rawffi.get_errno())
            if self._flags_ & _rawffi.FUNCFLAG_USE_LASTERROR:
                set_last_error(_rawffi.get_last_error())
        #
        return self._build_result(self._restype_, result, newargs)

    def _do_errcheck(self, result, args):
        # The 'errcheck' protocol
        if self._errcheck_:
            v = self._errcheck_(result, self, args)
            # If the errcheck funtion failed, let it throw
            # If the errcheck function returned newargs unchanged,
            # continue normal processing.
            # If the errcheck function returned something else,
            # use that as result.
            if v is not args:
                return v
        return result

    def _getfuncptr_fromaddress(self, argtypes, restype):
        address = self._get_address()
        ffiargs = [argtype.get_ffi_argtype() for argtype in argtypes]
        ffires = restype.get_ffi_argtype()
        return _ffi.FuncPtr.fromaddr(address, '', ffiargs, ffires)

    def _getfuncptr(self, argtypes, restype, thisarg=None):
        if self._ptr is not None and (argtypes is self._argtypes_ or argtypes == self._argtypes_):
            return self._ptr
        if restype is None or not isinstance(restype, _CDataMeta):
            import ctypes
            restype = ctypes.c_int
        if self._buffer is not None:
            ptr = self._getfuncptr_fromaddress(argtypes, restype)
            if argtypes == self._argtypes_:
                self._ptr = ptr
            return ptr

        if self._com_index:
            # extract the address from the object's virtual table
            if not thisarg:
                raise ValueError("COM method call without VTable")
            ptr = thisarg[0][self._com_index - 0x1000]
            ffiargs = [argtype.get_ffi_argtype() for argtype in argtypes]
            ffires = restype.get_ffi_argtype()
            return _ffi.FuncPtr.fromaddr(ptr, '', ffiargs, ffires)
        
        cdll = self.dll._handle
        try:
            ffi_argtypes = [argtype.get_ffi_argtype() for argtype in argtypes]
            ffi_restype = restype.get_ffi_argtype()
            self._ptr = cdll.getfunc(self.name, ffi_argtypes, ffi_restype)
            return self._ptr
        except AttributeError:
            if self._flags_ & _rawffi.FUNCFLAG_CDECL:
                raise

            # Win64 has no stdcall calling conv, so it should also not have the
            # name mangling of it.
            if WIN64:
                raise
            # For stdcall, try mangled names:
            # funcname -> _funcname@<n>
            # where n is 0, 4, 8, 12, ..., 128
            for i in range(33):
                mangled_name = "_%s@%d" % (self.name, i*4)
                try:
                    return cdll.getfunc(mangled_name,
                                        ffi_argtypes, ffi_restype,
                                        # XXX self._flags_
                                        )
                except AttributeError:
                    pass
            raise

    @classmethod
    def _conv_param(cls, argtype, arg):
        if isinstance(argtype, _CDataMeta):
            #arg = argtype.from_param(arg)
            arg = argtype.get_ffi_param(arg)
            return arg, argtype
        
        if argtype is not None:
            arg = argtype.from_param(arg)
        if hasattr(arg, '_as_parameter_'):
            arg = arg._as_parameter_
        if isinstance(arg, _CData):
            return arg._to_ffi_param(), type(arg)
        #
        # non-usual case: we do the import here to save a lot of code in the
        # jit trace of the normal case
        from ctypes import c_char_p, c_wchar_p, c_void_p, c_int
        #
        if isinstance(arg, str):
            cobj = c_char_p(arg)
        elif isinstance(arg, unicode):
            cobj = c_wchar_p(arg)
        elif arg is None:
            cobj = c_void_p()
        elif isinstance(arg, (int, long)):
            cobj = c_int(arg)
        else:
            raise TypeError("Don't know how to handle %s" % (arg,))

        return cobj._to_ffi_param(), type(cobj)

    def _convert_args(self, argtypes, args, kwargs, marker=object()):
        newargs = []
        outargs = []
        newargtypes = []
        total = len(args)
        paramflags = self._paramflags
        inargs_idx = 0

        if not paramflags and total < len(argtypes):
            raise TypeError("not enough arguments")

        for i, argtype in enumerate(argtypes):
            flag = 0
            name = None
            defval = marker
            if paramflags:
                paramflag = paramflags[i]
                paramlen = len(paramflag)
                name = None
                if paramlen == 1:
                    flag = paramflag[0]
                elif paramlen == 2:
                    flag, name = paramflag
                elif paramlen == 3:
                    flag, name, defval = paramflag
                flag = flag & PARAMFLAG_COMBINED
                if flag == PARAMFLAG_FIN | PARAMFLAG_FLCID:
                    val = defval
                    if val is marker:
                        val = 0
                    newarg, newargtype = self._conv_param(argtype, val)
                    newargs.append(newarg)
                    newargtypes.append(newargtype)
                elif flag in (0, PARAMFLAG_FIN):
                    if inargs_idx < total:
                        val = args[inargs_idx]
                        inargs_idx += 1
                    elif kwargs and name in kwargs:
                        val = kwargs[name]
                        inargs_idx += 1
                    elif defval is not marker:
                        val = defval
                    elif name:
                        raise TypeError("required argument '%s' missing" % name)
                    else:
                        raise TypeError("not enough arguments")
                    newarg, newargtype = self._conv_param(argtype, val)
                    newargs.append(newarg)
                    newargtypes.append(newargtype)
                elif flag == PARAMFLAG_FOUT:
                    if defval is not marker:
                        outargs.append(defval)
                        newarg, newargtype = self._conv_param(argtype, defval)
                    else:
                        import ctypes
                        val = argtype._type_()
                        outargs.append(val)
                        newarg = ctypes.byref(val)
                        newargtype = type(newarg)
                    newargs.append(newarg)
                    newargtypes.append(newargtype)
                else:
                    raise ValueError("paramflag %d not yet implemented" % flag)
            else:
                try:
                    newarg, newargtype = self._conv_param(argtype, args[i])
                except (UnicodeError, TypeError, ValueError), e:
                    raise ArgumentError(str(e))
                newargs.append(newarg)
                newargtypes.append(newargtype)
                inargs_idx += 1

        if len(newargs) < len(args):
            extra = args[len(newargs):]
            for i, arg in enumerate(extra):
                try:
                    newarg, newargtype = self._conv_param(None, arg)
                except (UnicodeError, TypeError, ValueError), e:
                    raise ArgumentError(str(e))
                newargs.append(newarg)
                newargtypes.append(newargtype)
        return newargs, newargtypes, outargs

    
    def _wrap_result(self, restype, result):
        """
        Convert from low-level repr of the result to the high-level python
        one.
        """
        # hack for performance: if restype is a "simple" primitive type, don't
        # allocate the buffer because it's going to be thrown away immediately
        if restype.__bases__[0] is _SimpleCData and not restype._is_pointer_like():
            return result
        #
        shape = restype._ffishape
        if is_struct_shape(shape):
            buf = result
        else:
            buf = _rawffi.Array(shape)(1, autofree=True)
            buf[0] = result
        retval = restype._CData_retval(buf)
        return retval

    def _build_result(self, restype, result, argsandobjs):
        """Build the function result:
           If there is no OUT parameter, return the actual function result
           If there is one OUT parameter, return it
           If there are many OUT parameters, return a tuple"""

        # XXX: note for the future: the function used to take a "resbuffer",
        # i.e. an array of ints. Now it takes a result, which is already a
        # python object. All places that do "resbuffer[0]" should check that
        # result is actually an int and just use it.
        #
        # Also, argsandobjs used to be "args" in __call__, now it's "newargs"
        # (i.e., the already unwrapped objects). It's used only when we have a
        # PARAMFLAG_FOUT and it's probably wrong, I'll fix it when I find a
        # failing test

        retval = None

        if restype is not None:
            checker = getattr(self.restype, '_check_retval_', None)
            if checker:
                val = restype(result)
                # the original ctypes seems to make the distinction between
                # classes defining a new type, and their subclasses
                if '_type_' in restype.__dict__:
                    val = val.value
                # XXX Raise a COMError when restype is HRESULT and
                # checker(val) fails.  How to check for restype == HRESULT?
                if self._com_index:
                    if result & 0x80000000:
                        raise get_com_error(result, None, None)
                else:
                    retval = checker(val)
            elif not isinstance(restype, _CDataMeta):
                retval = restype(result)
            else:
                retval = self._wrap_result(restype, result)

        return retval

    def __nonzero__(self):
        return self._com_index is not None or bool(self._buffer[0])

    def __del__(self):
        if self._needs_free:
            # XXX we need to find a bad guy here
            if self._buffer is None:
                return
            self._buffer.free()
            self._buffer = None
            if isinstance(self._ptr, _rawffi.CallbackPtr):
                self._ptr.free()
                self._ptr = None
            self._needs_free = False


def make_fastpath_subclass(CFuncPtr):
    if CFuncPtr._is_fastpath:
        return CFuncPtr
    #
    try:
        return make_fastpath_subclass.memo[CFuncPtr]
    except KeyError:
        pass

    class CFuncPtrFast(CFuncPtr):

        _is_fastpath = True
        _slowpath_allowed = True # set to False by tests

        @classmethod
        def enable_fastpath_maybe(cls, obj):
            if (obj.callable is None and
                obj._com_index is None):
                obj.__class__ = cls

        def __rollback(self):
            assert self._slowpath_allowed
            self.__class__ = CFuncPtr

        # disable the fast path if we reset argtypes
        def _setargtypes(self, argtypes):
            self.__rollback()
            self._setargtypes(argtypes)
        argtypes = property(CFuncPtr._getargtypes, _setargtypes)

        def _setcallable(self, func):
            self.__rollback()
            self.callable = func
        callable = property(lambda x: None, _setcallable)

        def _setcom_index(self, idx):
            self.__rollback()
            self._com_index = idx
        _com_index = property(lambda x: None, _setcom_index)

        def __call__(self, *args):
            thisarg = None
            argtypes = self._argtypes_
            restype = self._restype_
            funcptr = self._getfuncptr(argtypes, restype, thisarg)
            try:
                result = self._call_funcptr(funcptr, *args)
                result = self._do_errcheck(result, args)
            except (TypeError, ArgumentError): # XXX, should be FFITypeError
                assert self._slowpath_allowed
                return CFuncPtr.__call__(self, *args)
            return result

    make_fastpath_subclass.memo[CFuncPtr] = CFuncPtrFast
    return CFuncPtrFast
make_fastpath_subclass.memo = {}