;;;; lazy-ffi-support.scm ; ; Copyright (c) 2000-2011, Felix L. Winkelmann ; All rights reserved. ; ; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following ; conditions are met: ; ; Redistributions of source code must retain the above copyright notice, this list of conditions and the following ; disclaimer. ; Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following ; disclaimer in the documentation and/or other materials provided with the distribution. ; Neither the name of the author nor the names of its contributors may be used to endorse or promote ; products derived from this software without specific prior written permission. ; ; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS ; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY ; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR ; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ; POSSIBILITY OF SUCH DAMAGE. (module lazy-ffi (lazy-ffi:module lazy-ffi:function-ptr lazy-ffi:function) (import scheme chicken (except foreign foreign-declare)) (use srfi-1 srfi-69 easyffi) (cond-expand (debug (define-syntax dbg (syntax-rules () ((_ . args) (print . args))))) (else (define-syntax dbg (syntax-rules () ((_ . args) #f))))) (define-syntax switch (syntax-rules (else) ((_ "clause" tmp ()) (void)) ((_ "clause" tmp ((else . body))) (let () . body)) ((_ "clause" tmp ((val . body) . more)) (if (eq? tmp val) (let () . body) (switch "clause" tmp more))) ((_ exp clause ...) (let ((tmp exp)) (switch "clause" tmp (clause ...)))))) #> #ifdef __MINGW32__ # include # define RTLD_LAZY 0 # define RTLD_GLOBAL 0 # define dlopen(fn, t) LoadLibrary(fn) # define dlsym(h, f) GetProcAddress(h, f) #else # include #endif #if defined(C_MACOSX) # include #else # include #endif <# (foreign-parse #<symbol name))) ) ) (define (lookup-symbol name) (let loop ([ms *modules*]) (if (null? ms) (error 'lazy-ffi:function "can not resolve external symbol" name) (let* ([m (car ms)] [h (or (hash-table-ref/default *handles* m #f) (and-let* ([h (dlopen (and m (symbol->string m)) (fxior RTLD_LAZY RTLD_GLOBAL))]) (hash-table-set! *handles* m h) (dbg "[dlopened: " m "]") h) (begin (##sys#warn "can not open library" m) #f) ) ] ) (if h (let ([ptr (dlsym h (symbol->string name))]) (dbg "[resolving " name " in " m "]") (or ptr (let ([s2 (##sys#string-append "_" (symbol->string name))]) (let ([ptr (dlsym h s2)]) (dbg "[resolving " s2 " in " m "]") (or ptr (loop (cdr ms)) ) ) ) ) ) (loop (cdr ms)) ) ) ) ) ) (define (type-code t loc) (case t [(unsigned-int:) LAZY_UINT_TYPE] [(bool: int:) LAZY_INT_TYPE] [(void:) LAZY_VOID_TYPE] [(char:) LAZY_CHAR_TYPE] [(float: double:) LAZY_DOUBLE_TYPE] [(pointer:) LAZY_PTR_TYPE] [(string: symbol:) LAZY_STRING_TYPE] [(scheme-pointer: byte-vector:) LAZY_SCHEME_PTR_TYPE] [(scheme-object:) LAZY_SCHEME_TYPE] [(u8vector: s8vector: u16vector: s16vector: u32vector: s32vector: f32fector: f64vector:) LAZY_SCHEME_PTR_TYPE] [else (error loc "invalid ffi type" t)] ) ) (define (check-arg x t loc) (switch t [LAZY_INT_TYPE (cond [(boolean? x) (if x 1 0)] [else x] ) ] [LAZY_UINT_TYPE (cond [(boolean? x) (if x 1 0)] [else x] ) ] [LAZY_DOUBLE_TYPE (exact->inexact x)] [LAZY_CHAR_TYPE (##sys#check-char x 'lazy-ffi:function) x] [LAZY_PTR_TYPE (when x (##sys#check-special x 'lazy-ffi:function)) x] [LAZY_STRING_TYPE (cond [(string? x) (##sys#make-c-string x)] [(symbol? x) (##sys#make-c-string (##sys#slot x 1))] [else (error loc "bad argument type - not a string or symbol" x)] ) ] [LAZY_SCHEME_PTR_TYPE (if (##sys#immediate? x) (error loc "immediate object can not be passes as pointer" x) x) ] [else x] ) ) (define (srfi-4-vector? x) (and (not (##sys#immediate? x)) (##sys#generic-structure? x) (memq (##sys#slot x 0) '(u8vector s8vector u16vector s16vector u32vector s32vector f32vector f64vector)) ) ) (define (val-type-code x) (cond [(or (boolean? x) (fixnum? x)) LAZY_INT_TYPE] [(char? x) LAZY_CHAR_TYPE] [(number? x) LAZY_DOUBLE_TYPE] [(or (symbol? x) (string? x)) LAZY_STRING_TYPE] [(##sys#immediate? x) LAZY_SCHEME_TYPE] [(srfi-4-vector? x) LAZY_SCHEME_PTR_TYPE] [(or (##core#inline "C_pointerp" x) (##core#inline "C_locativep" x) (##core#inline "C_taggedpointerp" x) (##core#inline "C_swigpointerp" x) ) LAZY_PTR_TYPE] [else LAZY_SCHEME_PTR_TYPE] ) ) (define-inline (make-flonum) (##core#inline_allocate ("C_a_i_flonum" 4) 0)) ;XXX words-per-flonum (define ((function-wrapper name ptr) . args) (let ([rtype LAZY_VOID_TYPE] [rtypename #f] [safe #f] [result #f] ) (let loop ([args args] [argvals '()] [argtypes '()]) (if (null? args) (begin (dbg "[call: " name " " (reverse argvals) " " (reverse argtypes) " -> " rtype "]") (let ([c ((if safe call_lazy_safe call_lazy) ptr (reverse argvals) (length argvals) rtype (reverse argtypes) result) ] ) (switch c [LAZY_OK (switch rtype [LAZY_INT_TYPE (or (##sys#slot result 0) (##sys#slot result 1))] [LAZY_UINT_TYPE (or (##sys#slot result 0) (##sys#slot result 1))] [LAZY_CHAR_TYPE (##sys#slot result 0)] [LAZY_VOID_TYPE (void)] [LAZY_STRING_TYPE (let ([str (##sys#peek-c-string result 0)]) (if (eq? rtypename symbol:) (##sys#intern-symbol str) str) ) ] [LAZY_SCHEME_TYPE (##sys#slot result 0)] [LAZY_PTR_TYPE (and (not (##sys#null-pointer? result)) result)] [else result] ) ] [LAZY_BAD_ARG (error name "error while calling foreign function - argument has invalid type")] [LAZY_BAD_TYPE (error name "error while calling foreign function - invalid type code")] [LAZY_BAD_RTYPE (error name "error while calling foreign function - invalid return type code")] [else (error name "error while calling foreign function - could not create context")] ) ) ) (let ([arg (car args)]) (case arg [(safe:) (set! safe (cadr args)) (loop (cddr args) argvals argtypes) ] [(return:) (set! rtypename (cadr args)) (set! rtype (type-code rtypename name)) (set! result (switch rtype [LAZY_INT_TYPE (vector #f (make-flonum))] [LAZY_UINT_TYPE (vector #f (make-flonum))] [LAZY_CHAR_TYPE (vector 0)] [LAZY_DOUBLE_TYPE (make-flonum)] [LAZY_SCHEME_TYPE (vector #f)] [LAZY_PTR_TYPE (##sys#make-pointer)] [LAZY_STRING_TYPE (##sys#make-pointer)] [else #f] ) ) (loop (cddr args) argvals argtypes) ] [(int: void: double: pointer: unsigned-int:) (let* ([x (cadr args)] [t (type-code arg name)] ) (loop (cddr args) (cons (check-arg x t name) argvals) (cons t argtypes) ) ) ] [(string: symbol:) (let ([x (cadr args)]) (loop (cddr args) (cons (check-arg x LAZY_STRING_TYPE name) argvals) (cons LAZY_STRING_TYPE argtypes)) ) ] [(scheme-object:) (loop (cddr args) (cons (cadr args) argvals) (cons LAZY_SCHEME_TYPE argtypes)) ] [(scheme-pointer: byte-vector:) (loop (cddr args) (cons (check-arg (cadr args) LAZY_SCHEME_PTR_TYPE name) argvals) (cons LAZY_SCHEME_PTR_TYPE argtypes) ) ] [else (cond [(string? arg) (loop (cdr args) (cons (##sys#make-c-string arg) argvals) (cons LAZY_STRING_TYPE argtypes)) ] [(symbol? arg) (loop (cdr args) (cons (##sys#make-c-string (##sys#slot arg 1)) argvals) (cons LAZY_STRING_TYPE argtypes)) ] [(srfi-4-vector? arg) (loop (cdr args) (cons (##sys#slot arg 1) argvals) (cons LAZY_SCHEME_PTR_TYPE argtypes) ) ] [else (loop (cdr args) (cons arg argvals) (cons (val-type-code arg) argtypes)) ] ) ] ) ) ) ) ) ) (define (lazy-ffi:function name uid) (if (##sys#symbol-has-toplevel-binding? uid) (##sys#slot uid 0) (let* ([ptr (lookup-symbol name)] [proc (function-wrapper name ptr)] ) (##sys#setslot uid 0 proc) proc) ) ) (define (lazy-ffi:function-ptr ptr) (function-wrapper "" ptr) ) )