You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucy.apache.org by ma...@apache.org on 2012/11/13 02:50:10 UTC

[lucy-commits] [5/7] git commit: refs/heads/kill_clownfish_host - Move code for generating callbacks.

Move code for generating callbacks.

Move code for generating callbacks from CFCBindMethod to CFCPerlMethod
in anticipation of inlining Perl-specific code and eliminating
Clownfish::Host.


Project: http://git-wip-us.apache.org/repos/asf/lucy/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy/commit/beca21c4
Tree: http://git-wip-us.apache.org/repos/asf/lucy/tree/beca21c4
Diff: http://git-wip-us.apache.org/repos/asf/lucy/diff/beca21c4

Branch: refs/heads/kill_clownfish_host
Commit: beca21c48b51e5e474986faea05dba1ebfc39573
Parents: 9b29e4b
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Fri Nov 9 18:26:35 2012 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Fri Nov 9 18:26:35 2012 -0800

----------------------------------------------------------------------
 clownfish/compiler/perl/lib/Clownfish/CFC.xs |    8 -
 clownfish/compiler/src/CFCBindMethod.c       |  330 ------------------
 clownfish/compiler/src/CFCBindMethod.h       |    7 -
 clownfish/compiler/src/CFCPerl.c             |    2 +-
 clownfish/compiler/src/CFCPerlMethod.c       |  376 +++++++++++++++++++++
 clownfish/compiler/src/CFCPerlMethod.h       |    7 +
 6 files changed, 384 insertions(+), 346 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy/blob/beca21c4/clownfish/compiler/perl/lib/Clownfish/CFC.xs
----------------------------------------------------------------------
diff --git a/clownfish/compiler/perl/lib/Clownfish/CFC.xs b/clownfish/compiler/perl/lib/Clownfish/CFC.xs
index fd7012e..058f3b9 100644
--- a/clownfish/compiler/perl/lib/Clownfish/CFC.xs
+++ b/clownfish/compiler/perl/lib/Clownfish/CFC.xs
@@ -1818,14 +1818,6 @@ CODE:
 OUTPUT: RETVAL
 
 SV*
-callback_def(unused, meth)
-    SV *unused;
-    CFCMethod *meth;
-CODE:
-    RETVAL = S_sv_eat_c_string(CFCBindMeth_callback_def(meth));
-OUTPUT: RETVAL
-
-SV*
 _method_def(meth, klass)
     CFCMethod *meth;
     CFCClass  *klass;

http://git-wip-us.apache.org/repos/asf/lucy/blob/beca21c4/clownfish/compiler/src/CFCBindMethod.c
----------------------------------------------------------------------
diff --git a/clownfish/compiler/src/CFCBindMethod.c b/clownfish/compiler/src/CFCBindMethod.c
index 4e17be1..85ac99e 100644
--- a/clownfish/compiler/src/CFCBindMethod.c
+++ b/clownfish/compiler/src/CFCBindMethod.c
@@ -48,46 +48,6 @@ S_build_unused_vars(CFCVariable **vars);
 static char*
 S_maybe_unreachable(CFCType *return_type);
 
