diff --git a/gi/Makefile.am b/gi/Makefile.am
index 6fb1c5d..d74c9fb 100644
--- a/gi/Makefile.am
+++ b/gi/Makefile.am
@@ -93,7 +93,6 @@ _gi_la_SOURCES = \
 	pygi-signal-closure.c \
 	pygi-signal-closure.h \
 	pygobject-external.h \
-	pygi-invoke.c \
 	pygi-invoke.h \
 	pygi-invoke-state-struct.h \
 	pygi-cache.h \
diff --git a/gi/pygi-array.c b/gi/pygi-array.c
index fceffc6..a1f0496 100644
--- a/gi/pygi-array.c
+++ b/gi/pygi-array.c
@@ -761,11 +761,7 @@ pygi_arg_garray_len_arg_setup (PyGIArgCache *arg_cache,
         seq_cache->len_arg_index = g_type_info_get_array_length (type_info);
 
         /* offset by self arg for methods and vfuncs */
-        if (seq_cache->len_arg_index >= 0 && callable_cache != NULL &&
-                (callable_cache->function_type == PYGI_FUNCTION_TYPE_METHOD ||
-                 callable_cache->function_type == PYGI_FUNCTION_TYPE_VFUNC)) {
-            seq_cache->len_arg_index += 1;
-        }
+        seq_cache->len_arg_index += callable_cache->args_offset;
     }
 
     if (seq_cache->len_arg_index >= 0) {
diff --git a/gi/pygi-cache.c b/gi/pygi-cache.c
index 24bb1a4..46cc020 100644
--- a/gi/pygi-cache.c
+++ b/gi/pygi-cache.c
@@ -35,6 +35,9 @@
 #include "pygi-struct-marshal.h"
 #include "pygi-enum-marshal.h"
 
+/* XXX: REMOVE */
+#include "pygi-invoke.c"
+
 
 /* _arg_info_default_value
  * info:
@@ -117,24 +120,6 @@ pygi_arg_cache_free (PyGIArgCache *cache)
         g_slice_free (PyGIArgCache, cache);
 }
 
-void
-pygi_callable_cache_free (PyGICallableCache *cache)
-{
-    if (cache == NULL)
-        return;
-
-    g_slist_free (cache->to_py_args);
-    g_slist_free (cache->arg_name_list);
-    g_hash_table_destroy (cache->arg_name_hash);
-    g_ptr_array_unref (cache->args_cache);
-
-    if (cache->return_cache != NULL)
-        pygi_arg_cache_free (cache->return_cache);
-
-    g_function_invoker_destroy (&cache->invoker);
-    g_slice_free (PyGICallableCache, cache);
-}
-
 
 /* PyGIInterfaceCache */
 
@@ -454,22 +439,23 @@ _pygi_get_direction (PyGICallableCache *callable_cache, GIDirection gi_direction
     if (gi_direction == GI_DIRECTION_INOUT) {
         return PYGI_DIRECTION_BIDIRECTIONAL;
     } else if (gi_direction == GI_DIRECTION_IN) {
-        if (callable_cache->function_type == PYGI_FUNCTION_TYPE_CALLBACK)
+        if (callable_cache->direction != PYGI_DIRECTION_FROM_PYTHON)
             return PYGI_DIRECTION_TO_PYTHON;
         return PYGI_DIRECTION_FROM_PYTHON;
     } else {
-        if (callable_cache->function_type == PYGI_FUNCTION_TYPE_CALLBACK)
+        if (callable_cache->direction != PYGI_DIRECTION_FROM_PYTHON)
             return PYGI_DIRECTION_FROM_PYTHON;
         return PYGI_DIRECTION_TO_PYTHON;
     }
 }
 
-/* Generate the cache for the callable's arguments */
+/* PyGICallableCache */
+
 static gboolean
-_args_cache_generate (GICallableInfo *callable_info,
-                      PyGICallableCache *callable_cache)
+_callable_cache_generate_args_cache_real (PyGICallableCache *callable_cache,
+                                          GICallableInfo *callable_info,
+                                          gssize arg_index)
 {
-    gssize arg_index = 0;
     gssize i;
     GITypeInfo *return_info;
     GITransfer return_transfer;
@@ -500,40 +486,6 @@ _args_cache_generate (GICallableInfo *callable_info,
     callable_cache->return_cache = return_cache;
     g_base_info_unref (return_info);
 
-    /* first arg is the instance */
-    if (callable_cache->function_type == PYGI_FUNCTION_TYPE_METHOD ||
-            callable_cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) {
-        GIInterfaceInfo *interface_info;
-        PyGIArgCache *instance_cache;
-
-        interface_info = g_base_info_get_container ( (GIBaseInfo *)callable_info);
-
-        instance_cache =
-            _arg_cache_new_for_interface (interface_info,
-                                          NULL,
-                                          NULL,
-                                          GI_TRANSFER_NOTHING,
-                                          PYGI_DIRECTION_FROM_PYTHON,
-                                          callable_cache);
-
-        g_base_info_unref ( (GIBaseInfo *)interface_info);
-
-        if (instance_cache == NULL)
-            return FALSE;
-
-        /* Because we are not supplied a GITypeInfo for instance arguments,
-         * assume some defaults. */
-        instance_cache->is_pointer = TRUE;
-        instance_cache->py_arg_index = 0;
-        instance_cache->c_arg_index = 0;
-
-        _pygi_callable_cache_set_arg (callable_cache, arg_index, instance_cache);
-
-        arg_index++;
-        callable_cache->n_py_args++;
-    }
-
-
     for (i=0; arg_index < _pygi_callable_cache_args_len (callable_cache); arg_index++, i++) {
         PyGIArgCache *arg_cache = NULL;
         GIArgInfo *arg_info;
@@ -681,70 +633,44 @@ _args_cache_generate (GICallableInfo *callable_info,
     return TRUE;
 }
 
-static gboolean
-_setup_invoker (GICallableInfo *callable_info,
-                GIInfoType info_type,
-                GIFunctionInvoker *invoker,
-                GCallback function_ptr)
+static void
+_callable_cache_deinit (PyGICallableCache *cache)
 {
-    GError *error = NULL;
-
-    if (info_type == GI_INFO_TYPE_FUNCTION) {
-        if (g_function_info_prep_invoker ((GIFunctionInfo *)callable_info,
-                                          invoker,
-                                          &error)) {
-            return TRUE;
-        }
-        if (!pygi_error_check (&error)) {
-            PyErr_Format (PyExc_RuntimeError,
-                          "unknown error creating invoker for %s",
-                          g_base_info_get_name ((GIBaseInfo *)callable_info));
-        }
-        return FALSE;
+    g_slist_free (cache->to_py_args);
+    g_slist_free (cache->arg_name_list);
+    g_hash_table_destroy (cache->arg_name_hash);
+    g_ptr_array_unref (cache->args_cache);
 
-    } else {
-        if (!g_function_invoker_new_for_address (function_ptr,
-                                                 (GIFunctionInfo *)callable_info,
-                                                 invoker,
-                                                 &error)) {
-            if (!pygi_error_check (&error)) {
-                PyErr_Format (PyExc_RuntimeError,
-                              "unknown error creating invoker for %s",
-                              g_base_info_get_name ((GIBaseInfo *)callable_info));
-            }
-            return FALSE;
-        }
-    }
-    return TRUE;
+    if (cache->return_cache != NULL)
+        pygi_arg_cache_free (cache->return_cache);
 }
 
-PyGICallableCache *
-pygi_callable_cache_new (GICallableInfo *callable_info,
-                         GCallback function_ptr,
-                         gboolean is_ccallback)
+static gboolean
+_callable_cache_init (PyGICallableCache *cache,
+                      GICallableInfo *callable_info)
 {
-    gint n_args;
-    PyGICallableCache *cache;
+    GIBaseInfo *base_info = (GIBaseInfo *)callable_info;
     GIInfoType type = g_base_info_get_type ( (GIBaseInfo *)callable_info);
+    gssize n_args;
 
-    cache = g_slice_new0 (PyGICallableCache);
+    cache->info = g_base_info_ref ((GIBaseInfo *) base_info);
 
-    if (cache == NULL)
-        return NULL;
+    if (cache->generate_args_cache == NULL)
+        cache->generate_args_cache = _callable_cache_generate_args_cache_real;
 
-    cache->name = g_base_info_get_name ((GIBaseInfo *)callable_info);
-    cache->throws = g_callable_info_can_throw_gerror ((GIBaseInfo *)callable_info);
+    cache->name = g_base_info_get_name (base_info);
+    cache->throws = g_callable_info_can_throw_gerror (base_info);
 
     if (g_base_info_is_deprecated (callable_info)) {
-        const gchar *deprecated = g_base_info_get_attribute (callable_info, "deprecated");
+        const gchar *deprecated = g_base_info_get_attribute (base_info, "deprecated");
         gchar *warning;
         if (deprecated != NULL)
             warning = g_strdup_printf ("%s.%s is deprecated: %s",
-                                       g_base_info_get_namespace (callable_info), cache->name,
+                                       g_base_info_get_namespace (base_info), cache->name,
                                        deprecated);
         else
             warning = g_strdup_printf ("%s.%s is deprecated",
-                                       g_base_info_get_namespace (callable_info), cache->name);
+                                       g_base_info_get_namespace (base_info), cache->name);
         PyErr_WarnEx(PyExc_DeprecationWarning, warning, 0);
         g_free (warning);
     }
@@ -758,38 +684,438 @@ pygi_callable_cache_new (GICallableInfo *callable_info,
             cache->function_type = PYGI_FUNCTION_TYPE_CONSTRUCTOR;
         else if (flags & GI_FUNCTION_IS_METHOD)
             cache->function_type = PYGI_FUNCTION_TYPE_METHOD;
+        else
+            cache->function_type = PYGI_FUNCTION_TYPE_FUNCTION;
     } else if (type == GI_INFO_TYPE_VFUNC) {
         cache->function_type = PYGI_FUNCTION_TYPE_VFUNC;
     } else if (type == GI_INFO_TYPE_CALLBACK) {
-        if (is_ccallback)
+        if (((PyGIFunctionCache *)cache)->invoker.native_address != NULL)
             cache->function_type = PYGI_FUNCTION_TYPE_CCALLBACK;
         else
             cache->function_type = PYGI_FUNCTION_TYPE_CALLBACK;
     } else {
-        cache->function_type = PYGI_FUNCTION_TYPE_METHOD;
+        cache->function_type = 47; /*PYGI_FUNCTION_TYPE_METHOD;*/
+        g_error ("Unknown function type");
     }
 
-    n_args = g_callable_info_get_n_args (callable_info);
-
-    /* if we are a method or vfunc make sure the instance parameter is counted */
-    if (cache->function_type == PYGI_FUNCTION_TYPE_METHOD ||
-            cache->function_type == PYGI_FUNCTION_TYPE_VFUNC)
-        n_args++;
+    n_args = cache->args_offset + g_callable_info_get_n_args (callable_info);
 
     if (n_args >= 0) {
         cache->args_cache = g_ptr_array_new_full (n_args, (GDestroyNotify) pygi_arg_cache_free);
         g_ptr_array_set_size (cache->args_cache, n_args);
     }
 
-    if (!_args_cache_generate (callable_info, cache))
-        goto err;
+    if (!cache->generate_args_cache (cache, callable_info, 0)) {
+        _callable_cache_deinit (cache);
+        return FALSE;
+    }
+
+    return TRUE;
+}
 
-    if (!_setup_invoker (callable_info, type, &cache->invoker, function_ptr)) {
+/* PyGIFunctionCache */
+
+static PyObject *
+_function_cache_invoke_real (PyGIFunctionCache *function_cache,
+                             PyGIInvokeState *state,
+                             PyObject *py_args, PyObject *py_kwargs)
+{
+    PyGICallableCache *cache = (PyGICallableCache *)function_cache;
+    PyObject *ret;
+
+    if (!_invoke_marshal_in_args (state, cache, py_args, py_kwargs))
+        return NULL;
+
+    if (!_invoke_callable (state, cache, NULL))
+        return NULL;
+
+    ret = _invoke_marshal_out_args (state, cache);
+    pygi_marshal_cleanup_args_from_py_marshal_success (state, cache);
+
+    if (ret != NULL)
+        pygi_marshal_cleanup_args_to_py_marshal_success (state, cache);
+
+    return ret;
+}
+
+static void
+_function_cache_deinit (PyGIFunctionCache *function_cache)
+{
+    _callable_cache_deinit ((PyGICallableCache *) function_cache);
+
+    g_function_invoker_destroy (&function_cache->invoker);
+}
+
+static gboolean
+_function_cache_init (PyGIFunctionCache *function_cache,
+                      GICallableInfo *callable_info)
+{
+    PyGICallableCache *callable_cache = (PyGICallableCache *) function_cache;
+    GIFunctionInvoker *invoker = &function_cache->invoker;
+    GError *error = NULL;
+
+    callable_cache->direction = PYGI_DIRECTION_FROM_PYTHON;
+
+    if (function_cache->invoke == NULL)
+        function_cache->invoke = _function_cache_invoke_real;
+
+    if (!_callable_cache_init (callable_cache, callable_info))
+        return FALSE;
+
+    /* Set by PyGICCallbackCache and PyGIVFuncCache */
+    if (invoker->native_address == NULL) {
+        if (g_function_info_prep_invoker ((GIFunctionInfo *) callable_info,
+                                          invoker,
+                                          &error)) {
+            return TRUE;
+        }
+    } else {
+        if (g_function_invoker_new_for_address (invoker->native_address,
+                                                (GIFunctionInfo *) callable_info,
+                                                invoker,
+                                                &error)) {
+            return TRUE;
+        }
+    }
+
+    if (!pygi_error_check (&error)) {
+        PyErr_Format (PyExc_RuntimeError,
+                      "unknown error creating invoker for %s",
+                      g_base_info_get_name ((GIBaseInfo *) callable_info));
+    }
+
+    _callable_cache_deinit (callable_cache);
+    return FALSE;
+}
+
+PyGIFunctionCache *
+pygi_function_cache_new (GICallableInfo *info)
+{
+    PyGIFunctionCache *function_cache;
+
+    function_cache = g_slice_new0 (PyGIFunctionCache);
+
+    if (!_function_cache_init (function_cache, info)) {
+        g_slice_free (PyGIFunctionCache, function_cache);
+        return NULL;
+    }
+
+    return function_cache;
+}
+
+void
+pygi_function_cache_free (PyGIFunctionCache *function_cache)
+{
+    if (function_cache == NULL)
+        return;
+
+    _function_cache_deinit (function_cache);
+
+    g_slice_free (PyGIFunctionCache, function_cache);
+}
+
+PyObject *
+pygi_function_cache_invoke (PyGIFunctionCache *function_cache,
+                            PyObject *py_args, PyObject *py_kwargs)
+{
+    PyGICallableCache *cache = (PyGICallableCache *)function_cache;
+    PyGIInvokeState state = { 0, };
+    PyObject *ret = NULL;
+
+    if (!_invoke_state_init_from_callable_cache (NULL, &state, cache,
+                                                 NULL, NULL))
         goto err;
+
+    ret = function_cache->invoke (function_cache, &state,
+                                  py_args, py_kwargs);
+
+err:
+    _invoke_state_clear (&state, cache);
+    return ret;
+}
+
+/* PyGICCallbackCache */
+
+PyGIFunctionCache *
+pygi_ccallback_cache_new (GICallableInfo *info,
+                          GCallback function_ptr)
+{
+    PyGICCallbackCache *ccallback_cache;
+    PyGIFunctionCache *function_cache;
+
+    ccallback_cache = g_slice_new0 (PyGICCallbackCache);
+    function_cache = (PyGIFunctionCache *) ccallback_cache;
+
+    function_cache->invoker.native_address = function_ptr;
+
+    if (!_function_cache_init (function_cache, info)) {
+        g_slice_free (PyGICCallbackCache, ccallback_cache);
+        return NULL;
     }
 
-    return cache;
+    return function_cache;
+}
+
+void
+pygi_ccallback_cache_free (PyGICCallbackCache *ccallback_cache)
+{
+    if (ccallback_cache == NULL)
+        return;
+
+    _function_cache_deinit ((PyGIFunctionCache *) ccallback_cache);
+
+    g_slice_free (PyGICCallbackCache, ccallback_cache);
+}
+
+PyObject *
+pygi_ccallback_cache_invoke (PyGICCallbackCache *ccallback_cache,
+                             PyObject *py_args, PyObject *py_kwargs,
+                             GCallback user_data)
+{
+    PyGICallableCache *cache = (PyGICallableCache *)ccallback_cache;
+    PyGIFunctionCache *function_cache = (PyGIFunctionCache *)ccallback_cache;
+    PyGIInvokeState state = { 0, };
+    PyObject *ret;
+
+    if (!_invoke_state_init_from_callable_cache (NULL, &state, cache,
+                                                 NULL, NULL))
+        goto err;
+
+    state.user_data = user_data;
+
+    ret = function_cache->invoke (function_cache, &state,
+                                  py_args, py_kwargs);
+
 err:
-    pygi_callable_cache_free (cache);
-    return NULL;
+    _invoke_state_clear (&state, cache);
+    return ret;
+}
+
+/* PyGIConstructorCache */
+
+static PyObject *
+_constructor_cache_invoke_real (PyGIFunctionCache *function_cache,
+                                PyGIInvokeState *state,
+                                PyObject *py_args, PyObject *py_kwargs)
+{
+    PyObject *constructor_class;
+    PyObject *ret;
+
+    /* TODO: We don't use the class parameter sent in by  the structure
+     * so we remove it from the py_args tuple but we can keep it
+     * around if we want to call actual gobject constructors
+     * in the future instead of calling g_object_new
+     */
+    constructor_class = PyTuple_GetItem (py_args, 0);
+    if (constructor_class == NULL) {
+        PyErr_Clear ();
+        PyErr_Format (PyExc_TypeError,
+                      "Constructors require the class to be passed in as an argument, "
+                      "No arguments passed to the %s constructor.",
+                      ((PyGICallableCache *)function_cache)->name);
+
+        return FALSE;
+    }
+
+    py_args = PyTuple_GetSlice (py_args, 1, PyTuple_Size (py_args));
+    ret = _function_cache_invoke_real (function_cache, state, py_args, py_kwargs);
+    Py_DECREF (py_args);
+
+    return ret;
+}
+
+PyGIFunctionCache *
+pygi_constructor_cache_new (GICallableInfo *info)
+{
+    PyGIConstructorCache *constructor_cache;
+    PyGIFunctionCache *function_cache;
+
+    constructor_cache = g_slice_new0 (PyGIConstructorCache);
+    function_cache = (PyGIFunctionCache *) constructor_cache;
+
+    function_cache->invoke = _constructor_cache_invoke_real;
+
+    if (!_function_cache_init (function_cache, info)) {
+        g_slice_free (PyGIConstructorCache, constructor_cache);
+        return NULL;
+    }
+
+    return function_cache;
+}
+
+void
+pygi_constructor_cache_free (PyGIConstructorCache *constructor_cache)
+{
+    if (constructor_cache == NULL)
+        return;
+
+    _function_cache_deinit ((PyGIFunctionCache *) constructor_cache);
+
+    g_slice_free (PyGIConstructorCache, constructor_cache);
+}
+
+/* PyGIFunctionWithInstanceCache */
+
+static gboolean
+_function_with_instance_cache_generate_args_cache_real (PyGICallableCache *callable_cache,
+                                                        GICallableInfo *callable_info,
+                                                        gssize arg_index)
+{
+    GIInterfaceInfo *interface_info;
+    PyGIArgCache *instance_cache;
+
+    interface_info = g_base_info_get_container ((GIBaseInfo *) callable_info);
+
+    instance_cache =
+        _arg_cache_new_for_interface (interface_info,
+                                      NULL,
+                                      NULL,
+                                      GI_TRANSFER_NOTHING,
+                                      PYGI_DIRECTION_FROM_PYTHON,
+                                      callable_cache);
+
+    g_base_info_unref ((GIBaseInfo *) interface_info);
+
+    if (instance_cache == NULL)
+        return FALSE;
+
+    /* Because we are not supplied a GITypeInfo for instance arguments,
+     * assume some defaults. */
+    instance_cache->arg_name = "PY_INSTANCE";
+    instance_cache->is_pointer = TRUE;
+    instance_cache->py_arg_index = 0;
+    instance_cache->c_arg_index = 0;
+
+    _pygi_callable_cache_set_arg (callable_cache, arg_index, instance_cache);
+
+    callable_cache->n_py_args++;
+
+    return _callable_cache_generate_args_cache_real (callable_cache,
+                                                     callable_info,
+                                                     arg_index + 1);
+}
+
+static gboolean
+_function_with_instance_cache_init (PyGIFunctionWithInstanceCache *fwi_cache,
+                                    GICallableInfo *info)
+{
+    PyGICallableCache *callable_cache = (PyGICallableCache *)fwi_cache;
+
+    callable_cache->args_offset += 1;
+    callable_cache->generate_args_cache = _function_with_instance_cache_generate_args_cache_real;
+
+    return _function_cache_init ((PyGIFunctionCache *) fwi_cache, info);
+}
+
+/* PyGIMethodCache */
+
+PyGIFunctionCache *
+pygi_method_cache_new (GICallableInfo *info)
+{
+    PyGIMethodCache *method_cache;
+    PyGIFunctionCache *function_cache;
+    PyGIFunctionWithInstanceCache *fwi_cache;
+
+    method_cache = g_slice_new0 (PyGIMethodCache);
+    function_cache = (PyGIFunctionCache *) method_cache;
+    fwi_cache = (PyGIFunctionWithInstanceCache *) method_cache;
+
+    if (!_function_with_instance_cache_init (fwi_cache, info)) {
+        g_slice_free (PyGIMethodCache, method_cache);
+        return NULL;
+    }
+
+    return function_cache;
+}
+
+void
+pygi_method_cache_free (PyGIMethodCache *method_cache)
+{
+    if (method_cache == NULL)
+        return;
+
+    _function_cache_deinit ((PyGIFunctionCache *) method_cache);
+
+    g_slice_free (PyGIMethodCache, method_cache);
+}
+
+/* PyGIVFuncCache */
+
+static PyObject *
+_vfunc_cache_invoke_real (PyGIFunctionCache *function_cache,
+                          PyGIInvokeState *state,
+                          PyObject *py_args, PyObject *py_kwargs)
+{
+    PyGIVFuncCache *vfunc_cache = (PyGIVFuncCache *)function_cache;
+    PyObject *py_gtype;
+    GError *error = NULL;
+    PyObject *ret;
+
+    py_gtype = PyTuple_GetItem (py_args, 0);
+    if (py_gtype == NULL) {
+        PyErr_SetString (PyExc_TypeError,
+                         "need the GType of the implementor class");
+        return FALSE;
+    }
+
+    state->implementor_gtype = pyg_type_from_object (py_gtype);
+    if (state->implementor_gtype == G_TYPE_INVALID)
+        return FALSE;
+
+    /* vfunc addresses are pulled into the state at call time and cannot be
+     * cached because the call site can specify a different portion of the
+     * class hierarchy. e.g. Object.do_func vs. SubObject.do_func might
+     * retrieve a different vfunc address but GI gives us the same vfunc info.
+     */
+    state->function_ptr = g_vfunc_info_get_address ((GIVFuncInfo *) vfunc_cache->info,
+                                                    state->implementor_gtype,
+                                                    &error);
+    if (pygi_error_check (&error)) {
+        return FALSE;
+    }
+
+    py_args = PyTuple_GetSlice (py_args, 1, PyTuple_Size (py_args));
+    ret = _function_cache_invoke_real (function_cache, state, py_args, py_kwargs);
+    Py_DECREF (py_args);
+
+    return ret;
+}
+
+PyGIFunctionCache *
+pygi_vfunc_cache_new (GICallableInfo *info)
+{
+    PyGIVFuncCache *vfunc_cache;
+    PyGIFunctionCache *function_cache;
+    PyGIFunctionWithInstanceCache *fwi_cache;
+
+    vfunc_cache = g_slice_new0 (PyGIVFuncCache);
+    function_cache = (PyGIFunctionCache *) vfunc_cache;
+    fwi_cache = (PyGIFunctionWithInstanceCache *) vfunc_cache;
+
+    /* This will be set in _vfunc_cache_invoke_real() */
+    function_cache->invoker.native_address = (gpointer) 0xdeadbeef;
+
+    function_cache->invoke = _vfunc_cache_invoke_real;
+
+    if (!_function_with_instance_cache_init (fwi_cache, info)) {
+        g_slice_free (PyGIVFuncCache, vfunc_cache);
+        return NULL;
+    }
+
+    /* Required by vfunc_cache_invoke_real() */
+    vfunc_cache->info = g_base_info_ref ((GIBaseInfo *) info);
+
+    return function_cache;
+}
+
+void
+pygi_vfunc_cache_free (PyGIVFuncCache *vfunc_cache)
+{
+    if (vfunc_cache == NULL)
+        return;
+
+    _function_cache_deinit ((PyGIFunctionCache *) vfunc_cache);
+
+    g_base_info_unref (vfunc_cache->info);
+    g_slice_free (PyGIVFuncCache, vfunc_cache);
 }
diff --git a/gi/pygi-cache.h b/gi/pygi-cache.h
index cb8a2e7..569e646 100644
--- a/gi/pygi-cache.h
+++ b/gi/pygi-cache.h
@@ -29,8 +29,16 @@
 
 G_BEGIN_DECLS
 
-typedef struct _PyGICallableCache PyGICallableCache;
 typedef struct _PyGIArgCache PyGIArgCache;
+typedef struct _PyGICallableCache PyGICallableCache;
+typedef struct _PyGIFunctionCache PyGIFunctionCache;
+typedef struct _PyGIVFuncCache PyGIVFuncCache;
+
+typedef PyGIFunctionCache PyGICCallbackCache;
+typedef PyGIFunctionCache PyGIConstructorCache;
+typedef PyGIFunctionCache PyGIFunctionWithInstanceCache;
+typedef PyGIFunctionCache PyGIMethodCache;
+
 
 typedef gboolean (*PyGIMarshalFromPyFunc) (PyGIInvokeState   *state,
                                            PyGICallableCache *callable_cache,
@@ -69,6 +77,7 @@ typedef enum {
     PYGI_META_ARG_TYPE_CLOSURE,
 } PyGIMetaArgType;
 
+/* XXX: REMOVE */
 /*
  * GI determines function types via a combination of flags and info classes.
  * Since for branching purposes they are mutually exclusive, the 
@@ -155,10 +164,14 @@ typedef struct _PyGIInterfaceCache
     gchar *type_name;
 } PyGIInterfaceCache;
 
-struct _PyGICallableCache
+typedef struct _PyGICallableCache
 {
     const gchar *name;
+    PyGIDirection direction;
+
+    GIBaseInfo *info;
 
+    /* XXX: REMOVE */
     PyGIFunctionType function_type;
 
     PyGIArgCache *return_cache;
@@ -171,6 +184,9 @@ struct _PyGICallableCache
     /* Index of user_data arg that can eat variable args passed to a callable. */
     gssize user_data_varargs_index;
 
+    /* Number of args already added */
+    gssize args_offset;
+
     /* Number of out args passed to g_function_info_invoke.
      * This is used for the length of PyGIInvokeState.out_values */
     gssize n_to_py_args;
@@ -188,9 +204,28 @@ struct _PyGICallableCache
      * This count does not include args with defaults. */
     gssize n_py_required_args;
 
+    gboolean (*generate_args_cache) (PyGICallableCache *callable_cache,
+                                     GICallableInfo *callable_info,
+                                     gssize arg_index);
+} PyGICallableCache;
+
+typedef struct _PyGIFunctionCache {
+    PyGICallableCache callable_cache;
+
     /* An invoker with ffi_cif already setup */
     GIFunctionInvoker invoker;
-};
+
+    PyObject *(*invoke) (PyGIFunctionCache *function_cache,
+                         PyGIInvokeState *state,
+                         PyObject *py_args, PyObject *py_kwargs);
+} PyGIFunctionCache;
+
+typedef struct _PyGIVFuncCache {
+    PyGIFunctionWithInstanceCache fwi_cache;
+
+    GIBaseInfo *info;
+} PyGIVFuncCache;
+
 
 gboolean
 pygi_arg_base_setup      (PyGIArgCache *arg_cache,
@@ -237,13 +272,49 @@ pygi_arg_cache_new       (GITypeInfo *type_info,
 void
 pygi_arg_cache_free      (PyGIArgCache *cache);
 
+
+PyGIFunctionCache *
+pygi_function_cache_new (GICallableInfo *info);
+
+void
+pygi_function_cache_free (PyGIFunctionCache *function_cache);
+
+PyObject *
+pygi_function_cache_invoke (PyGIFunctionCache *function_cache,
+                            PyObject *py_args, PyObject *py_kwargs);
+
+PyGIFunctionCache *
+pygi_ccallback_cache_new (GICallableInfo *info,
+                          GCallback function_ptr);
+
+void
+pygi_ccallback_cache_free (PyGICCallbackCache *ccallback_cache);
+
+PyObject *
+pygi_ccallback_cache_invoke (PyGIFunctionCache *function_cache,
+                             PyObject *py_args, PyObject *py_kwargs,
+                             GCallback function_ptr);
+
+PyGIFunctionCache *
+pygi_constructor_cache_new (GICallableInfo *info);
+
+void
+pygi_constructor_cache_free (PyGIConstructorCache *constructor_cache);
+
+
+PyGIFunctionCache *
+pygi_method_cache_new (GICallableInfo *info);
+
+void
+pygi_method_cache_free (PyGIMethodCache *method_cache);
+
+
+PyGIFunctionCache *
+pygi_vfunc_cache_new (GICallableInfo *info);
+
 void
-pygi_callable_cache_free (PyGICallableCache *cache);
+pygi_vfunc_cache_free (PyGIVFuncCache *vfunc_cache);
 
-PyGICallableCache *
-pygi_callable_cache_new  (GICallableInfo *callable_info,
-                          GCallback function_ptr,
-                          gboolean is_ccallback);
 
 #define _pygi_callable_cache_args_len(cache) ((cache)->args_cache)->len
 
diff --git a/gi/pygi-ccallback.c b/gi/pygi-ccallback.c
index 9f63ea0..52bc398 100644
--- a/gi/pygi-ccallback.c
+++ b/gi/pygi-ccallback.c
@@ -32,16 +32,15 @@ _ccallback_call(PyGICCallback *self, PyObject *args, PyObject *kwargs)
     PyObject *result;
 
     if (self->cache == NULL) {
-        self->cache = pygi_callable_cache_new (self->info, self->callback, TRUE);
+        self->cache = pygi_ccallback_cache_new (self->info, self->callback);
         if (self->cache == NULL)
             return NULL;
     }
 
-    result = pygi_callable_info_invoke( (GIBaseInfo *) self->info,
-                                         args,
-                                         kwargs,
-                                         self->cache,
-                                         self->user_data);
+    result = pygi_ccallback_cache_invoke( (PyGICCallbackCache *) self->cache,
+                                          args,
+                                          kwargs,
+                                          self->user_data);
     return result;
 }
 
diff --git a/gi/pygi-closure.c b/gi/pygi-closure.c
index 42692e9..bf8fedc 100644
--- a/gi/pygi-closure.c
+++ b/gi/pygi-closure.c
@@ -927,9 +927,12 @@ pygi_arg_callback_setup_from_info (PyGICallbackCache  *arg_cache,
     }
 
     if (callable_cache != NULL)
+        child_offset = callable_cache->args_offset;
+#if 0
         child_offset =
             (callable_cache->function_type == PYGI_FUNCTION_TYPE_METHOD ||
                  callable_cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) ? 1: 0;
+#endif
 
     ( (PyGIArgCache *)arg_cache)->destroy_notify = (GDestroyNotify)_callback_cache_free_func;
 
diff --git a/gi/pygi-info.c b/gi/pygi-info.c
index 43ee711..18e4a09 100644
--- a/gi/pygi-info.c
+++ b/gi/pygi-info.c
@@ -175,7 +175,29 @@ _base_info_dealloc (PyGIBaseInfo *self)
 
     g_base_info_unref (self->info);
 
-    pygi_callable_cache_free(self->cache);
+    if (self->cache != NULL) {
+        GIInfoType type = g_base_info_get_type (self->info);
+
+        if (type == GI_INFO_TYPE_FUNCTION) {
+            GIFunctionInfoFlags flags;
+
+            flags = g_function_info_get_flags ( (GIFunctionInfo *)self->info);
+
+            if (flags & GI_FUNCTION_IS_CONSTRUCTOR)
+                pygi_constructor_cache_free ( (PyGIConstructorCache *)self->cache);
+            else if (flags & GI_FUNCTION_IS_METHOD)
+                pygi_method_cache_free ( (PyGIMethodCache *)self->cache);
+            else
+                pygi_function_cache_free ( (PyGIFunctionCache *)self->cache);
+        } else if (type == GI_INFO_TYPE_VFUNC) {
+            pygi_vfunc_cache_free ( (PyGIVFuncCache *)self->cache);
+        } else if (type == GI_INFO_TYPE_CALLBACK) {
+            /* TODO */
+            pygi_ccallback_cache_free ( (PyGICCallbackCache *)self->cache);
+        } else {
+            pygi_method_cache_free ( (PyGIMethodCache *)self->cache);
+        }
+    }
 
     Py_TYPE( (PyObject *) self)->tp_free ( (PyObject *) self);
 }
diff --git a/gi/pygi-invoke.c b/gi/pygi-invoke.c
index eebf959..489dcf1 100644
--- a/gi/pygi-invoke.c
+++ b/gi/pygi-invoke.c
@@ -30,11 +30,12 @@ _invoke_callable (PyGIInvokeState *state,
                   PyGICallableCache *cache,
                   GICallableInfo *callable_info)
 {
+    PyGIFunctionCache *function_cache = (PyGIFunctionCache *)cache;
     GIFFIReturnValue ffi_return_value = {0};
 
     Py_BEGIN_ALLOW_THREADS;
 
-        ffi_call (&cache->invoker.cif,
+        ffi_call (&function_cache->invoker.cif,
                   state->function_ptr,
                   (void *)&ffi_return_value,
                   (void **)state->args);
@@ -254,7 +255,8 @@ _invoke_state_init_from_callable_cache (GIBaseInfo *info,
                                         PyObject *py_args,
                                         PyObject *kwargs)
 {
-    PyObject *combined_args = NULL;
+    PyGIFunctionCache *function_cache = (PyGIFunctionCache *)cache;
+
     state->implementor_gtype = 0;
     state->n_args = _pygi_callable_cache_args_len (cache);
 
@@ -262,80 +264,12 @@ _invoke_state_init_from_callable_cache (GIBaseInfo *info,
         state->n_args++;
     }
 
-    /* Copy the function pointer to the state for the normal case. For vfuncs,
-     * this will be filled out based on the implementor_gtype calculated below.
-     */
-    state->function_ptr = cache->invoker.native_address;
-
-    /* TODO: We don't use the class parameter sent in by  the structure
-     * so we remove it from the py_args tuple but we can keep it 
-     * around if we want to call actual gobject constructors
-     * in the future instead of calling g_object_new
+    /* Copy the function pointer to the state for the normal case.
+     * For vfuncs this will have already been set.
      */
-    if (cache->function_type == PYGI_FUNCTION_TYPE_CONSTRUCTOR) {
-        PyObject *constructor_class;
-        constructor_class = PyTuple_GetItem (py_args, 0);
-
-        if (constructor_class == NULL) {
-            PyErr_Clear ();
-            PyErr_Format (PyExc_TypeError,
-                          "Constructors require the class to be passed in as an argument, "
-                          "No arguments passed to the %s constructor.",
-                          cache->name);
-
-            return FALSE;
-        }
-    } else if (cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) {
-        PyObject *py_gtype;
-        GError *error = NULL;
-
-        py_gtype = PyTuple_GetItem (py_args, 0);
-        if (py_gtype == NULL) {
-            PyErr_SetString (PyExc_TypeError,
-                             "need the GType of the implementor class");
-            return FALSE;
-        }
-
-        state->implementor_gtype = pyg_type_from_object (py_gtype);
-
-        if (state->implementor_gtype == 0)
-            return FALSE;
-
-        /* vfunc addresses are pulled into the state at call time and cannot be
-         * cached because the call site can specify a different portion of the
-         * class hierarchy. e.g. Object.do_func vs. SubObject.do_func might
-         * retrieve a different vfunc address but GI gives us the same vfunc info.
-         */
-        state->function_ptr = g_vfunc_info_get_address ((GIVFuncInfo *)info,
-                                                        state->implementor_gtype,
-                                                        &error);
-        if (pygi_error_check (&error)) {
-            return FALSE;
-        }
-    }
-
-    if  (cache->function_type == PYGI_FUNCTION_TYPE_CONSTRUCTOR ||
-            cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) {
-
-        /* we could optimize this by using offsets instead of modifying the tuple but it makes the
-         * code more error prone and confusing so don't do that unless profiling shows
-         * significant gain
-         */
-        combined_args = PyTuple_GetSlice (py_args, 1, PyTuple_Size (py_args));
-    } else {
-        combined_args = py_args;
-        Py_INCREF (combined_args);
-    }
-
-    state->py_in_args = _py_args_combine_and_check_length (cache,
-                                                           combined_args,
-                                                           kwargs);
-    Py_DECREF (combined_args);
-
-    if (state->py_in_args == NULL) {
-        return FALSE;
+    if (state->function_ptr == NULL) {
+        state->function_ptr = function_cache->invoker.native_address;
     }
-    state->n_py_in_args = PyTuple_Size (state->py_in_args);
 
     state->args = g_slice_alloc0 (state->n_args * sizeof (GIArgument *));
     if (state->args == NULL && state->n_args != 0) {
@@ -456,10 +390,21 @@ _caller_alloc (PyGIArgCache *arg_cache, GIArgument *arg)
  *
  */
 static inline gboolean
-_invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache)
+_invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache,
+                         PyObject *py_args, PyObject *kwargs)
 {
     gssize i;
 
+
+    state->py_in_args = _py_args_combine_and_check_length (cache,
+                                                           py_args,
+                                                           kwargs);
+
+    if (state->py_in_args == NULL) {
+        return FALSE;
+    }
+    state->n_py_in_args = PyTuple_Size (state->py_in_args);
+
     if (state->n_py_in_args > cache->n_py_args) {
         PyErr_Format (PyExc_TypeError,
                       "%s() takes exactly %zd argument(s) (%zd given)",
@@ -610,6 +555,7 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache)
 
     if (cache->return_cache) {
         if (!cache->return_cache->is_skipped) {
+#if 1
             if (cache->function_type == PYGI_FUNCTION_TYPE_CONSTRUCTOR) {
                 if (state->return_arg.v_pointer == NULL) {
                     PyErr_SetString (PyExc_TypeError, "constructor returned NULL");
@@ -618,6 +564,7 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache)
                     return NULL;
                 }
             }
+#endif
 
             py_return = cache->return_cache->to_py_marshaller ( state,
                                                                 cache,
@@ -717,15 +664,15 @@ pygi_callable_info_invoke (GIBaseInfo *info, PyObject *py_args,
                            PyObject *kwargs, PyGICallableCache *cache,
                            gpointer user_data)
 {
+    return pygi_function_cache_invoke ((PyGIFunctionCache *)cache, py_args, kwargs);
+
+#if 0
     PyGIInvokeState state = { 0, };
     PyObject *ret = NULL;
 
     if (!_invoke_state_init_from_callable_cache (info, &state, cache, py_args, kwargs))
         goto err;
 
-    if (cache->function_type == PYGI_FUNCTION_TYPE_CCALLBACK)
-        state.user_data = user_data;
-
     if (!_invoke_marshal_in_args (&state, cache))
         goto err;
 
@@ -740,6 +687,7 @@ pygi_callable_info_invoke (GIBaseInfo *info, PyObject *py_args,
 err:
     _invoke_state_clear (&state, cache);
     return ret;
+#endif
 }
 
 PyObject *
@@ -747,10 +695,30 @@ _wrap_g_callable_info_invoke (PyGIBaseInfo *self, PyObject *py_args,
                               PyObject *kwargs)
 {
     if (self->cache == NULL) {
-        self->cache = pygi_callable_cache_new (self->info, NULL, FALSE);
+        GIInfoType type = g_base_info_get_type (self->info);
+
+        if (type == GI_INFO_TYPE_FUNCTION) {
+            GIFunctionInfoFlags flags;
+
+            flags = g_function_info_get_flags ( (GIFunctionInfo *)self->info);
+
+            if (flags & GI_FUNCTION_IS_CONSTRUCTOR)
+                self->cache = pygi_constructor_cache_new (self->info);
+            else if (flags & GI_FUNCTION_IS_METHOD)
+                self->cache = pygi_method_cache_new (self->info);
+            else
+                self->cache = pygi_function_cache_new (self->info);
+        } else if (type == GI_INFO_TYPE_VFUNC) {
+            self->cache = pygi_vfunc_cache_new (self->info);
+        } else if (type == GI_INFO_TYPE_CALLBACK) {
+            g_error ("Cannot invoke callback types");
+        } else {
+            self->cache = pygi_method_cache_new (self->info);
+        }
+
         if (self->cache == NULL)
             return NULL;
     }
 
-    return pygi_callable_info_invoke (self->info, py_args, kwargs, self->cache, NULL);
+    return pygi_function_cache_invoke (self->cache, py_args, kwargs);
 }
diff --git a/gi/pygi.h b/gi/pygi.h
index 2a9ee14..199488b 100644
--- a/gi/pygi.h
+++ b/gi/pygi.h
@@ -41,7 +41,7 @@ typedef struct {
     PyObject_HEAD
     GIBaseInfo *info;
     PyObject *inst_weakreflist;
-    PyGICallableCache *cache;
+    PyGIFunctionCache *cache;
 } PyGIBaseInfo;
 
 typedef struct {
@@ -75,7 +75,7 @@ typedef struct {
     gpointer user_data;
     GIScopeType scope;
     GDestroyNotify destroy_notify_func;
-    PyGICallableCache *cache;
+    PyGIFunctionCache *cache;
 } PyGICCallback;
 
 
