[Xfce4-commits] <glib-objc:no-foundation-dep> add GOCClosure... sorta like NSInvocation. untested

Brian J. Tarricone noreply at xfce.org
Sun Nov 22 04:02:17 CET 2009


Updating branch refs/heads/no-foundation-dep
         to 33329ae709dc907d02c613a4514f1fc16c41b00f (commit)
       from 85cca33b1b3827211dd1d4cbd9fb2ef2f863efac (commit)

commit 33329ae709dc907d02c613a4514f1fc16c41b00f
Author: Brian J. Tarricone <brian at tarricone.org>
Date:   Fri Jul 10 13:34:46 2009 -0700

    add GOCClosure... sorta like NSInvocation.  untested
    
    also add a platform-dependent header in $(libdir) to support this

 .gitignore                |    2 +
 Makefile.am               |   15 ++
 common/goc-private.h      |   24 +++
 configure.ac.in           |  132 +++++++++++--
 gobject-objc/GOCClosure.h |   99 +++++++++
 gobject-objc/GOCClosure.m |  502 +++++++++++++++++++++++++++++++++++++++++++++
 gobject-objc/Makefile.am  |    8 +-
 7 files changed, 763 insertions(+), 19 deletions(-)

diff --git a/.gitignore b/.gitignore
index 2141f62..c4d3d28 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,8 +23,10 @@ config.sub
 configure
 configure.ac
 depcomp
+glib-objc-config.h
 install-sh
 libtool
 ltmain.sh
 missing
+stamp-goc-h
 stamp-h1
diff --git a/Makefile.am b/Makefile.am
index 02714f1..a16667b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,3 +3,18 @@ SUBDIRS = \
 	glib-objc \
 	gobject-objc \
     tests
+
+archdepdir = $(libdir)/glib-objc-$(GLIB_OBJC_API_VERSION)/include
+archdep_HEADERS = \
+	glib-objc-config.h
+
+BUILT_SOURCES = stamp-goc-h
+glib-objc-config.h: stamp-goc-h
+	@if test -f glib-objc-config.h; then :; \
+	else rm -f stamp-goc-h; $(MAKE) stamp-goc-h; fi
+stamp-goc-h: config.status
+	cd $(top_builddir) && $(SHELL) ./config.status glib-objc-config.h
+	echo timestamp > stamp-goc-h
+
+DISTCLEANFILES = \
+	glib-objc-config.h
diff --git a/common/goc-private.h b/common/goc-private.h
index ba15d56..2afc758 100644
--- a/common/goc-private.h
+++ b/common/goc-private.h
@@ -58,4 +58,28 @@
     return (val); \
 } G_STMT_END
 
+
+#define _goc_collect_varargs(arrayp, countp, first_param, var_args, do_strdup)  G_STMT_START{ \
+    void *__cur; \
+    int __max_params = 6; \
+    \
+    *(countp) = 0; \
+    *(arrayp) = g_malloc(sizeof(void *) * (__max_params + 1)); \
+    __cur = (void *)first_param; \
+    while(__cur) { \
+        if(*(countp) == __max_params) { \
+            __max_params *= 2; \
+            *(arrayp) = g_realloc(*(arrayp), sizeof(void *) * (__max_params + 1)); \
+        } \
+        \
+        if(do_strdup) \
+            (*(arrayp))[(*(countp))++] = (void *)g_strdup(__cur); \
+        else \
+            (*(arrayp))[(*(countp))++] = __cur; \
+        __cur = va_arg(var_args, void *); \
+    } \
+    (*(arrayp))[*(countp)] = NULL; \
+}G_STMT_END
+    
+
 #endif  /* __GOC_PRIVATE_H__ */
diff --git a/configure.ac.in b/configure.ac.in
index bcf053c..72e3819 100644
--- a/configure.ac.in
+++ b/configure.ac.in
@@ -50,23 +50,89 @@ AC_SUBST(GOC_API_VERSION)
 
 dnl required
 
-PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.12.0], , [AC_MSG_ERROR([
-*** Glib version 2.12.0 or greater is required.
-*** YOu can download it from http://gtk.org/
-])])
-AC_MSG_CHECKING([GLIB_CFLAGS])
-AC_MSG_RESULT([$GLIB_CFLAGS])
-AC_MSG_CHECKING([GLIB_LIBS])
-AC_MSG_RESULT([$GLIB_LIBS])
-
-PKG_CHECK_MODULES([GOBJECT], [gobject-2.0 >= 2.12.0], , [AC_MSG_ERROR([
-*** GObject (a part of the Glib library) version 2.12.0 or greater is required.
-*** You can download it from http://gtk.org/
-])])
-AC_MSG_CHECKING([GOBJECT_CFLAGS])
-AC_MSG_RESULT([$GOBJECT_CFLAGS])
-AC_MSG_CHECKING([GOBJECT_LIBS])
-AC_MSG_RESULT([$GOBJECT_LIBS])
+#
+# BT_CHECK_PACKAGE(VAR, MODULE, MIN_VERSION, [WEBSITE])
+#
+AC_DEFUN([BT_CHECK_PACKAGE],
+[
+    PKG_CHECK_MODULES([$1], [$2 >= $3], ,
+    [
+        echo '***'" $2 version $3 is required."
+        if test "x$4" != "x"; then
+            echo '***'" You can download it from $4"
+        fi
+        exit 1
+    ])
+    AC_MSG_CHECKING([$1_CFLAGS])
+    AC_MSG_RESULT([$$1_CFLAGS])
+    AC_MSG_CHECKING([$1_LIBS])
+    AC_MSG_RESULT([$$1_LIBS])
+])
+
+BT_CHECK_PACKAGE([GLIB], [glib-2.0], [2.12.0], [http://gtk.org/])
+BT_CHECK_PACKAGE([GOBJECT], [gobject-2.0], [2.12.0], [http://gtk.org/])
+BT_CHECK_PACKAGE([LIBFFI], [libffi], [3.0.0], [http://sourceware.org/libffi])
+
+dnl figure out some semi-custom types
+
+# BT_CHECK_OBJC_SIG(VAR, TYPE_MACRO, FRIENDLY_NAME)
+AC_DEFUN([BT_CHECK_OBJC_SIG],
+[
+    AC_LANG_PUSH([Objective C])
+
+    AC_MSG_CHECKING([the type signature of $3])
+    AC_RUN_IFELSE(
+    [AC_LANG_PROGRAM(
+      [
+        #include <stdio.h>
+        #if defined(HAVE_OBJC_OBJC_RUNTIME_H)
+        # include <objc/objc-runtime.h>
+        #elif defined(HAVE_OBJC_OBJC_API_H)
+        # include <objc/objc-api.h>
+        #endif
+      ],
+      [
+        FILE *fp = fopen("bt-conftest.out", "w");
+        if(!fp)
+            return 1;
+        fprintf(fp, "%c\n", $2);
+        fclose(fp);
+      ])
+    ], ,
+    [
+        AC_MSG_RESULT([failed])
+        echo <<__EOERR
+*** Failed to figure out the type signature of $3.  Your Objective C runtime
+*** may not be supported.
+__EOERR
+        rm -f bt-conftest.out
+        exit 1
+    ])
+
+    AC_LANG_POP()
+
+    val=`cat bt-conftest.out`
+    rm -f bt-conftest.out
+    if test -z "$val"; then
+        AC_MSG_RESULT([failed])
+        echo <<__EOERR
+*** Type signature of $3 appears to be empty.  Your Objective C runtime may
+*** not be supported.
+__EOERR
+        exit 1
+    fi
+
+    AC_MSG_RESULT([$val])
+    $1="$val"
+])
+
+BT_CHECK_OBJC_SIG([GOC_ARGTYPE_BOOL], [_C_BOOL], [BOOL])
+BT_CHECK_OBJC_SIG([GOC_ARGTYPE_FLAGS], [_C_BFLD], [bitfields/flags])
+# this next one shouldn't be hard-coded.  instead should make sure there are
+# no conflicts with the system signatures
+AC_MSG_CHECKING([the type signature of enums])
+GOC_ARGTYPE_ENUM='e'
+AC_MSG_RESULT([$GOC_ARGTYPE_ENUM])
 
 dnl check for debugging support
 AC_ARG_ENABLE([debug],
@@ -93,6 +159,38 @@ if test "x$enable_debug" != "xno"; then
 fi
 AC_MSG_RESULT([$enable_debug])
 
+AC_CONFIG_COMMANDS([glib-objc-config.h],
+[
+    outfile=glib-objc-config.h-tmp
+    cat > $outfile <<__EOF
+/*
+ * glib-objc-config.h
+ *
+ * This is a generated file.  Please modifiy 'configure.ac.in'.
+ */
+
+#ifndef __GLIB_OBJC_CONFIG_H__
+#define __GLIB_OBJC_CONFIG_H__
+
+#define __GOC_ARGTYPE_BOOL         "$argtype_bool"
+#define __GOC_ARGTYPE_FLAGS        "$argtype_flags"
+#define __GOC_ARGTYPE_ENUM         "$argtype_enum"
+
+#endif  /* __GLIB_OBJC_CONFIG_H__ */
+__EOF
+    if cmp -s $outfile glib-objc-config.h; then
+        AC_MSG_NOTICE([glib-objc-config.h is unchanged])
+        rm -f $outfile
+    else
+        mv $outfile glib-objc-config.h
+    fi
+],
+[
+    argtype_bool=$GOC_ARGTYPE_BOOL
+    argtype_flags=$GOC_ARGTYPE_FLAGS
+    argtype_enum=$GOC_ARGTYPE_ENUM
+])
+
 AC_OUTPUT([
 Makefile
 common/Makefile
diff --git a/gobject-objc/GOCClosure.h b/gobject-objc/GOCClosure.h
new file mode 100644
index 0000000..da4f015
--- /dev/null
+++ b/gobject-objc/GOCClosure.h
@@ -0,0 +1,99 @@
+/*
+ *  glib-objc - objective-c bindings for glib/gobject
+ *
+ *  Copyright (c) 2009 Brian Tarricone <brian at tarricone.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published
+ *  by the Free Software Foundation; version 2 of the License ONLY.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __GOC_CLOSURE_H__
+#define __GOC_CLOSURE_H__
+
+#include <glib-objc-config.h>
+
+#import <glib-objc/GOCObjectBase.h>
+#import <gobject-objc/GOCValue.h>
+
+#define GOC_ARGTYPE_INVALID  NULL
+#define GOC_ARGTYPE_NONE     @encode(void)
+#define GOC_ARGTYPE_BOOL     __GOC_ARGTYPE_BOOL
+#define GOC_ARGTYPE_UCHAR    @encode(unsigned char)
+#define GOC_ARGTYPE_CHAR     @encode(char)
+#define GOC_ARGTYPE_USHORT   @encode(unsigned short)
+#define GOC_ARGTYPE_SHORT    @encode(short)
+#define GOC_ARGTYPE_UINT     @encode(unsigned int)
+#define GOC_ARGTYPE_INT      @encode(int)
+#define GOC_ARGTYPE_ULONG    @encode(unsigned long)
+#define GOC_ARGTYPE_LONG     @encode(long)
+#define GOC_ARGTYPE_UINT64   @encode(unsigned long long)
+#define GOC_ARGTYPE_INT64    @encode(long long)
+#define GOC_ARGTYPE_FLOAT    @encode(float)
+#define GOC_ARGTYPE_DOUBLE   @encode(double)
+#define GOC_ARGTYPE_FLAGS    __GOC_ARGTYPE_FLAGS
+#define GOC_ARGTYPE_ENUM     __GOC_ARGTYPE_ENUM
+#define GOC_ARGTYPE_OBJECT   @encode(id)
+#define GOC_ARGTYPE_POINTER  @encode(void *)
+#define GOC_ARGTYPE_STRING   @encode(char *)
+#define GOC_ARGTYPE_STRV     @encode(char **)
+
+typedef struct _GOCClosurePriv  GOCClosurePriv;
+
+ at interface GOCClosure : GOCObjectBase
+{
+  @private
+    GOCClosurePriv *priv;
+}
+
+/* designated initializer */
+- (id)initWithSelector:(SEL)aSelector
+              onTarget:(id)target
+          withUserData:(id <GOCObject>)userData
+        withReturnType:(const char *)returnType
+           andArgTypeV:(char **)argTypes;
+
+/* argument varargs lists should be terminated with NULL or nil */
+- (id)initWithSelector:(SEL)aSelector
+              onTarget:(id)target
+          withUserData:(id <GOCObject>)userData
+        withReturnType:(const char *)returnType
+           andArgTypes:(const char *)firstArgType,...;
+- (id)initWithSelector:(SEL)aSelector
+          withUserData:(id <GOCObject>)userData
+        withReturnType:(const char *)returnType
+           andArgTypes:(const char *)firstArgType,...;
+/* return type is assumed to be void */
+- (id)initWithSelector:(SEL)aSelector
+      andArgTypes:(const char *)firstArgType,...;
+
+- (void)setTarget:(id)aTarget;
+- (id)target;
+
+- (void)setSelector:(SEL)aSelector;
+- (SEL)selector;
+
+/* return value is the return value from the closure's callback.  it will be nil
+ * if the return type is void.  arguments in the varargs array should be terminated
+ * with nil.  to pass a nil/NULL value, use [GOCValue valueWithVoid] or similar. */
+- (GOCValue *)invokeWithInvocationHint:(void *)invocationHint
+                               andArgs:(GOCValue *)firstArg,...;
+- (GOCValue *)invokeWithArgs:(GOCValue *)firstArg,...;
+
+/* non-varargs versions. array should be terminated with nil */
+- (GOCValue *)invokeWithInvocationHint:(void *)invocationHint
+                               andArgV:(GOCValue **)argv;
+- (GOCValue *)invokeWithArgV:(GOCValue **)argv;
+
+ at end
+
+#endif  /* __GOC_CLOSURE_H__ */
diff --git a/gobject-objc/GOCClosure.m b/gobject-objc/GOCClosure.m
new file mode 100644
index 0000000..7e4c21c
--- /dev/null
+++ b/gobject-objc/GOCClosure.m
@@ -0,0 +1,502 @@
+/*
+ *  glib-objc - objective-c bindings for glib/gobject
+ *
+ *  Copyright (c) 2009 Brian Tarricone <brian at tarricone.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published
+ *  by the Free Software Foundation; version 2 of the License ONLY.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdarg.h>
+
+#if defined(HAVE_OBJC_OBJC_RUNTIME_H)
+#include <objc/objc-runtime.h>
+#elif defined(HAVE_OBJC_OBJC_API_H)
+#include <objc/objc-api.h>
+#endif
+
+#include <ffi.h>
+#include <glib.h>
+
+#include <goc-private.h>
+#import "GOCClosure.h"
+#import "GOCValue+GOCPrivate.h"
+#import "GOCNumber+GOCPrivate.h"
+
+ at implementation GOCClosure
+
+struct _GOCClosurePriv
+{
+    SEL selector;
+    id target;
+    IMP msgImp;
+    char *returnType;
+    unsigned int argCount;
+    char **argTypes;
+    id <GOCObject> userData;
+};
+
+
+/* designated initializer */
+- (id)initWithSelector:(SEL)aSelector
+              onTarget:(id)target
+          withUserData:(id <GOCObject>)userData
+        withReturnType:(const char *)returnType
+           andArgTypeV:(char **)argTypes
+{
+    self = [super init];
+    if(self) {
+        priv = g_slice_new0(GOCClosurePriv);
+
+        priv->selector = aSelector;
+        priv->target = target;  /* FIXME: ref? */
+        if(priv->selector && priv->target)
+            priv->msgImp = [priv->target methodFor:priv->selector];
+        priv->returnType = g_strdup(returnType);
+        priv->argTypes = g_strdupv(argTypes);  /* FIXME: avoid copy for internal calls? */
+        priv->argCount = g_strv_length(priv->argTypes);
+        priv->userData = userData;  /* FIXME: ref? */
+    }
+    return self;
+}
+
+- (id)_initWithSelector:(SEL)aSelector
+               onTarget:(id)target
+           withUserData:(id <GOCObject>)userData
+         withReturnType:(const char *)returnType
+        andFirstArgType:(const char *)firstArgType
+       andArgTypeValist:(va_list)argTypes
+{
+    char **argtv = NULL;
+    unsigned int argt_count = 0;
+    id ret = nil;
+
+    _goc_collect_varargs(&argtv, &argt_count, firstArgType, argTypes, YES);
+
+    ret = [self initWithSelector:aSelector
+                        onTarget:target
+                    withUserData:userData
+                  withReturnType:returnType
+                     andArgTypeV:argtv];
+
+    g_strfreev(argtv);
+
+    return ret;
+}
+
+- (id)initWithSelector:(SEL)aSelector
+              onTarget:(id)target
+          withUserData:(id <GOCObject>)userData
+        withReturnType:(const char *)returnType
+           andArgTypes:(const char *)firstArgType,...
+{
+    va_list var_args;
+    id ret;
+
+    va_start(var_args, firstArgType);
+
+    ret = [self _initWithSelector:aSelector
+                         onTarget:target
+                     withUserData:userData
+                   withReturnType:returnType
+                  andFirstArgType:firstArgType
+                 andArgTypeValist:var_args];
+
+    va_end(var_args);
+
+    return ret;
+
+}
+
+- (id)initWithSelector:(SEL)aSelector
+          withUserData:(id <GOCObject>)userData
+        withReturnType:(const char *)returnType
+           andArgTypes:(const char *)firstArgType,...
+{
+    va_list var_args;
+    id ret;
+
+    va_start(var_args, firstArgType);
+
+    ret = [self _initWithSelector:aSelector
+                         onTarget:nil
+                     withUserData:userData
+                   withReturnType:returnType
+                  andFirstArgType:firstArgType
+                 andArgTypeValist:var_args];
+
+    va_end(var_args);
+
+    return ret;
+
+}
+
+- (id)initWithSelector:(SEL)aSelector
+           andArgTypes:(const char *)firstArgType,...
+{
+    va_list var_args;
+    id ret;
+
+    va_start(var_args, firstArgType);
+
+    ret = [self _initWithSelector:aSelector
+                         onTarget:nil
+                     withUserData:nil
+                   withReturnType:NULL
+                  andFirstArgType:firstArgType
+                 andArgTypeValist:var_args];
+
+    va_end(var_args);
+
+    return ret;
+}
+
+- (void)setTarget:(id)aTarget
+{
+    if(priv->target == aTarget)
+        return;
+
+    priv->target = aTarget;
+
+    if(priv->selector && priv->target)
+        priv->msgImp = [priv->target methodFor:priv->selector];
+    else
+        priv->msgImp = NULL;
+}
+
+- (id)target
+{
+    return priv->target;
+}
+
+- (void)setSelector:(SEL)aSelector
+{
+    if(priv->selector == aSelector)
+        return;
+
+    priv->selector = aSelector;
+
+    if(priv->selector && priv->target)
+        priv->msgImp = [priv->target methodFor:priv->selector];
+    else
+        priv->msgImp = NULL;
+}
+
+- (SEL)selector
+{
+    return priv->selector;
+}
+
+- (GOCValue *)invokeWithInvocationHint:(void *)invocationHint
+                               andArgs:(GOCValue *)firstArg,...
+{
+    GOCValue **argv = NULL, *ret = nil;
+    unsigned int arg_count = 0;
+    va_list var_args;
+
+    va_start(var_args, firstArg);
+    _goc_collect_varargs(&argv, &arg_count, firstArg, var_args, NO);
+    va_end(var_args);
+
+    if(arg_count != priv->argCount) {
+        g_critical("Expected %u args, but got %u", priv->argCount, arg_count);
+        return nil;
+    }
+
+    ret = [self invokeWithInvocationHint:invocationHint andArgV:argv];
+
+    g_free(argv);
+
+    return ret;
+}
+
+- (GOCValue *)invokeWithArgs:(GOCValue *)firstArg,...
+{
+    GOCValue **argv = NULL, *ret = nil;
+    unsigned int arg_count = 0;
+    va_list var_args;
+
+    va_start(var_args, firstArg);
+    _goc_collect_varargs(&argv, &arg_count, firstArg, var_args, NO);
+    va_end(var_args);
+
+    if(arg_count != priv->argCount) {
+        g_critical("Expected %u args, but got %u", priv->argCount, arg_count);
+        return nil;
+    }
+
+    ret = [self invokeWithInvocationHint:NULL andArgV:argv];
+
+    g_free(argv);
+
+    return ret;
+}
+
+static ffi_type *
+libffi_type_from_objc_signature(const char *sig)
+{
+    if(!sig)
+        return NULL;
+
+    switch(*sig) {
+        case _C_ID:
+        case _C_PTR:
+        case _C_CLASS:
+        case _C_SEL:
+        case _C_CHARPTR:
+            return &ffi_type_pointer;
+        case _C_CHR:
+            return &ffi_type_schar;
+        case _C_UCHR:
+            return &ffi_type_uchar;
+        case _C_SHT:
+            return &ffi_type_sshort;
+        case _C_USHT:
+            return &ffi_type_ushort;
+        case _C_INT:
+            return &ffi_type_sint;
+        case _C_UINT:
+            return &ffi_type_uint;
+        case _C_LNG:
+            return &ffi_type_slong;
+        case _C_ULNG:
+            return &ffi_type_ulong;
+        case _C_LNG_LNG:
+            return &ffi_type_sint64;
+        case _C_ULNG_LNG:
+            return &ffi_type_uint64;
+        case _C_FLT:
+            return &ffi_type_float;
+        case _C_DBL:
+            return &ffi_type_double;
+        case _C_BFLD:
+            return &ffi_type_uint;
+        case _C_BOOL:
+            if(sizeof(BOOL) == sizeof(unsigned char))
+                return &ffi_type_uchar;
+            else if(sizeof(BOOL) == sizeof(int))
+                return &ffi_type_sint;
+
+            g_critical("Unhandled type for ObjC BOOL type (sizeof(BOOL) is %d)", (int)sizeof(BOOL));
+            return NULL;
+        case _C_VOID:
+            return &ffi_type_void;
+        default:
+            if(*sig == GOC_ARGTYPE_ENUM[0])
+                return &ffi_type_sint;
+    }
+
+    g_critical("Unhandled/unsupported ObjC signature type '%s'", sig);
+    return NULL;
+}
+
+static BOOL
+libffi_value_from_gocvalue(void **ffival,
+                           const GOCValue *gocval)
+{
+    if(!ffival || !gocval)
+        return NO;
+
+    *ffival = NULL;
+
+    if([gocval holdsObject])
+        *(id *)ffival = [gocval objectValue];
+    else if([gocval holdsPointer])
+        *ffival = [gocval pointerValue];
+    else if([gocval holdsVoid])
+        return YES;  /* we're returning a NULL value in *ffival */
+    else if([gocval isKindOf:[GOCNumber class]]) {
+        GOCNumber *gocn = (GOCNumber *)gocval;
+
+        if([gocn holdsBool])
+            *(BOOL **)ffival = (BOOL *)[gocn storage];
+        else if([gocn holdsUChar])
+            *(unsigned char **)ffival = (unsigned char *)[gocn storage];
+        else if([gocn holdsChar])
+            *(char **)ffival = (char *)[gocn storage];
+        else if([gocn holdsUShort])
+            *(unsigned short **)ffival = (unsigned short *)[gocn storage];
+        else if([gocn holdsShort])
+            *(short **)ffival = (short *)[gocn storage];
+        else if([gocn holdsUInt])
+            *(unsigned int **)ffival = (unsigned int *)[gocn storage];
+        else if([gocn holdsInt])
+            *(int **)ffival = (int *)[gocn storage];
+        else if([gocn holdsULong])
+            *(unsigned long **)ffival = (unsigned long *)[gocn storage];
+        else if([gocn holdsLong])
+            *(long **)ffival = (long *)[gocn storage];
+        else if([gocn holdsUInt64])
+            *(unsigned long long **)ffival = (unsigned long long *)[gocn storage];
+        else if([gocn holdsInt64])
+            *(long long **)ffival = (long long *)[gocn storage];
+        else if([gocn holdsFloat])
+            *(float **)ffival = (float *)[gocn storage];
+        else if([gocn holdsDouble])
+            *(double **)ffival = (double *)[gocn storage];
+        else if([gocn holdsEnum])
+            *(int **)ffival = (int *)[gocn storage];
+        else if([gocn holdsFlags])
+            *(unsigned int **)ffival = (unsigned int *)[gocn storage];
+    }
+
+    if(!*ffival)
+        return NO;
+
+    return YES;
+}
+
+static GOCValue *
+gocvalue_from_libffi_retval(void *retval,
+                            const char *type_sig)
+{
+    if(!retval || !type_sig)
+        return nil;
+
+    switch(*type_sig) {
+        case _C_ID:
+            return [GOCValue valueWithObject:*(id *)retval];
+        case _C_PTR:
+        case _C_CLASS:
+        case _C_SEL:
+        case _C_CHARPTR:
+            return [GOCValue valueWithPointer:*(void **)retval];
+        case _C_CHR:
+            return [GOCNumber numberWithChar:*(char *)retval];
+        case _C_UCHR:
+            return [GOCNumber numberWithUChar:*(unsigned char *)retval];
+        case _C_SHT:
+            return [GOCNumber numberWithShort:*(short *)retval];
+        case _C_USHT:
+            return [GOCNumber numberWithUShort:*(unsigned short *)retval];
+        case _C_INT:
+            return [GOCNumber numberWithInt:*(int *)retval];
+        case _C_UINT:
+            return [GOCNumber numberWithUInt:*(unsigned int *)retval];
+        case _C_LNG:
+            return [GOCNumber numberWithLong:*(long *)retval];
+        case _C_ULNG:
+            return [GOCNumber numberWithULong:*(unsigned long *)retval];
+        case _C_LNG_LNG:
+            return [GOCNumber numberWithInt64:*(long long *)retval];
+        case _C_ULNG_LNG:
+            return [GOCNumber numberWithUInt64:*(unsigned long long *)retval];
+        case _C_FLT:
+            return [GOCNumber numberWithFloat:*(float *)retval];
+        case _C_DBL:
+            return [GOCNumber numberWithDouble:*(double *)retval];
+        case _C_BFLD:
+            return [GOCNumber numberWithFlags:*(unsigned int *)retval];
+        case _C_BOOL:
+            return [GOCNumber numberWithBool:*(BOOL *)retval];
+        case _C_VOID:
+            return [GOCValue valueWithVoid];
+        default:
+            if(*type_sig == GOC_ARGTYPE_ENUM[0])
+                return [GOCNumber numberWithEnum:*(int *)retval];
+    }
+
+    g_critical("Unhandled/unsupported ObjC signature type '%s'", type_sig);
+    return nil;
+}
+
+/* non-varargs versions. array should be terminated with nil */
+- (GOCValue *)invokeWithInvocationHint:(void *)invocationHint
+                               andArgV:(GOCValue **)argv
+{
+    GOCValue *ret = nil;
+    unsigned int arg_count = 0, i;
+    ffi_status status;
+    ffi_cif fcif;
+    ffi_type *ret_type = NULL;
+    ffi_type **arg_types = NULL;
+    char retval[32] = { 0, };  /* should be more than enough */
+    void **args = NULL;
+
+    if(!priv->msgImp) {
+        g_critical("Unable to invoke closure without a valid IMP pointer "
+                   "(either the target or selector are unset, or the selector "
+                   "is not a valid message for the target)");
+        return nil;
+    }
+
+    while(argv[arg_count])
+        ++arg_count;
+
+    if(arg_count != priv->argCount) {
+        g_critical("Expected %u args, but got %u", priv->argCount, arg_count);
+        goto out;
+    }
+
+    ret_type = libffi_type_from_objc_signature(priv->returnType);
+    if(!ret_type) {
+        g_critical("Couldn't determine FFI return type for ObjC type '%s'", priv->returnType);
+        goto out;
+    }
+
+    arg_types = g_malloc(sizeof(ffi_type *) * (arg_count + 2));
+    arg_types[0] = &ffi_type_pointer;  /* self */
+    arg_types[1] = &ffi_type_pointer;  /* _cmd */
+    for(i = 0; i < arg_count; ++i) {
+        arg_types[i+2] = libffi_type_from_objc_signature(priv->argTypes[i]);
+        if(!arg_types[i]) {
+            g_critical("Couldn't determine FFI arg type for ObjC type '%s' at index %u",
+                       priv->argTypes[i], i);
+            goto out;
+        }
+    }
+
+    status = ffi_prep_cif(&fcif, FFI_DEFAULT_ABI, arg_count, ret_type, arg_types);
+    if(status != FFI_OK) {
+        g_critical("ffi_prep_cif() failed with exit code %d", (int)status);
+        goto out;
+    }
+
+    args = g_malloc(sizeof(void *) * (arg_count + 2));
+    args[0] = priv->target;
+    args[1] = (void *)priv->selector;
+    for(i = 0; i < arg_count; ++i) {
+        if(!libffi_value_from_gocvalue(&args[i+2], argv[i])) {
+            g_critical("Unable to convert GOCValue to FFI value at index %d", i);
+            goto out;
+        }
+    }
+
+    ffi_call(&fcif, FFI_FN(priv->msgImp), retval, args);
+    ret = gocvalue_from_libffi_retval(retval, priv->returnType);
+
+out:
+    if(arg_types)
+        g_free(arg_types);
+    if(args)
+        g_free(args);
+
+    return ret;
+}
+
+- (GOCValue *)invokeWithArgV:(GOCValue **)argv
+{
+    return [self invokeWithInvocationHint:NULL andArgV:argv];
+}
+
+- (void)free
+{
+    g_slice_free(GOCClosurePriv, priv);
+    [super free];
+}
+
+ at end
diff --git a/gobject-objc/Makefile.am b/gobject-objc/Makefile.am
index e214396..70694a9 100644
--- a/gobject-objc/Makefile.am
+++ b/gobject-objc/Makefile.am
@@ -6,6 +6,7 @@ glibobjcmaininclude_HEADERS = \
 gobjectobjcincludedir = $(glibobjcmainincludedir)/gobject-objc
 gobjectobjcinclude_HEADERS = \
 	GOCBoxedValue.h \
+	GOCClosure.h \
 	GOCObject.h \
 	GOCNumber.h \
 	GOCValue.h
@@ -14,6 +15,7 @@ libgobject_objc_2_0_la_SOURCES = \
 	$(gobjectobjcmaininclude_HEADERS) \
 	$(gobjectobjcinclude_HEADERS) \
 	GOCBoxedValue.m \
+	GOCClosure.m \
 	GOCObject.m \
 	GOCNumber.m \
 	GOCValue.m \
@@ -22,7 +24,8 @@ libgobject_objc_2_0_la_SOURCES = \
 
 libgobject_objc_2_0_la_CFLAGS = \
 	-DGLIB_OBJC_COMPILATION \
-	$(GOBJECT_CFLAGS)
+	$(GOBJECT_CFLAGS) \
+	$(LIBFFI_CFLAGS)
 
 libgobject_objc_2_0_la_OBJCFLAGS = $(libgobject_objc_2_0_la_CFLAGS)
 
@@ -30,7 +33,8 @@ libgobject_objc_2_0_la_LDFLAGS = \
 	-version-info $(GOC_VERINFO)
 
 libgobject_objc_2_0_la_LIBADD = \
-	$(GOBJECT_LIBS)
+	$(GOBJECT_LIBS) \
+	$(LIBFFI_LIBS)
 
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_in_files = gobject-objc-$(GOC_API_VERSION).pc.in



More information about the Xfce4-commits mailing list