-/* Return a string which maps arguments to various arg wrappers conforming
- * to Host's callback interface.  For instance, (int32_t foo, Obj *bar)
- * produces the following:
- *
- *   CFISH_ARG_I32("foo", foo),
- *   CFISH_ARG_OBJ("bar", bar)
- */
-static char*
-S_callback_params(CFCMethod *method);
-
-/* Adapt the refcounts of parameters and return types, since Host_callback_xxx
- * has no impact on refcounts aside from Host_callback_obj returning an
- * incremented Obj.
- */
-static char*
-S_callback_refcount_mods(CFCMethod *method);
-
-/* Return a function which throws a runtime error indicating which variable
- * couldn't be mapped.  TODO: it would be better to resolve all these cases at
- * compile-time.
- */
-static char*
-S_invalid_callback_def(CFCMethod *method);
-
-// Create a callback for a method which operates in a void context.
-static char*
-S_void_callback_def(CFCMethod *method, const char *callback_params,
-                    const char *refcount_mods);
-
-// Create a callback which returns a primitive type.
-static char*
-S_primitive_callback_def(CFCMethod *method, const char *callback_params,
-                         const char *refcount_mods);
-
-/* Create a callback which returns an object type -- either a generic object or
- * a string. */
-static char*
-S_obj_callback_def(CFCMethod *method, const char *callback_params,
-                   const char *refcount_mods);
-
 char*
 CFCBindMeth_method_def(CFCMethod *method, CFCClass *klass) {
     if (CFCMethod_final(method)) {
@@ -356,293 +316,3 @@ CFCBindMeth_callback_dec(CFCMethod *method) {
     return callback_dec;
 }
 
-char*
-CFCBindMeth_callback_def(CFCMethod *method) {
-    CFCType *return_type = CFCMethod_get_return_type(method);
-    char *params = S_callback_params(method);
-    char *callback_def = NULL;
-    char *refcount_mods = S_callback_refcount_mods(method);
-
-    if (!params) {
-        // Can't map vars, because there's at least one type in the argument
-        // list we don't yet support.  Return a callback wrapper that throws
-        // an error error.
-        callback_def = S_invalid_callback_def(method);
-    }
-    else if (CFCType_is_void(return_type)) {
-        callback_def = S_void_callback_def(method, params, refcount_mods);
-    }
-    else if (CFCType_is_object(return_type)) {
-        callback_def = S_obj_callback_def(method, params, refcount_mods);
-    }
-    else if (CFCType_is_integer(return_type)
-             || CFCType_is_floating(return_type)
-        ) {
-        callback_def = S_primitive_callback_def(method, params, refcount_mods);
-    }
-    else {
-        // Can't map return type.
-        callback_def = S_invalid_callback_def(method);
-    }
-
-    FREEMEM(params);
-    FREEMEM(refcount_mods);
-    return callback_def;
-}
-
-static char*
-S_callback_params(CFCMethod *method) {
-    const char *micro_sym = CFCSymbol_micro_sym((CFCSymbol*)method);
-    CFCParamList *param_list = CFCMethod_get_param_list(method);
-    unsigned num_params = CFCParamList_num_vars(param_list) - 1;
-    size_t needed = strlen(micro_sym) + 30;
-    char *params = (char*)MALLOCATE(needed);
-
-    // TODO: use something other than micro_sym here.
-    sprintf(params, "self, \"%s\", %u", micro_sym, num_params);
-
-    // Iterate over arguments, mapping them to various arg wrappers which
-    // conform to Host's callback interface.
-    CFCVariable **arg_vars = CFCParamList_get_variables(param_list);
-    for (int i = 1; arg_vars[i] != NULL; i++) {
-        CFCVariable *var      = arg_vars[i];
-        const char  *name     = CFCVariable_micro_sym(var);
-        size_t       name_len = strlen(name);
-        CFCType     *type     = CFCVariable_get_type(var);
-        const char  *c_type   = CFCType_to_c(type);
-        size_t       size     = strlen(params)
-                                + strlen(c_type)
-                                + name_len * 2
-                                + 30;
-        char        *new_buf  = (char*)MALLOCATE(size);
-
-        if (CFCType_is_string_type(type)) {
-            sprintf(new_buf, "%s, CFISH_ARG_STR(\"%s\", %s)", params, name, name);
-        }
-        else if (CFCType_is_object(type)) {
-            sprintf(new_buf, "%s, CFISH_ARG_OBJ(\"%s\", %s)", params, name, name);
-        }
-        else if (CFCType_is_integer(type)) {
-            int width = CFCType_get_width(type);
-            if (width) {
-                if (width <= 4) {
-                    sprintf(new_buf, "%s, CFISH_ARG_I32(\"%s\", %s)", params,
-                            name, name);
-                }
-                else {
-                    sprintf(new_buf, "%s, CFISH_ARG_I64(\"%s\", %s)", params,
-                            name, name);
-                }
-            }
-            else {
-                sprintf(new_buf, "%s, CFISH_ARG_I(%s, \"%s\", %s)", params,
-                        c_type, name, name);
-            }
-        }
-        else if (CFCType_is_floating(type)) {
-            sprintf(new_buf, "%s, CFISH_ARG_F64(\"%s\", %s)", params, name, name);
-        }
-        else {
-            // Can't map variable type.  Signal to caller.
-            FREEMEM(params);
-            FREEMEM(new_buf);
-            return NULL;
-        }
-
-        FREEMEM(params);
-        params = new_buf;
-    }
-
-    return params;
-}
-
-static char*
-S_callback_refcount_mods(CFCMethod *method) {
-    char *refcount_mods = CFCUtil_strdup("");
-    CFCType *return_type = CFCMethod_get_return_type(method);
-    CFCParamList *param_list = CFCMethod_get_param_list(method);
-    CFCVariable **arg_vars = CFCParamList_get_variables(param_list);
-
-    // Host_callback_obj returns an incremented object.  If this method does
-    // not return an incremented object, we must cancel out that refcount.
-    // (No function can return a decremented object.)
-    if (CFCType_is_object(return_type) && !CFCType_incremented(return_type)) {
-        refcount_mods = CFCUtil_cat(refcount_mods,
-                                    "\n    CFISH_DECREF(retval);", NULL);
-    }
-
-    // The Host_callback_xxx functions have no effect on the refcounts of
-    // arguments, so we need to adjust them after the fact.
-    for (int i = 0; arg_vars[i] != NULL; i++) {
-        CFCVariable *var  = arg_vars[i];
-        CFCType     *type = CFCVariable_get_type(var);
-        const char  *name = CFCVariable_micro_sym(var);
-        if (!CFCType_is_object(type)) {
-            continue;
-        }
-        else if (CFCType_incremented(type)) {
-            refcount_mods = CFCUtil_cat(refcount_mods, "\n    CFISH_INCREF(",
-                                        name, ");", NULL);
-        }
-        else if (CFCType_decremented(type)) {
-            refcount_mods = CFCUtil_cat(refcount_mods, "\n    CFISH_DECREF(",
-                                        name, ");", NULL);
-        }
-    }
-    
-    return refcount_mods;
-}
-
-static char*
-S_invalid_callback_def(CFCMethod *method) {
-    size_t meth_sym_size = CFCMethod_full_method_sym(method, NULL, NULL, 0);
-    char *full_method_sym = (char*)MALLOCATE(meth_sym_size);
-    CFCMethod_full_method_sym(method, NULL, full_method_sym, meth_sym_size);
-
-    const char *override_sym = CFCMethod_full_override_sym(method);
-    CFCParamList *param_list = CFCMethod_get_param_list(method);
-    const char *params = CFCParamList_to_c(param_list);
-    CFCVariable **param_vars = CFCParamList_get_variables(param_list);
-
-    // Thwart compiler warnings.
-    CFCType *return_type = CFCMethod_get_return_type(method);
-    const char *ret_type_str = CFCType_to_c(return_type);
-    char *unused = S_build_unused_vars(param_vars);
-    char *unreachable = S_maybe_unreachable(return_type);
-
-    char pattern[] =
-        "%s\n"
-        "%s(%s) {%s\n"
-        "    CFISH_THROW(CFISH_ERR, \"Can't override %s via binding\");%s\n"
-        "}\n";
-    size_t size = sizeof(pattern)
-                  + strlen(ret_type_str)
-                  + strlen(override_sym)
-                  + strlen(params)
-                  + strlen(unused)
-                  + strlen(full_method_sym)
-                  + strlen(unreachable)
-                  + 20;
-    char *callback_def = (char*)MALLOCATE(size);
-    sprintf(callback_def, pattern, ret_type_str, override_sym, params, unused,
-            full_method_sym, unreachable);
-
-    FREEMEM(full_method_sym);
-    FREEMEM(unreachable);
-    FREEMEM(unused);
-    return callback_def;
-}
-
-static char*
-S_void_callback_def(CFCMethod *method, const char *callback_params,
-                    const char *refcount_mods) {
-    const char *override_sym = CFCMethod_full_override_sym(method);
-    const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method));
-    const char pattern[] =
-        "void\n"
-        "%s(%s) {\n"
-        "    cfish_Host_callback(%s);%s\n"
-        "}\n";
-    size_t size = sizeof(pattern)
-                  + strlen(override_sym)
-                  + strlen(params)
-                  + strlen(callback_params)
-                  + strlen(refcount_mods)
-                  + 200;
-    char *callback_def = (char*)MALLOCATE(size);
-    sprintf(callback_def, pattern, override_sym, params, callback_params,
-            refcount_mods);
-    return callback_def;
-}
-
-static char*
-S_primitive_callback_def(CFCMethod *method, const char *callback_params,
-                         const char *refcount_mods) {
-    const char *override_sym = CFCMethod_full_override_sym(method);
-    const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method));
-    CFCType *return_type = CFCMethod_get_return_type(method);
-    const char *ret_type_str = CFCType_to_c(return_type);
-    char cb_func_name[40];
-    if (CFCType_is_floating(return_type)) {
-        strcpy(cb_func_name, "cfish_Host_callback_f64");
-    }
-    else if (CFCType_is_integer(return_type)) {
-        strcpy(cb_func_name, "cfish_Host_callback_i64");
-    }
-    else if (strcmp(ret_type_str, "void*") == 0) {
-        strcpy(cb_func_name, "cfish_Host_callback_host");
-    }
-    else {
-        CFCUtil_die("unrecognized type: %s", ret_type_str);
-    }
-
-    char pattern[] =
-        "%s\n"
-        "%s(%s) {\n"
-        "    return (%s)%s(%s);%s\n"
-        "}\n";
-    size_t size = sizeof(pattern)
-                  + strlen(ret_type_str)
-                  + strlen(override_sym)
-                  + strlen(params)
-                  + strlen(ret_type_str)
-                  + strlen(cb_func_name)
-                  + strlen(callback_params)
-                  + strlen(refcount_mods)
-                  + 20;
-    char *callback_def = (char*)MALLOCATE(size);
-    sprintf(callback_def, pattern, ret_type_str, override_sym, params,
-            ret_type_str, cb_func_name, callback_params, refcount_mods);
-
-    return callback_def;
-}
-
-static char*
-S_obj_callback_def(CFCMethod *method, const char *callback_params,
-                   const char *refcount_mods) {
-    const char *override_sym = CFCMethod_full_override_sym(method);
-    const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method));
-    CFCType *return_type = CFCMethod_get_return_type(method);
-    const char *ret_type_str = CFCType_to_c(return_type);
-    const char *cb_func_name = CFCType_is_string_type(return_type)
-                               ? "cfish_Host_callback_str"
-                               : "cfish_Host_callback_obj";
-
-    char *nullable_check = CFCUtil_strdup("");
-    if (!CFCType_nullable(return_type)) {
-        const char *macro_sym = CFCMethod_get_macro_sym(method);
-        char pattern[] =
-            "\n    if (!retval) { CFISH_THROW(CFISH_ERR, "
-            "\"%s() for class '%%o' cannot return NULL\", "
-            "Cfish_Obj_Get_Class_Name((cfish_Obj*)self)); }";
-        size_t size = sizeof(pattern) + strlen(macro_sym) + 30;
-        nullable_check = (char*)REALLOCATE(nullable_check, size);
-        sprintf(nullable_check, pattern, macro_sym);
-    }
-
-    char pattern[] =
-        "%s\n"
-        "%s(%s) {\n"
-        "    %s retval = (%s)%s(%s);%s%s\n"
-        "    return retval;\n"
-        "}\n";
-    size_t size = sizeof(pattern)
-                  + strlen(ret_type_str)
-                  + strlen(override_sym)
-                  + strlen(params)
-                  + strlen(ret_type_str)
-                  + strlen(ret_type_str)
-                  + strlen(cb_func_name)
-                  + strlen(callback_params)
-                  + strlen(nullable_check)
-                  + strlen(refcount_mods)
-                  + 30;
-    char *callback_def = (char*)MALLOCATE(size);
-    sprintf(callback_def, pattern, ret_type_str, override_sym, params,
-            ret_type_str, ret_type_str, cb_func_name, callback_params,
-            nullable_check, refcount_mods);
-
-    FREEMEM(nullable_check);
-    return callback_def;
-}
-

