Index: test/js/testEverythingBasic.js
===================================================================
--- test/js/testEverythingBasic.js	(revision 154)
+++ test/js/testEverythingBasic.js	(working copy)
@@ -140,4 +140,21 @@
    let e = Everything.test_enum_param(Everything.TestEnum.VALUE1);
    assertEquals('Enum parameter', 'value1', e);
 }
+
+function testCallback() {
+    let callback = function() {
+        return 42;
+    };
+    assertEquals('Callback', Everything.test_callback(callback), 42);
+}
+
+function testCallbackUserData() {
+    let callbackUserData = function(userData) {
+        return userData.foo.length;
+    };
+    let userData = {'foo': 'bar'};
+    assertEquals('CallbackUserData', Everything.test_callback_user_data(
+                                     callbackUserData, userData), 3);
+}
+
 gjstestRun();
Index: gi/arg.c
===================================================================
--- gi/arg.c	(revision 154)
+++ gi/arg.c	(working copy)
@@ -1,6 +1,6 @@
 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
 /*
- * Copyright (c) 2008  litl, LLC
+ * Copyright (c) 2008-2009  litl, LLC
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -29,9 +29,100 @@
 #include "union.h"
 #include "value.h"
 #include <gjs/gjs.h>
+#include <girffi.h>
+#include <girepository.h>
 
 #include <util/log.h>
 
+typedef struct {
+    JSContext *context;
+    GICallableInfo *info;
+    jsval function;
+    ffi_cif *cif;
+    ffi_closure *closure;
+} GjsCallbackClosure;
+    
+#if 0
+static void
+_gjs_ffi_closure_free(GjsCallbackClosure *closure)
+{
+    //munmap(ffi_closure->closure, sizeof(ffi_closure->closure));
+    g_slice_free(ffi_cif, closure->cif);
+    g_slice_free(GjsCallbackClosure, closure);
+}
+#endif
+
+static void
+_gjs_ffi_closure_callback(ffi_cif             *cif,
+                          void                *result,
+                          void               **args,
+                          GjsCallbackClosure *closure)
+{
+    int i, n_args;
+    jsval *jsargs, rval;
+
+    n_args = g_callable_info_get_n_args(closure->info);
+    jsargs = (jsval*)g_new0(jsval, n_args);
+    
+    for (i = 0; i < n_args; i++) {
+        GIArgInfo * arg_info;
+        GITypeInfo * type_info;
+        
+        arg_info = g_callable_info_get_arg(closure->info, i);
+        type_info = g_arg_info_get_type(arg_info);
+        if (!gjs_value_from_g_argument(closure->context,
+                                       &jsargs[i],
+                                       type_info,
+                                       args[i])) {
+            g_warning("COULD NOT CONVERT ARGUMENT");
+            return;
+        }
+    }
+
+    if (!JS_CallFunctionValue(closure->context,
+                              NULL,
+                              closure->function,
+                              n_args,
+                              jsargs,
+                              &rval)) {
+        g_warning("Couldn't call function");
+        return;
+    }
+
+    gjs_value_to_g_argument(closure->context,
+                            rval,
+                            g_callable_info_get_return_type(closure->info),
+                            "callback",
+                            GJS_ARGUMENT_RETURN_VALUE,
+                            FALSE,
+                            result);
+}
+
+static JSBool
+_gjs_callback_to_value(JSContext      *context,
+                       jsval           function,
+                       GICallableInfo *callable_info,
+                       gpointer       *v_pointer)
+{
+    GjsCallbackClosure *gjs_closure;
+    
+    g_return_val_if_fail(JS_TypeOfValue(context, function) == JSTYPE_FUNCTION, FALSE);
+
+    gjs_closure = g_slice_new(GjsCallbackClosure);
+    gjs_closure->context = context;
+    gjs_closure->info = callable_info;
+    gjs_closure->function = function;
+    gjs_closure->cif = g_slice_new(ffi_cif);
+    gjs_closure->closure = g_callable_info_prepare_closure(callable_info, gjs_closure->cif,
+                                                           (GIFFIClosureCallback)_gjs_ffi_closure_callback,
+                                                           gjs_closure);
+    g_assert (gjs_closure != NULL);
+
+    *v_pointer = gjs_closure->closure;
+
+    return JS_TRUE;
+}
+
 JSBool
 _gjs_flags_value_is_valid(JSContext   *context,
                           GType        gtype,
@@ -432,6 +523,12 @@
 
             symbol_type = g_base_info_get_type(symbol_info);
 
+            if (symbol_type == GI_INFO_TYPE_CALLBACK) {
+                if (!_gjs_callback_to_value(context, value, (GICallableInfo *)symbol_info, &arg->v_pointer))
+                    return JS_FALSE;
+                return JS_TRUE;
+            }
+
             gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)symbol_info);
 
             if (gtype != G_TYPE_NONE)
@@ -1248,15 +1345,20 @@
     case GI_TYPE_TAG_INTERFACE: {
         GIBaseInfo* symbol_info;
         GType gtype;
+        GIInfoType symbol_type;
 
         symbol_info = g_type_info_get_interface(type_info);
         g_assert(symbol_info != NULL);
 
-        gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)symbol_info);
+        symbol_type = g_base_info_get_type(symbol_info);
+        if (symbol_type == GI_INFO_TYPE_CALLBACK) {
+            /* FIXME: free up callback etc */
+        } else {
+            gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)symbol_info);
 
-        if (g_type_is_a(gtype, G_TYPE_CLOSURE))
-            needs_release = TRUE;
-
+            if (g_type_is_a(gtype, G_TYPE_CLOSURE))
+                needs_release = TRUE;
+        }
         g_base_info_unref(symbol_info);
         break;
     }