http://git-wip-us.apache.org/repos/asf/lucy/blob/beca21c4/clownfish/compiler/src/CFCBindMethod.h
----------------------------------------------------------------------
diff --git a/clownfish/compiler/src/CFCBindMethod.h b/clownfish/compiler/src/CFCBindMethod.h
index fc15c4f..85945da 100644
--- a/clownfish/compiler/src/CFCBindMethod.h
+++ b/clownfish/compiler/src/CFCBindMethod.h
@@ -62,13 +62,6 @@ CFCBindMeth_abstract_method_def(struct CFCMethod *method);
 char*
 CFCBindMeth_callback_dec(struct CFCMethod *method);
 
-/** Return C code implementing a callback to the Host for this method.  This
- * code is used when a Host method has overridden a method in a Clownfish
- * class.
- */
-char*
-CFCBindMeth_callback_def(struct CFCMethod *method);
-
 #ifdef __cplusplus
 }
 #endif

http://git-wip-us.apache.org/repos/asf/lucy/blob/beca21c4/clownfish/compiler/src/CFCPerl.c
----------------------------------------------------------------------
diff --git a/clownfish/compiler/src/CFCPerl.c b/clownfish/compiler/src/CFCPerl.c
index ca30ad2..7e15e21 100644
--- a/clownfish/compiler/src/CFCPerl.c
+++ b/clownfish/compiler/src/CFCPerl.c
@@ -536,7 +536,7 @@ CFCPerl_write_callbacks(CFCPerl *self) {
 
             // Define callback.
             if (CFCMethod_novel(method) && !CFCMethod_final(method)) {
-                char *cb_def = CFCBindMeth_callback_def(method);
+                char *cb_def = CFCPerlMethod_callback_def(method);
                 content = CFCUtil_cat(content, cb_def, "\n", NULL);
                 FREEMEM(cb_def);
             }

http://git-wip-us.apache.org/repos/asf/lucy/blob/beca21c4/clownfish/compiler/src/CFCPerlMethod.c
----------------------------------------------------------------------
diff --git a/clownfish/compiler/src/CFCPerlMethod.c b/clownfish/compiler/src/CFCPerlMethod.c
index 7c91f0b..aab6429 100644
--- a/clownfish/compiler/src/CFCPerlMethod.c
+++ b/clownfish/compiler/src/CFCPerlMethod.c
@@ -23,6 +23,7 @@
 #include "CFCUtil.h"
 #include "CFCClass.h"
 #include "CFCMethod.h"
+#include "CFCSymbol.h"
 #include "CFCType.h"
 #include "CFCParcel.h"
 #include "CFCParamList.h"
@@ -51,6 +52,60 @@ S_xsub_def_labeled_params(CFCPerlMethod *self);
 static char*
 S_xsub_def_positional_args(CFCPerlMethod *self);
 
+/* Take a NULL-terminated list of CFCVariables and build up a string of
+ * directives like:
+ *
+ *     UNUSED_VAR(var1);
+ *     UNUSED_VAR(var2);
+ */
+static char*
+S_build_unused_vars(CFCVariable **vars);
+
+/* Create an unreachable return statement if necessary, in order to thwart
+ * compiler warnings. */
+static char*
+S_maybe_unreachable(CFCType *return_type);
+
+/* Return a string which maps arguments to various arg wrappers conforming
+ * to Host's callback interface.  For instance, (int32_t foo, Obj *bar)
+ * produces the following:
+ *
+ *   CFISH_ARG_I32("foo", foo),
+ *   CFISH_ARG_OBJ("bar", bar)
+ */
+static char*
+S_callback_params(CFCMethod *method);
+
+/* Adapt the refcounts of parameters and return types, since Host_callback_xxx
+ * has no impact on refcounts aside from Host_callback_obj returning an
+ * incremented Obj.
+ */
+static char*
+S_callback_refcount_mods(CFCMethod *method);
+
+/* Return a function which throws a runtime error indicating which variable
+ * couldn't be mapped.  TODO: it would be better to resolve all these cases at
+ * compile-time.
+ */
+static char*
+S_invalid_callback_def(CFCMethod *method);
+
+// Create a callback for a method which operates in a void context.
+static char*
+S_void_callback_def(CFCMethod *method, const char *callback_params,
+                    const char *refcount_mods);
+
+// Create a callback which returns a primitive type.
+static char*
+S_primitive_callback_def(CFCMethod *method, const char *callback_params,
+                         const char *refcount_mods);
+
+/* Create a callback which returns an object type -- either a generic object or
+ * a string. */
+static char*
+S_obj_callback_def(CFCMethod *method, const char *callback_params,
+                   const char *refcount_mods);
+
 const static CFCMeta CFCPERLMETHOD_META = {
     "Clownfish::CFC::Binding::Perl::Method",
     sizeof(CFCPerlMethod),
@@ -370,3 +425,324 @@ S_xsub_def_positional_args(CFCPerlMethod *self) {
     return xsub;
 }
 
+char*
+CFCPerlMethod_callback_def(CFCMethod *method) {
+    CFCType *return_type = CFCMethod_get_return_type(method);
+    char *params = S_callback_params(method);
+    char *callback_def = NULL;
+    char *refcount_mods = S_callback_refcount_mods(method);
+
+    if (!params) {
+        // Can't map vars, because there's at least one type in the argument
+        // list we don't yet support.  Return a callback wrapper that throws
+        // an error error.
+        callback_def = S_invalid_callback_def(method);
+    }
+    else if (CFCType_is_void(return_type)) {
+        callback_def = S_void_callback_def(method, params, refcount_mods);
+    }
+    else if (CFCType_is_object(return_type)) {
+        callback_def = S_obj_callback_def(method, params, refcount_mods);
+    }
+    else if (CFCType_is_integer(return_type)
+             || CFCType_is_floating(return_type)
+        ) {
+        callback_def = S_primitive_callback_def(method, params, refcount_mods);
+    }
+    else {
+        // Can't map return type.
+        callback_def = S_invalid_callback_def(method);
+    }
+
+    FREEMEM(params);
+    FREEMEM(refcount_mods);
+    return callback_def;
+}
+
+static char*
+S_build_unused_vars(CFCVariable **vars) {
+    char *unused = CFCUtil_strdup("");
+
+    for (int i = 0; vars[i] != NULL; i++) {
+        const char *var_name = CFCVariable_micro_sym(vars[i]);
+        size_t size = strlen(unused) + strlen(var_name) + 80;
+        unused = (char*)REALLOCATE(unused, size);
+        strcat(unused, "\n    CHY_UNUSED_VAR(");
+        strcat(unused, var_name);
+        strcat(unused, ");");
+    }
+
+    return unused;
+}
+
+static char*
+S_maybe_unreachable(CFCType *return_type) {
+    char *return_statement;
+    if (CFCType_is_void(return_type)) {
+        return_statement = CFCUtil_strdup("");
+    }
+    else {
+        const char *ret_type_str = CFCType_to_c(return_type);
+        return_statement = (char*)MALLOCATE(strlen(ret_type_str) + 60);
+        sprintf(return_statement, "\n    CHY_UNREACHABLE_RETURN(%s);",
+                ret_type_str);
+    }
+    return return_statement;
+}
+
+static char*
+S_callback_params(CFCMethod *method) {
+    const char *micro_sym = CFCSymbol_micro_sym((CFCSymbol*)method);
+    CFCParamList *param_list = CFCMethod_get_param_list(method);
+    unsigned num_params = CFCParamList_num_vars(param_list) - 1;
+    size_t needed = strlen(micro_sym) + 30;
+    char *params = (char*)MALLOCATE(needed);
+
+    // TODO: use something other than micro_sym here.
+    sprintf(params, "self, \"%s\", %u", micro_sym, num_params);
+
+    // Iterate over arguments, mapping them to various arg wrappers which
+    // conform to Host's callback interface.
+    CFCVariable **arg_vars = CFCParamList_get_variables(param_list);
+    for (int i = 1; arg_vars[i] != NULL; i++) {
+        CFCVariable *var      = arg_vars[i];
+        const char  *name     = CFCVariable_micro_sym(var);
+        size_t       name_len = strlen(name);
+        CFCType     *type     = CFCVariable_get_type(var);
+        const char  *c_type   = CFCType_to_c(type);
+        size_t       size     = strlen(params)
+                                + strlen(c_type)
+                                + name_len * 2
+                                + 30;
+        char        *new_buf  = (char*)MALLOCATE(size);
+
+        if (CFCType_is_string_type(type)) {
+            sprintf(new_buf, "%s, CFISH_ARG_STR(\"%s\", %s)", params, name, name);
+        }
+        else if (CFCType_is_object(type)) {
+            sprintf(new_buf, "%s, CFISH_ARG_OBJ(\"%s\", %s)", params, name, name);
+        }
+        else if (CFCType_is_integer(type)) {
+            int width = CFCType_get_width(type);
+            if (width) {
+                if (width <= 4) {
+                    sprintf(new_buf, "%s, CFISH_ARG_I32(\"%s\", %s)", params,
+                            name, name);
+                }
+                else {
+                    sprintf(new_buf, "%s, CFISH_ARG_I64(\"%s\", %s)", params,
+                            name, name);
+                }
+            }
+            else {
+                sprintf(new_buf, "%s, CFISH_ARG_I(%s, \"%s\", %s)", params,
+                        c_type, name, name);
+            }
+        }
+        else if (CFCType_is_floating(type)) {
+            sprintf(new_buf, "%s, CFISH_ARG_F64(\"%s\", %s)", params, name, name);
+        }
+        else {
+            // Can't map variable type.  Signal to caller.
+            FREEMEM(params);
+            FREEMEM(new_buf);
+            return NULL;
+        }
+
+        FREEMEM(params);
+        params = new_buf;
+    }
+
+    return params;
+}
+
+static char*
+S_callback_refcount_mods(CFCMethod *method) {
+    char *refcount_mods = CFCUtil_strdup("");
+    CFCType *return_type = CFCMethod_get_return_type(method);
+    CFCParamList *param_list = CFCMethod_get_param_list(method);
+    CFCVariable **arg_vars = CFCParamList_get_variables(param_list);
+
+    // Host_callback_obj returns an incremented object.  If this method does
+    // not return an incremented object, we must cancel out that refcount.
+    // (No function can return a decremented object.)
+    if (CFCType_is_object(return_type) && !CFCType_incremented(return_type)) {
+        refcount_mods = CFCUtil_cat(refcount_mods,
+                                    "\n    CFISH_DECREF(retval);", NULL);
+    }
+
+    // The Host_callback_xxx functions have no effect on the refcounts of
+    // arguments, so we need to adjust them after the fact.
+    for (int i = 0; arg_vars[i] != NULL; i++) {
+        CFCVariable *var  = arg_vars[i];
+        CFCType     *type = CFCVariable_get_type(var);
+        const char  *name = CFCVariable_micro_sym(var);
+        if (!CFCType_is_object(type)) {
+            continue;
+        }
+        else if (CFCType_incremented(type)) {
+            refcount_mods = CFCUtil_cat(refcount_mods, "\n    CFISH_INCREF(",
+                                        name, ");", NULL);
+        }
+        else if (CFCType_decremented(type)) {
+            refcount_mods = CFCUtil_cat(refcount_mods, "\n    CFISH_DECREF(",
+                                        name, ");", NULL);
+        }
+    }
+
+    return refcount_mods;
+}
+
+static char*
+S_invalid_callback_def(CFCMethod *method) {
+    size_t meth_sym_size = CFCMethod_full_method_sym(method, NULL, NULL, 0);
+    char *full_method_sym = (char*)MALLOCATE(meth_sym_size);
+    CFCMethod_full_method_sym(method, NULL, full_method_sym, meth_sym_size);
+
+    const char *override_sym = CFCMethod_full_override_sym(method);
+    CFCParamList *param_list = CFCMethod_get_param_list(method);
+    const char *params = CFCParamList_to_c(param_list);
+    CFCVariable **param_vars = CFCParamList_get_variables(param_list);
+
+    // Thwart compiler warnings.
+    CFCType *return_type = CFCMethod_get_return_type(method);
+    const char *ret_type_str = CFCType_to_c(return_type);
+    char *unused = S_build_unused_vars(param_vars);
+    char *unreachable = S_maybe_unreachable(return_type);
+
+    char pattern[] =
+        "%s\n"
+        "%s(%s) {%s\n"
+        "    CFISH_THROW(CFISH_ERR, \"Can't override %s via binding\");%s\n"
+        "}\n";
+    size_t size = sizeof(pattern)
+                  + strlen(ret_type_str)
+                  + strlen(override_sym)
+                  + strlen(params)
+                  + strlen(unused)
+                  + strlen(full_method_sym)
+                  + strlen(unreachable)
+                  + 20;
+    char *callback_def = (char*)MALLOCATE(size);
+    sprintf(callback_def, pattern, ret_type_str, override_sym, params, unused,
+            full_method_sym, unreachable);
+
+    FREEMEM(full_method_sym);
+    FREEMEM(unreachable);
+    FREEMEM(unused);
+    return callback_def;
+}
+
+static char*
+S_void_callback_def(CFCMethod *method, const char *callback_params,
+                    const char *refcount_mods) {
+    const char *override_sym = CFCMethod_full_override_sym(method);
+    const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method));
+    const char pattern[] =
+        "void\n"
+        "%s(%s) {\n"
+        "    cfish_Host_callback(%s);%s\n"
+        "}\n";
+    size_t size = sizeof(pattern)
+                  + strlen(override_sym)
+                  + strlen(params)
+                  + strlen(callback_params)
+                  + strlen(refcount_mods)
+                  + 200;
+    char *callback_def = (char*)MALLOCATE(size);
+    sprintf(callback_def, pattern, override_sym, params, callback_params,
+            refcount_mods);
+    return callback_def;
+}
+
+static char*
+S_primitive_callback_def(CFCMethod *method, const char *callback_params,
+                         const char *refcount_mods) {
+    const char *override_sym = CFCMethod_full_override_sym(method);
+    const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method));
+    CFCType *return_type = CFCMethod_get_return_type(method);
+    const char *ret_type_str = CFCType_to_c(return_type);
+    char cb_func_name[40];
+    if (CFCType_is_floating(return_type)) {
+        strcpy(cb_func_name, "cfish_Host_callback_f64");
+    }
+    else if (CFCType_is_integer(return_type)) {
+        strcpy(cb_func_name, "cfish_Host_callback_i64");
+    }
+    else if (strcmp(ret_type_str, "void*") == 0) {
+        strcpy(cb_func_name, "cfish_Host_callback_host");
+    }
+    else {
+        CFCUtil_die("unrecognized type: %s", ret_type_str);
+    }
+
+    char pattern[] =
+        "%s\n"
+        "%s(%s) {\n"
+        "    return (%s)%s(%s);%s\n"
+        "}\n";
+    size_t size = sizeof(pattern)
+                  + strlen(ret_type_str)
+                  + strlen(override_sym)
+                  + strlen(params)
+                  + strlen(ret_type_str)
+                  + strlen(cb_func_name)
+                  + strlen(callback_params)
+                  + strlen(refcount_mods)
+                  + 20;
+    char *callback_def = (char*)MALLOCATE(size);
+    sprintf(callback_def, pattern, ret_type_str, override_sym, params,
+            ret_type_str, cb_func_name, callback_params, refcount_mods);
+
+    return callback_def;
+}
+
+static char*
+S_obj_callback_def(CFCMethod *method, const char *callback_params,
+                   const char *refcount_mods) {
+    const char *override_sym = CFCMethod_full_override_sym(method);
+    const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method));
+    CFCType *return_type = CFCMethod_get_return_type(method);
+    const char *ret_type_str = CFCType_to_c(return_type);
+    const char *cb_func_name = CFCType_is_string_type(return_type)
+                               ? "cfish_Host_callback_str"
+                               : "cfish_Host_callback_obj";
+
+    char *nullable_check = CFCUtil_strdup("");
+    if (!CFCType_nullable(return_type)) {
+        const char *macro_sym = CFCMethod_get_macro_sym(method);
+        char pattern[] =
+            "\n    if (!retval) { CFISH_THROW(CFISH_ERR, "
+            "\"%s() for class '%%o' cannot return NULL\", "
+            "Cfish_Obj_Get_Class_Name((cfish_Obj*)self)); }";
+        size_t size = sizeof(pattern) + strlen(macro_sym) + 30;
+        nullable_check = (char*)REALLOCATE(nullable_check, size);
+        sprintf(nullable_check, pattern, macro_sym);
+    }
+
+    char pattern[] =
+        "%s\n"
+        "%s(%s) {\n"
+        "    %s retval = (%s)%s(%s);%s%s\n"
+        "    return retval;\n"
+        "}\n";
+    size_t size = sizeof(pattern)
+                  + strlen(ret_type_str)
+                  + strlen(override_sym)
+                  + strlen(params)
+                  + strlen(ret_type_str)
+                  + strlen(ret_type_str)
+                  + strlen(cb_func_name)
+                  + strlen(callback_params)
+                  + strlen(nullable_check)
+                  + strlen(refcount_mods)
+                  + 30;
+    char *callback_def = (char*)MALLOCATE(size);
+    sprintf(callback_def, pattern, ret_type_str, override_sym, params,
+            ret_type_str, ret_type_str, cb_func_name, callback_params,
+            nullable_check, refcount_mods);
+
+    FREEMEM(nullable_check);
+    return callback_def;
+}
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/beca21c4/clownfish/compiler/src/CFCPerlMethod.h
----------------------------------------------------------------------
diff --git a/clownfish/compiler/src/CFCPerlMethod.h b/clownfish/compiler/src/CFCPerlMethod.h
index 5c4638d..dbd19c0 100644
--- a/clownfish/compiler/src/CFCPerlMethod.h
+++ b/clownfish/compiler/src/CFCPerlMethod.h
@@ -54,6 +54,13 @@ CFCPerlMethod_destroy(CFCPerlMethod *self);
 char*
 CFCPerlMethod_xsub_def(CFCPerlMethod *self);
 
+/** Return C code implementing a callback to Perl for this method.  This code
+ * is run when a Perl subclass has overridden a method in a Clownfish base
+ * class.
+ */
+char*
+CFCPerlMethod_callback_def(struct CFCMethod *method);
+
 #ifdef __cplusplus
 }
 #endif