wip

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

Branch: refs/heads/py_exp1
Commit: 40a5c015de020fb6f846e329b6fe2e82b5632227
Parents: 0f3b369
Author: Marvin Humphrey <[email protected]>
Authored: Sat Dec 6 13:56:40 2014 -0800
Committer: Marvin Humphrey <[email protected]>
Committed: Sat Dec 13 09:46:39 2014 -0800

----------------------------------------------------------------------
 compiler/go/cfc/cfc.go     |  82 +++++++
 compiler/include/CFC.h     |   6 +
 compiler/src/CFCGo.c       | 467 ++++++++++++++++++++++++++++++++++++++++
 compiler/src/CFCGo.h       |  69 ++++++
 compiler/src/CFCGoClass.c  | 349 ++++++++++++++++++++++++++++++
 compiler/src/CFCGoClass.h  | 143 ++++++++++++
 compiler/src/CFCGoFunc.c   | 174 +++++++++++++++
 compiler/src/CFCGoFunc.h   | 104 +++++++++
 compiler/src/CFCGoMethod.c | 243 +++++++++++++++++++++
 compiler/src/CFCGoMethod.h |  77 +++++++
 runtime/go/build.go        |  12 +-
 11 files changed, 1725 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/go/cfc/cfc.go
----------------------------------------------------------------------
diff --git a/compiler/go/cfc/cfc.go b/compiler/go/cfc/cfc.go
index 7c12a6c..0e515e9 100644
--- a/compiler/go/cfc/cfc.go
+++ b/compiler/go/cfc/cfc.go
@@ -67,6 +67,10 @@ func DoStuff() {
        hierarchy.Build()
 }
 
+type Parcel struct {
+       ref *C.CFCParcel
+}
+
 type Hierarchy struct {
        ref *C.CFCHierarchy
 }
@@ -79,6 +83,30 @@ type BindC struct {
        ref *C.CFCC
 }
 
+type BindGo struct {
+       ref *C.CFCGo
+}
+
+type GoClass struct {
+       ref *C.CFCGoClass
+}
+
+func FetchParcel(name string) *Parcel {
+       nameC := C.CString(name)
+       defer C.free(unsafe.Pointer(nameC))
+       parcelC := C.CFCParcel_fetch(nameC)
+       if parcelC == nil {
+               return nil
+       }
+       obj := &Parcel{parcelC}
+       runtime.SetFinalizer(obj, (*Parcel).finalize)
+       return obj
+}
+
+func (obj *Parcel) finalize() {
+       C.CFCBase_decref((*C.CFCBase)(unsafe.Pointer(obj.ref)))
+}
+
 func NewHierarchy(dest string) *Hierarchy {
        destCString := C.CString(dest)
        defer C.free(unsafe.Pointer(destCString))
@@ -159,3 +187,57 @@ func (obj *BindC) WriteCallbacks() {
 func (obj *BindC) WriteHostDefs() {
        C.CFCC_write_hostdefs(obj.ref)
 }
+
+func NewBindGo(hierarchy *Hierarchy) *BindGo {
+       obj := &BindGo{
+               C.CFCGo_new(hierarchy.ref),
+       }
+       runtime.SetFinalizer(obj, (*BindGo).finalize)
+       return obj
+}
+
+func (obj *BindGo) finalize() {
+       C.CFCBase_decref((*C.CFCBase)(unsafe.Pointer(obj.ref)))
+}
+
+func (obj *BindGo) SetHeader(header string) {
+       headerCString := C.CString(header)
+       defer C.free(unsafe.Pointer(headerCString))
+       C.CFCGo_set_header(obj.ref, headerCString)
+}
+
+func (obj *BindGo) SetFooter(header string) {
+       headerCString := C.CString(header)
+       defer C.free(unsafe.Pointer(headerCString))
+       C.CFCGo_set_header(obj.ref, headerCString)
+}
+
+func (obj *BindGo) WriteBindings(parcelName string, dest string) {
+       parcelNameC := C.CString(parcelName)
+       destC := C.CString(dest)
+       defer C.free(unsafe.Pointer(parcelNameC))
+       defer C.free(unsafe.Pointer(destC))
+       C.CFCGo_write_bindings(obj.ref, parcelNameC, destC)
+}
+
+func NewGoClass(parcel *Parcel, className string) *GoClass {
+       var parcelC *C.CFCParcel
+       if parcel != nil {
+               parcelC = parcel.ref
+       }
+       classNameC := C.CString(className)
+       defer C.free(unsafe.Pointer(classNameC))
+       obj := &GoClass{ C.CFCGoClass_new(parcelC, classNameC) }
+       runtime.SetFinalizer(obj, (*GoClass).finalize)
+       return obj
+}
+
+func (obj *GoClass) finalize() {
+       C.CFCBase_decref((*C.CFCBase)(unsafe.Pointer(obj.ref)))
+}
+
+func RegisterGoClass(binding *GoClass) {
+       C.CFCGoClass_register(binding.ref)
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/include/CFC.h
----------------------------------------------------------------------
diff --git a/compiler/include/CFC.h b/compiler/include/CFC.h
index d1eda29..0ef3b09 100644
--- a/compiler/include/CFC.h
+++ b/compiler/include/CFC.h
@@ -44,6 +44,11 @@
 #include "CFCBindFunction.h"
 #include "CFCBindMethod.h"
 
+#include "CFCGo.h"
+#include "CFCGoFunc.h"
+#include "CFCGoMethod.h"
+#include "CFCGoClass.h"
+
 #include "CFCPerl.h"
 #include "CFCPerlSub.h"
 #include "CFCPerlMethod.h"
@@ -52,5 +57,6 @@
 #include "CFCPerlPod.h"
 #include "CFCPerlTypeMap.h"
 
+
 #include "CFCRuby.h"
 

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/src/CFCGo.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGo.c b/compiler/src/CFCGo.c
new file mode 100644
index 0000000..8da8a0f
--- /dev/null
+++ b/compiler/src/CFCGo.c
@@ -0,0 +1,467 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "charmony.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#define CFC_NEED_BASE_STRUCT_DEF
+#include "CFCBase.h"
+#include "CFCGo.h"
+#include "CFCParcel.h"
+#include "CFCClass.h"
+#include "CFCMethod.h"
+#include "CFCHierarchy.h"
+#include "CFCUtil.h"
+#include "CFCGoClass.h"
+#include "CFCGoFunc.h"
+#include "CFCGoMethod.h"
+// #include "CFCGoConstructor.h"
+#include "CFCGoTypeMap.h"
+#include "CFCBindCore.h"
+
+static void
+S_write_callbacks(CFCGo *self, CFCParcel *parcel, const char *h_includes);
+
+struct CFCGo {
+    CFCBase base;
+    CFCHierarchy *hierarchy;
+    char *header;
+    char *footer;
+};
+
+static const CFCMeta CFCGO_META = {
+    "Clownfish::CFC::Binding::Go",
+    sizeof(CFCGo),
+    (CFCBase_destroy_t)CFCGo_destroy
+};
+
+CFCGo*
+CFCGo_new(CFCHierarchy *hierarchy) {
+    CFCGo *self = (CFCGo*)CFCBase_allocate(&CFCGO_META);
+    return CFCGo_init(self, hierarchy);
+}
+
+CFCGo*
+CFCGo_init(CFCGo *self, CFCHierarchy *hierarchy) {
+    CFCUTIL_NULL_CHECK(hierarchy);
+    self->hierarchy  = (CFCHierarchy*)CFCBase_incref((CFCBase*)hierarchy);
+    self->header     = CFCUtil_strdup("");
+    self->footer     = CFCUtil_strdup("");
+    return self;
+}
+
+void
+CFCGo_destroy(CFCGo *self) {
+    CFCBase_decref((CFCBase*)self->hierarchy);
+    FREEMEM(self->header);
+    FREEMEM(self->footer);
+    CFCBase_destroy((CFCBase*)self);
+}
+
+void
+CFCGo_set_header(CFCGo *self, const char *header) {
+    CFCUTIL_NULL_CHECK(header);
+    free(self->header);
+    self->header = CFCUtil_strdup(header);
+}
+
+void
+CFCGo_set_footer(CFCGo *self, const char *footer) {
+    CFCUTIL_NULL_CHECK(footer);
+    free(self->footer);
+    self->footer = CFCUtil_strdup(footer);
+}
+
+void
+S_write_hostdefs(CFCGo *self) {
+    const char pattern[] =
+        "%s\n"
+        "\n"
+        "#ifndef H_CFISH_HOSTDEFS\n"
+        "#define H_CFISH_HOSTDEFS 1\n"
+        "\n"
+        "#define CFISH_OBJ_HEAD \\\n"
+        "    size_t refcount;\n"
+        "\n"
+        "#endif /* H_CFISH_HOSTDEFS */\n"
+        "\n"
+        "%s\n";
+    char *content
+        = CFCUtil_sprintf(pattern, self->header, self->footer);
+
+    // Write if the content has changed.
+    const char *inc_dest = CFCHierarchy_get_include_dest(self->hierarchy);
+    char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "cfish_hostdefs.h",
+                                     inc_dest);
+    CFCUtil_write_if_changed(filepath, content, strlen(content));
+
+    FREEMEM(filepath);
+    FREEMEM(content);
+}
+
+static char*
+S_gen_init(CFCGo *self, CFCParcel *parcel) {
+    const char *prefix = CFCParcel_get_prefix(parcel);
+    const char pattern[] =
+        "func init() {\n"
+        "    C.%sbootstrap_parcel()\n"
+        "    C.%sglue_cgo_symbols()\n"
+        "}\n";
+    return CFCUtil_sprintf(pattern, prefix, prefix);
+}
+
+static void
+S_write_cfbind_go(CFCGo *self, const char *dest, const char *go_short_package,
+                  const char *cgo_comment, const char *init_code,
+                  const char *custom_go, const char *autogen_go) {
+    const char pattern[] =
+        "%s"
+        "\n"
+        "package %s\n"
+        "\n"
+        "/*\n"
+        "%s\n"
+        "*/\n"
+        "import \"C\"\n"
+        "import \"unsafe\"\n"
+        "import \"runtime\"\n"
+        "\n"
+        "%s\n"
+        "\n"
+        "%s\n"
+        "\n"
+        "%s\n"
+        "\n"
+        "//export DummyExport%s\n"
+        "func DummyExport%s() int {\n"
+        "\treturn 1\n"
+        "}\n"
+        "%s";
+    char *content
+        = CFCUtil_sprintf(pattern, self->header, go_short_package,
+                          cgo_comment, init_code, custom_go, autogen_go,
+                          go_short_package, go_short_package,
+                          self->footer);
+
+    char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "cfbind.go", dest);
+    CFCUtil_write_if_changed(filepath, content, strlen(content));
+
+    FREEMEM(filepath);
+    FREEMEM(content);
+}
+
+static char*
+S_gen_glue_cgo_symbols(CFCGo *self, CFCParcel *parcel) {
+    // Generate implementation files containing callback definitions.
+    CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy);
+    char *decs = CFCUtil_strdup("");
+    char *glue = CFCUtil_strdup("");
+    for (size_t i = 0; ordered[i] != NULL; i++) {
+        CFCClass *klass = ordered[i];
+        if (CFCClass_included(klass)
+            || CFCClass_inert(klass)
+            || CFCClass_get_parcel(klass) != parcel
+           ) {
+            continue;
+        }
+
+        CFCMethod **fresh_methods = CFCClass_fresh_methods(klass);
+        for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) {
+            CFCMethod *method = fresh_methods[meth_num];
+
+            if (CFCMethod_novel(method)
+                && !CFCMethod_final(method)
+                && CFCGoMethod_can_be_bound(method)
+               ) {
+                const char *meth_type = CFCMethod_full_typedef(method, NULL);
+                const char *override_sym = CFCMethod_full_override_sym(method);
+                decs = CFCUtil_cat(decs, "extern ", meth_type, " ",
+                                   override_sym, "_wrapper;\n", NULL);
+                glue = CFCUtil_cat(glue, "\n    ", override_sym,
+                                   "_wrapper = ", override_sym, "_internal;",
+                                   NULL);
+            }
+        }
+        FREEMEM(fresh_methods);
+    }
+
+    char pattern[] =
+        "%s"
+        "\n"
+        "static CHY_INLINE void\n"
+        "%sglue_cgo_symbols(void) {%s\n"
+        "}\n"
+        "\n";
+    char *content = CFCUtil_sprintf(pattern, decs,
+                                    CFCParcel_get_prefix(parcel), glue);
+
+    FREEMEM(decs);
+    FREEMEM(glue);
+    FREEMEM(ordered);
+
+    return content;
+}
+
+static void
+S_write_cfbind_c(CFCGo *self, const char *dest, const char *glue_cgo_symbols) {
+    char pattern[] =
+        "%s\n"
+        "\n"
+        "#include \"_cgo_export.h\"\n"
+        "\n"
+        "%s\n"
+        "\n"
+        "%s\n";
+    char *content = CFCUtil_sprintf(pattern, self->header, glue_cgo_symbols,
+                                    self->footer);
+    char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "cfbind.c", dest);
+    CFCUtil_write_if_changed(filepath, content, strlen(content));
+    FREEMEM(content);
+    FREEMEM(filepath);
+}
+
+static char*
+S_gen_callbacks_cgo(CFCGo *self, CFCParcel *parcel) {
+    CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy);
+    char *callbacks = CFCUtil_strdup("");
+
+    for (size_t i = 0; ordered[i] != NULL; i++) {
+        CFCClass *klass = ordered[i];
+        if (CFCClass_included(klass)
+            || CFCClass_inert(klass)
+            || CFCClass_get_parcel(klass) != parcel
+           ) {
+            continue;
+        }
+
+        CFCMethod **fresh_methods = CFCClass_fresh_methods(klass);
+        for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) {
+            CFCMethod *method = fresh_methods[meth_num];
+            if (CFCMethod_novel(method)
+                && !CFCMethod_final(method)
+                && CFCGoMethod_can_be_bound(method)
+               ) {
+                char *cb_cgo = CFCGoMethod_callback_cgo(method);
+                callbacks = CFCUtil_cat(callbacks, cb_cgo, "\n", NULL);
+                FREEMEM(cb_cgo);
+            }
+        }
+        FREEMEM(fresh_methods);
+    }
+
+    FREEMEM(ordered);
+    return callbacks;
+}
+
+
+void
+CFCGo_write_bindings(CFCGo *self, const char *parcel_name, const char *dest) {
+    //CFCGoClass **registry = CFCGoClass_registry();
+    CFCClass   **ordered  = CFCHierarchy_ordered_classes(self->hierarchy);
+    
+    // The Go short package name is the last component of the dot-separated
+    // Clownfish parcel name.
+    const char *parcel_frag = strrchr(parcel_name, '.');
+    if (parcel_frag) {
+        parcel_frag += 1;
+    }
+    else {
+        parcel_frag = parcel_name;
+    }
+    // TODO: Don't downcase package name once caps are forbidden in Clownfish
+    // parcel names.
+    char *go_short_package = CFCUtil_strdup(parcel_frag);
+    for (int i = 0; go_short_package[i] != '\0'; i++) {
+        go_short_package[i] = tolower(go_short_package[i]);
+    }
+
+    CFCParcel *parcel = NULL;
+    CFCParcel **parcels  = CFCParcel_all_parcels();
+    for (size_t i = 0; parcels[i] != NULL; i++) {
+        if (strcmp(CFCParcel_get_name(parcels[0]), parcel_name) == 0) {
+            parcel = parcels[i];
+            break;
+        }
+    }
+    if (!parcel) {
+        CFCUtil_die("Can't bind unknown parcel '%s'", parcel_name);
+    }
+    
+    // Start binding files.
+    char *h_includes  = CFCUtil_strdup("");
+    char *cgo_comment = CFCUtil_strdup("");
+    char *autogen_go  = CFCUtil_strdup("");
+    char *custom_go   = CFCUtil_strdup("");
+
+    // Bake in parcel privacy define, so that binding code can be compiled
+    // without extra compiler flags.
+    {
+        const char *privacy_sym = CFCParcel_get_privacy_sym(parcel);
+        cgo_comment = CFCUtil_cat(cgo_comment, "#define ", privacy_sym,
+                                   "\n\n", NULL);
+    }
+
+    // Pound-includes for generated headers.
+    for (size_t i = 0; ordered[i] != NULL; i++) {
+        CFCClass *klass = ordered[i];
+        const char *include_h = CFCClass_include_h(klass);
+        h_includes = CFCUtil_cat(h_includes, "#include \"", include_h,
+                                   "\"\n", NULL);
+    }
+    cgo_comment = CFCUtil_cat(cgo_comment, h_includes, "\n", NULL);
+
+    // Prep routine for exposing CGO wrappers outside this Go package.
+    char *glue_cgo_symbols = S_gen_glue_cgo_symbols(self, parcel);
+    cgo_comment = CFCUtil_cat(cgo_comment, "void\n",
+                              CFCParcel_get_prefix(parcel),
+                              "glue_cgo_symbols(void);\n\n", NULL);
+
+    // Generate reflect-based wrappers for invoking methods overridden in Go.
+    {
+        char *callbacks_cgo = S_gen_callbacks_cgo(self, parcel);
+        autogen_go = CFCUtil_cat(autogen_go, callbacks_cgo, NULL);
+        FREEMEM(callbacks_cgo);
+    }
+
+    for (size_t i = 0; ordered[i] != NULL; i++) {
+        CFCClass *klass = ordered[i];
+        if (CFCClass_included(klass)
+            || CFCClass_get_parcel(klass) != parcel
+           ) {
+            continue;
+        }
+
+        /*
+        // Constructors.
+        CFCGoConstructor **constructors
+            = CFCGoClass_constructor_bindings(klass);
+        for (size_t j = 0; constructors[j] != NULL; j++) {
+            // Add the function body.
+            char *ctor_def = CFCGoConstructor_func_def(constructors[j]);
+            autogen_go = CFCUtil_cat(autogen_go, ctor_def, "\n", NULL);
+            FREEMEM(ctor_def);
+        }
+        FREEMEM(constructors);
+        */
+
+        // Methods.
+        CFCGoMethod **meth_bindings = CFCGoClass_method_bindings(klass);
+        for (size_t j = 0; meth_bindings[j] != NULL; j++) {
+            CFCGoMethod *meth_binding = meth_bindings[j];
+
+            // Add the method body.
+            char *method_def = CFCGoMethod_func_def(meth_binding);
+            autogen_go = CFCUtil_cat(autogen_go, method_def, "\n", NULL);
+            FREEMEM(method_def);
+        }
+        FREEMEM(meth_bindings);
+    }
+
+    /*
+    // Hand-rolled binding code.
+    for (size_t i = 0; registry[i] != NULL; i++) {
+        CFCGoClass *binding = registry[i];
+        if (CFCClass_get_parcel(CFCGoClass_get_client(binding)) != parcel) {
+            continue;
+        }
+        const char *custom_code = CFCGoClass_get_custom_go(registry[i]);
+        custom_go = CFCUtil_cat(custom_go, custom_code, "\n", NULL);
+    }
+    */
+
+    char *init_code = S_gen_init(self, parcel);
+
+    // Write out if there have been any changes.
+    S_write_cfbind_go(self, dest, go_short_package, cgo_comment, init_code,
+                      custom_go, autogen_go);
+
+    // Write out callbacks, hostdefs.
+    S_write_cfbind_c(self, dest, glue_cgo_symbols);
+    S_write_hostdefs(self);
+    S_write_callbacks(self, parcel, h_includes);
+
+    FREEMEM(glue_cgo_symbols);
+    FREEMEM(go_short_package);
+    FREEMEM(init_code);
+    FREEMEM(h_includes);
+    FREEMEM(cgo_comment);
+    FREEMEM(ordered);
+}
+
+static void
+S_write_callbacks(CFCGo *self, CFCParcel *parcel, const char *h_includes) {
+    // Generate header files declaring callbacks.
+    // TODO: This creates header files for all parcels in source directories,
+    // not just the parcel being processed right this moment.  It should be
+    // harmless, but it would be better to limit generation to one parcel.
+    CFCBindCore *core_binding
+        = CFCBindCore_new(self->hierarchy, self->header, self->footer);
+    CFCBindCore_write_callbacks_h(core_binding);
+    CFCBase_decref((CFCBase*)core_binding);
+
+    // Generate implementation files containing callback definitions.
+    CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy);
+    char *callbacks = CFCUtil_strdup("");
+
+    for (size_t i = 0; ordered[i] != NULL; i++) {
+        CFCClass *klass = ordered[i];
+        if (CFCClass_included(klass)
+            || CFCClass_inert(klass)
+            || CFCClass_get_parcel(klass) != parcel
+           ) {
+            continue;
+        }
+
+        CFCMethod **fresh_methods = CFCClass_fresh_methods(klass);
+        for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) {
+            CFCMethod *method = fresh_methods[meth_num];
+
+            // Define callback.
+            if (CFCMethod_novel(method) && !CFCMethod_final(method)) {
+                char *cb_def = CFCGoMethod_callback_def(method);
+                callbacks = CFCUtil_cat(callbacks, cb_def, "\n", NULL);
+                FREEMEM(cb_def);
+            }
+        }
+        FREEMEM(fresh_methods);
+    }
+
+    static const char pattern[] =
+        "%s\n"
+        "%s\n"
+        "\n"
+        "%s\n"
+        "\n"
+        "%s";
+    char *content = CFCUtil_sprintf(pattern, self->header, h_includes,
+                                    callbacks, self->footer);
+
+    // Write if changed.
+    const char *src_dest = CFCHierarchy_get_source_dest(self->hierarchy);
+    char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "callbacks.c",
+                                     src_dest);
+    CFCUtil_write_if_changed(filepath, content, strlen(content));
+
+    FREEMEM(filepath);
+    FREEMEM(content);
+    FREEMEM(callbacks);
+    FREEMEM(ordered);
+}
+

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/src/CFCGo.h
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGo.h b/compiler/src/CFCGo.h
new file mode 100644
index 0000000..ac81667
--- /dev/null
+++ b/compiler/src/CFCGo.h
@@ -0,0 +1,69 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef H_CFCGO
+#define H_CFCGO
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct CFCGo CFCGo;
+struct CFCParcel;
+struct CFCHierarchy;
+
+/** Clownfish::CFC::Binding::Go - Go bindings for a
+ * Clownfish::CFC::Model::Hierarchy.
+ * 
+ * Clownfish::CFC::Binding::Go presents an interface for auto-generating CGO
+ * code to bind C code for a Clownfish class hierarchy to Go.
+ */
+
+/** 
+ * @param hierarchy A CFCHierarchy.
+ */
+
+CFCGo*
+CFCGo_new(struct CFCHierarchy *hierarchy);
+
+CFCGo*
+CFCGo_init(CFCGo *self, struct CFCHierarchy *hierarchy);
+
+void
+CFCGo_destroy(CFCGo *self);
+
+/** Set the text which will be prepended to generated CGO files -- for
+ * instance, an "autogenerated file" warning.
+ */
+void
+CFCGo_set_header(CFCGo *self, const char *header);
+
+/** Set the text which will be appended to the end of generated CGO files.
+ */
+void
+CFCGo_set_footer(CFCGo *self, const char *footer);
+
+/** Generate CGO bindings for the specified parcel.
+ */
+void
+CFCGo_write_bindings(CFCGo *self, const char *parcel_name, const char *dest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_CFCGO */
+

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/src/CFCGoClass.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoClass.c b/compiler/src/CFCGoClass.c
new file mode 100644
index 0000000..5fe7858
--- /dev/null
+++ b/compiler/src/CFCGoClass.c
@@ -0,0 +1,349 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#define CFC_NEED_BASE_STRUCT_DEF
+#include "CFCBase.h"
+#include "CFCGoClass.h"
+#include "CFCUtil.h"
+#include "CFCClass.h"
+#include "CFCMethod.h"
+#include "CFCParcel.h"
+#include "CFCParamList.h"
+#include "CFCFunction.h"
+#include "CFCSymbol.h"
+#include "CFCVariable.h"
+#include "CFCType.h"
+#include "CFCGoFunc.h"
+#include "CFCGoMethod.h"
+//#include "CFCGoConstructor.h"
+#include "CFCGoTypeMap.h"
+
+struct CFCGoClass {
+    CFCBase base;
+    CFCParcel *parcel;
+    char *class_name;
+    CFCClass *client;
+    char **cons_aliases;
+    char **cons_inits;
+    size_t num_cons;
+    int    exclude_cons;
+};
+
+static CFCGoClass **registry = NULL;
+static size_t registry_size = 0;
+static size_t registry_cap  = 0;
+
+static const CFCMeta CFCGOCLASS_META = {
+    "Clownfish::CFC::Binding::Go::Class",
+    sizeof(CFCGoClass),
+    (CFCBase_destroy_t)CFCGoClass_destroy
+};
+
+CFCGoClass*
+CFCGoClass_new(CFCParcel *parcel, const char *class_name) {
+    CFCGoClass *self = (CFCGoClass*)CFCBase_allocate(&CFCGOCLASS_META);
+    return CFCGoClass_init(self, parcel, class_name);
+}
+
+CFCGoClass*
+CFCGoClass_init(CFCGoClass *self, CFCParcel *parcel,
+                  const char *class_name) {
+    CFCUTIL_NULL_CHECK(parcel);
+    CFCUTIL_NULL_CHECK(class_name);
+    self->parcel = (CFCParcel*)CFCBase_incref((CFCBase*)parcel);
+    self->class_name = CFCUtil_strdup(class_name);
+    // Client may be NULL, since fetch_singleton() does not always succeed.
+    CFCClass *client = CFCClass_fetch_singleton(parcel, class_name);
+    self->client = (CFCClass*)CFCBase_incref((CFCBase*)client);
+    self->cons_aliases      = NULL;
+    self->cons_inits        = NULL;
+    self->num_cons          = 0;
+    self->exclude_cons      = 0;
+    return self;
+}
+
+void
+CFCGoClass_destroy(CFCGoClass *self) {
+    CFCBase_decref((CFCBase*)self->parcel);
+    CFCBase_decref((CFCBase*)self->client);
+    FREEMEM(self->class_name);
+    for (size_t i = 0; i < self->num_cons; i++) {
+        FREEMEM(self->cons_aliases[i]);
+        FREEMEM(self->cons_inits[i]);
+    }
+    FREEMEM(self->cons_aliases);
+    FREEMEM(self->cons_inits);
+    CFCBase_destroy((CFCBase*)self);
+}
+
+static int
+S_compare_cfcgoclass(const void *va, const void *vb) {
+    CFCGoClass *a = *(CFCGoClass**)va;
+    CFCGoClass *b = *(CFCGoClass**)vb;
+    return strcmp(a->class_name, b->class_name);
+}
+
+void
+CFCGoClass_register(CFCGoClass *self) {
+    if (registry_size == registry_cap) {
+        size_t new_cap = registry_cap + 10;
+        registry = (CFCGoClass**)REALLOCATE(registry,
+                                              (new_cap + 1) * 
sizeof(CFCGoClass*));
+        for (size_t i = registry_cap; i <= new_cap; i++) {
+            registry[i] = NULL;
+        }
+        registry_cap = new_cap;
+    }
+    CFCGoClass *existing = CFCGoClass_singleton(self->class_name);
+    if (existing) {
+        CFCUtil_die("Class '%s' already registered", self->class_name);
+    }
+    registry[registry_size] = (CFCGoClass*)CFCBase_incref((CFCBase*)self);
+    registry_size++;
+    qsort(registry, registry_size, sizeof(CFCGoClass*),
+          S_compare_cfcgoclass);
+}
+
+CFCGoClass*
+CFCGoClass_singleton(const char *class_name) {
+    CFCUTIL_NULL_CHECK(class_name);
+    for (size_t i = 0; i < registry_size; i++) {
+        CFCGoClass *existing = registry[i];
+        if (strcmp(class_name, existing->class_name) == 0) {
+            return existing;
+        }
+    }
+    return NULL;
+}
+
+CFCGoClass**
+CFCGoClass_registry() {
+    if (!registry) {
+        registry = (CFCGoClass**)CALLOCATE(1, sizeof(CFCGoClass*));
+    }
+    return registry;
+}
+
+void
+CFCGoClass_clear_registry(void) {
+    for (size_t i = 0; i < registry_size; i++) {
+        CFCBase_decref((CFCBase*)registry[i]);
+    }
+    FREEMEM(registry);
+    registry_size = 0;
+    registry_cap  = 0;
+    registry      = NULL;
+}
+#if 0
+
+void
+CFCGoClass_exclude_method(CFCGoClass *self, const char *meth_name) {
+    if (!self->client) {
+        CFCUtil_die("Can't exclude_method %s -- can't find client for %s",
+                    meth_name, self->class_name);
+    }
+    CFCMethod *method = CFCClass_method(self->client, meth_name);
+    if (!method) {
+        CFCUtil_die("Can't exclude_method %s -- method not found in %s",
+                    meth_name, self->class_name);
+    }
+    if (strcmp(CFCMethod_get_class_name(method), self->class_name) != 0) {
+        CFCUtil_die("Can't exclude_method %s -- method not fresh in %s",
+                    meth_name, self->class_name);
+    }
+    CFCMethod_exclude_from_host(method);
+}
+
+void
+CFCGoClass_bind_constructor(CFCGoClass *self, const char *alias,
+                              const char *initializer) {
+    alias       = alias       ? alias       : "new";
+    initializer = initializer ? initializer : "init";
+    size_t size = (self->num_cons + 1) * sizeof(char*);
+    self->cons_aliases = (char**)REALLOCATE(self->cons_aliases, size);
+    self->cons_inits   = (char**)REALLOCATE(self->cons_inits,   size);
+    self->cons_aliases[self->num_cons] = (char*)CFCUtil_strdup(alias);
+    self->cons_inits[self->num_cons]   = (char*)CFCUtil_strdup(initializer);
+    self->num_cons++;
+    if (!self->client) {
+        CFCUtil_die("Can't bind_constructor %s -- can't find client for %s",
+                    alias, self->class_name);
+    }
+}
+
+void
+CFCGoClass_exclude_constructor(CFCGoClass *self) {
+    self->exclude_cons = 1;
+}
+
+#endif
+CFCGoMethod**
+CFCGoClass_method_bindings(CFCClass *klass) {
+    CFCClass     *parent        = CFCClass_get_parent(klass);
+    size_t        num_bound     = 0;
+    CFCMethod   **fresh_methods = CFCClass_fresh_methods(klass);
+    CFCGoMethod **bound 
+        = (CFCGoMethod**)CALLOCATE(1, sizeof(CFCGoMethod*));
+
+     // Iterate over the class's fresh methods.
+    for (size_t i = 0; fresh_methods[i] != NULL; i++) {
+        CFCMethod *method = fresh_methods[i];
+
+        // Skip methods which have been explicitly excluded.
+        if (CFCMethod_excluded_from_host(method)) {
+            continue;
+        }
+
+        // Skip methods that shouldn't be bound.
+        if (!CFCGoMethod_can_be_bound(method)) {
+            continue;
+        }
+
+        /* Create the binding, add it to the array.
+         */
+        CFCGoMethod *meth_binding = CFCGoMethod_new(method);
+        size_t size = (num_bound + 2) * sizeof(CFCGoMethod*);
+        bound = (CFCGoMethod**)REALLOCATE(bound, size);
+        bound[num_bound] = meth_binding;
+        num_bound++;
+        bound[num_bound] = NULL;
+    }
+
+    FREEMEM(fresh_methods);
+
+    return bound;
+}
+#if 0
+
+static const char NEW[] = "new";
+
+CFCPerlConstructor**
+CFCGoClass_constructor_bindings(CFCClass *klass) {
+    const char    *class_name = CFCClass_get_class_name(klass);
+    CFCGoClass  *perl_class = CFCGoClass_singleton(class_name);
+    CFCFunction  **functions  = CFCClass_functions(klass);
+    size_t         num_bound  = 0;
+    CFCPerlConstructor **bound 
+        = (CFCPerlConstructor**)CALLOCATE(1, sizeof(CFCPerlConstructor*));
+
+    // Iterate over the list of possible initialization functions.
+    for (size_t i = 0; functions[i] != NULL; i++) {
+        CFCFunction  *function  = functions[i];
+        const char   *micro_sym = CFCFunction_micro_sym(function);
+        const char   *alias     = NULL;
+
+        // Find user-specified alias.
+        if (perl_class == NULL) {
+            // Bind init() to new() when possible.
+            if (strcmp(micro_sym, "init") == 0
+                && CFCPerlSub_can_be_bound(function)
+               ) {
+                alias = NEW;
+            }
+        }
+        else {
+            for (size_t j = 0; j < perl_class->num_cons; j++) {
+                if (strcmp(micro_sym, perl_class->cons_inits[j]) == 0) {
+                    alias = perl_class->cons_aliases[j];
+                    if (!CFCPerlSub_can_be_bound(function)) {
+                        CFCUtil_die("Can't bind %s as %s"
+                                    " -- types can't be mapped",
+                                    micro_sym, alias);
+                    }
+                    break;
+                }
+            }
+
+            // Automatically bind init() to new() when possible.
+            if (!alias
+                && !perl_class->exclude_cons
+                && strcmp(micro_sym, "init") == 0
+                && CFCPerlSub_can_be_bound(function)
+               ) {
+                int saw_new = 0;
+                for (size_t j = 0; j < perl_class->num_cons; j++) {
+                    if (strcmp(perl_class->cons_aliases[j], "new") == 0) {
+                        saw_new = 1;
+                    }
+                }
+                if (!saw_new) {
+                    alias = NEW;
+                }
+            }
+        }
+
+        if (!alias) {
+            continue;
+        }
+
+        // Create the binding, add it to the array.
+        CFCPerlConstructor *cons_binding
+            = CFCPerlConstructor_new(klass, alias, micro_sym);
+        size_t size = (num_bound + 2) * sizeof(CFCPerlConstructor*);
+        bound = (CFCPerlConstructor**)REALLOCATE(bound, size);
+        bound[num_bound] = cons_binding;
+        num_bound++;
+        bound[num_bound] = NULL;
+    }
+
+    return bound;
+}
+
+CFCClass*
+CFCGoClass_get_client(CFCGoClass *self) {
+    return self->client;
+}
+
+const char*
+CFCGoClass_get_class_name(CFCGoClass *self) {
+    return self->class_name;
+}
+
+// Generate C code which initializes method metadata.
+char*
+CFCGoClass_method_metadata_code(CFCGoClass *self) {
+    const char *class_var = CFCClass_full_class_var(self->client);
+    CFCMethod **fresh_methods = CFCClass_fresh_methods(self->client);
+    char *code = CFCUtil_strdup("");
+
+    for (int i = 0; fresh_methods[i] != NULL; i++) {
+        CFCMethod *method = fresh_methods[i];
+        if (!CFCMethod_novel(method)) { continue; }
+
+        const char *macro_sym = CFCMethod_get_macro_sym(method);
+        const char *alias     = CFCMethod_get_host_alias(method);
+        if (alias) {
+            code = CFCUtil_cat(code, "    CFISH_Class_Add_Host_Method_Alias(",
+                               class_var, ", \"", alias, "\", \"", macro_sym,
+                               "\");\n", NULL);
+        }
+        if (CFCMethod_excluded_from_host(method)) {
+            code = CFCUtil_cat(code, "    CFISH_Class_Exclude_Host_Method(",
+                               class_var, ", \"", macro_sym, "\");\n", NULL);
+        }
+    }
+
+    FREEMEM(fresh_methods);
+    return code;
+}
+
+
+#endif

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/src/CFCGoClass.h
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoClass.h b/compiler/src/CFCGoClass.h
new file mode 100644
index 0000000..a9fb973
--- /dev/null
+++ b/compiler/src/CFCGoClass.h
@@ -0,0 +1,143 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef H_CFCGOCLASS
+#define H_CFCGOCLASS
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct CFCGoClass CFCGoClass;
+struct CFCParcel;
+struct CFCClass;
+struct CFCFunction;
+struct CFCGoMethod;
+struct CFCGoConstructor;
+
+/** Clownfish::CFC::Binding::Go::Class - Generate Go binding code for a
+ * Clownfish::CFC::Model::Class.
+ */
+
+/** 
+ * @param parcel A Clownfish::CFC::Model::Parcel.
+ * @param class_name The name of the class to be registered.
+ */
+CFCGoClass*
+CFCGoClass_new(struct CFCParcel *parcel, const char *class_name);
+
+CFCGoClass*
+CFCGoClass_init(CFCGoClass *self, struct CFCParcel *parcel,
+                  const char *class_name);
+
+void
+CFCGoClass_destroy(CFCGoClass *self);
+
+/** Add a new class binding to the registry.  Each unique parcel/class-name
+ * combination may only be registered once.
+ */
+void
+CFCGoClass_register(CFCGoClass *self);
+
+/** Given a class name, return a class binding if one exists.
+ */
+CFCGoClass*
+CFCGoClass_singleton(const char *class_name);
+
+/** All registered class bindings.
+ */
+CFCGoClass**
+CFCGoClass_registry();
+
+/** Release all memory and references held by the registry.
+ */
+void
+CFCGoClass_clear_registry(void);
+
+#if 0
+
+/** Specify that a constructor should be made available from Go-space.
+ *
+ * The default alias for the constructor will follow Go conventions:
+ * `NewMyType()` for a `MyType` object.
+ *
+ * @param alias Name to override the default alias for the constructor.
+ * @param initializer The Clownfish name for the initialization function which
+ * will be invoked (default "init").
+ */
+void
+CFCGoClass_bind_constructor(CFCGoClass *self, const char *alias,
+                              const char *initializer);
+
+#endif
+/** Block the automatic generation of a method binding.
+ */
+void
+CFCGoClass_exclude_method(CFCGoClass *self, const char *method);
+#if 0
+
+/** Block the automatic generation of a constructor binding.
+ */
+void
+CFCGoClass_exclude_constructor(CFCGoClass *self);
+#endif
+
+/** Return an array of Clownfish::CFC::Binding::Perl::Method objects
+ * representing all bound methods.
+ */
+struct CFCGoMethod**
+CFCGoClass_method_bindings(struct CFCClass *klass);
+
+#if 0
+/** Return an array of Clownfish::CFC::Binding::Perl::Constructor objects
+ * representing all bound constructors.
+ */
+struct CFCPerlConstructor**
+CFCGoClass_constructor_bindings(struct CFCClass *klass);
+
+/** Accessor for the Clownfish::CFC::Model::Class module to be bound.
+ */
+struct CFCClass*
+CFCGoClass_get_client(CFCGoClass *self);
+
+/** Accessor for class name. 
+ */
+const char*
+CFCGoClass_get_class_name(CFCGoClass *self);
+
+/** Concatenate verbatim XS code.
+ */
+void
+CFCGoClass_append_xs(CFCGoClass *self, const char *xs);
+
+/** Accessor for verbatim XS code.
+ */
+const char*
+CFCGoClass_get_xs_code(CFCGoClass *self);
+
+/** Return C code which initializes method metadata.
+ */
+char*
+CFCGoClass_method_metadata_code(CFCGoClass *self);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+#endif /* H_CFCGOCLASS */
+

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/src/CFCGoFunc.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoFunc.c b/compiler/src/CFCGoFunc.c
new file mode 100644
index 0000000..84fd211
--- /dev/null
+++ b/compiler/src/CFCGoFunc.c
@@ -0,0 +1,174 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <string.h>
+#include <stdio.h>
+#define CFC_NEED_BASE_STRUCT_DEF
+#define CFC_NEED_GOFUNC_STRUCT_DEF
+#include "CFCGoFunc.h"
+#include "CFCGoTypeMap.h"
+#include "CFCBase.h"
+#include "CFCFunction.h"
+#include "CFCUtil.h"
+#include "CFCParcel.h"
+#include "CFCParamList.h"
+#include "CFCVariable.h"
+#include "CFCType.h"
+
+#ifndef true
+    #define true 1
+    #define false 0
+#endif
+
+CFCGoFunc*
+CFCGoFunc_init(CFCGoFunc *self, CFCParcel *parcel,
+               CFCParamList *param_list) {
+    CFCUTIL_NULL_CHECK(param_list);
+    CFCUTIL_NULL_CHECK(parcel);
+    self->param_list  = (CFCParamList*)CFCBase_incref((CFCBase*)param_list);
+    self->parcel      = (CFCParcel*)CFCBase_incref((CFCBase*)parcel);
+    self->go_name     = NULL;
+    return self;
+}
+
+void
+CFCGoFunc_destroy(CFCGoFunc *self) {
+    CFCBase_decref((CFCBase*)self->param_list);
+    CFCBase_decref((CFCBase*)self->parcel);
+    FREEMEM(self->go_name);
+    CFCBase_destroy((CFCBase*)self);
+}
+
+int
+CFCGoFunc_can_be_bound(CFCFunction *function) {
+    // Test whether parameters can be mapped automatically.
+    CFCParamList  *param_list = CFCFunction_get_param_list(function);
+    CFCVariable  **arg_vars   = CFCParamList_get_variables(param_list);
+    for (size_t i = 0; arg_vars[i] != NULL; i++) {
+        CFCType *type = CFCVariable_get_type(arg_vars[i]);
+        if (!CFCType_is_object(type) && !CFCType_is_primitive(type)) {
+            return false;
+        }
+    }
+
+    // Test whether return type can be mapped automatically.
+    CFCType *return_type = CFCFunction_get_return_type(function);
+    if (!CFCType_is_void(return_type)
+        && !CFCType_is_object(return_type)
+        && !CFCType_is_primitive(return_type)
+       ) {
+        return false;
+    }
+
+    return true;
+}
+
+char*
+CFCGoFunc_func_start(const char *name, CFCParamList *param_list,
+                     CFCParcel *parcel, CFCType *return_type, int is_method) {
+    CFCVariable **param_vars = CFCParamList_get_variables(param_list);
+    char *invocant;
+    if (is_method) {
+        CFCVariable *var = param_vars[0];
+        CFCType *type = CFCVariable_get_type(var);
+        char *go_type_name = CFCGoTypeMap_go_type_name(type, parcel);
+        invocant = CFCUtil_sprintf("(%s %s) ", CFCVariable_micro_sym(var),
+                                   go_type_name);
+        FREEMEM(go_type_name);
+    }
+    else {
+        invocant = CFCUtil_strdup("");
+    }
+
+    char *params = CFCUtil_strdup("");
+    int start = is_method ? 1 : 0;
+    for (int i = start; param_vars[i] != NULL; i++) {
+        CFCVariable *var = param_vars[i];
+        CFCType *type = CFCVariable_get_type(var);
+        char *go_type_name = CFCGoTypeMap_go_type_name(type, parcel);
+        if (i > start) {
+            params = CFCUtil_cat(params, ", ", NULL);
+        }
+        params = CFCUtil_cat(params, CFCVariable_micro_sym(var), " ",
+                             go_type_name, NULL);
+        FREEMEM(go_type_name);
+    }
+
+    char *ret_type_str;
+    if (CFCType_is_void(return_type)) {
+        ret_type_str = CFCUtil_strdup("");
+    }
+    else {
+        ret_type_str = CFCGoTypeMap_go_type_name(return_type, parcel);
+        if (ret_type_str == NULL) {
+            CFCUtil_die("Can't convert invalid type in method %s", name);
+        }
+        ret_type_str = CFCUtil_cat(ret_type_str, " ", NULL);
+    }
+
+    char pattern[] = "func %s%s(%s) %s{";
+    char *content = CFCUtil_sprintf(pattern, invocant, name, params,
+                                    ret_type_str);
+
+    FREEMEM(invocant);
+    FREEMEM(params);
+    FREEMEM(ret_type_str);
+
+    return content;
+}
+
+char*
+CFCGoFunc_go_meth_name(const char *orig) {
+    char *go_name = CFCUtil_strdup(orig);
+    for (int i = 0, j = 0, max = strlen(go_name) + 1; i < max; i++) {
+        if (go_name[i] != '_') {
+            go_name[j++] = go_name[i];
+        }
+    }
+    return go_name;
+}
+
+
+CFCParamList*
+CFCGoFunc_get_param_list(CFCGoFunc *self) {
+    return self->param_list;
+}
+
+CFCParcel*
+CFCGoFunc_get_parcel(CFCGoFunc *self) {
+    return self->parcel;
+}
+
+void
+CFCGoFunc_set_go_name(CFCGoFunc *self, const char *go_name) {
+    FREEMEM(self->go_name);
+    self->go_name = CFCUtil_strdup(go_name);
+}
+
+const char*
+CFCGoFunc_get_go_name(CFCGoFunc *self) {
+    if (!self->go_name) {
+        CFCUtil_die("go name not yet set");
+    }
+    return self->go_name;
+}
+
+const char*
+CFCGoFunc_c_name_list(CFCGoFunc *self) {
+    return CFCParamList_name_list(self->param_list);
+}
+

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/src/CFCGoFunc.h
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoFunc.h b/compiler/src/CFCGoFunc.h
new file mode 100644
index 0000000..3d67e75
--- /dev/null
+++ b/compiler/src/CFCGoFunc.h
@@ -0,0 +1,104 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef H_CFCGOFUNC
+#define H_CFCGOFUNC
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct CFCGoFunc CFCGoFunc;
+struct CFCParcel;
+struct CFCFunction;
+struct CFCParamList;
+struct CFCType;
+
+#ifdef CFC_NEED_GOFUNC_STRUCT_DEF
+#define CFC_NEED_BASE_STRUCT_DEF
+#include "CFCBase.h"
+struct CFCGoFunc {
+    CFCBase base;
+    struct CFCParcel *parcel;
+    struct CFCParamList *param_list;
+    char *go_name;
+};
+#endif
+
+/** Clownfish::CFC::Binding::Go::Function
+ * 
+ * This class is used to generate binding code for invoking Clownfish's
+ * inert functions the Go/C barrier.
+ */ 
+
+/** Constructor.
+ * 
+ * @param parcel The Clownfish parcel the function belongs to.
+ * @param param_list A Clownfish::CFC::Model::ParamList.
+ */
+CFCGoFunc*
+CFCGoFunc_init(CFCGoFunc *self, struct CFCParcel *parcel,
+               struct CFCParamList *param_list);
+
+void
+CFCGoFunc_destroy(CFCGoFunc *self);
+
+/** Test whether bindings can be generated for a CFCFunction.
+  */
+int
+CFCGoFunc_can_be_bound(struct CFCFunction *function);
+
+struct CFCParamList*
+CFCGoFunc_get_param_list(CFCGoFunc *self);
+
+struct CFCParcel*
+CFCGoFunc_get_parcel(CFCGoFunc *self);
+
+/**
+ * Set the name of the Go wrapper func.
+ */
+void
+CFCGoFunc_set_go_name(CFCGoFunc *self, const char *go_name);
+
+/**
+ * Return the fully-qualified Go func name.  Throws an error if set_name() has
+ * not yet been called.
+ */
+const char*
+CFCGoFunc_get_go_name(CFCGoFunc *self);
+
+/**
+ * @return a string containing the names of arguments to feed to bound C
+ * function, joined by commas.
+ */
+const char*
+CFCGoFunc_c_name_list(CFCGoFunc *self);
+
+char*
+CFCGoFunc_func_start(const char *name, struct CFCParamList *param_list,
+                     struct CFCParcel *parcel, struct CFCType *return_type,
+                     int is_method);
+
+char*
+CFCGoFunc_go_meth_name(const char *cf_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_CFCGOFUNC */
+

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/src/CFCGoMethod.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoMethod.c b/compiler/src/CFCGoMethod.c
new file mode 100644
index 0000000..76a1a9c
--- /dev/null
+++ b/compiler/src/CFCGoMethod.c
@@ -0,0 +1,243 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#define CFC_NEED_GOFUNC_STRUCT_DEF 1
+#include "CFCGoFunc.h"
+#include "CFCGoMethod.h"
+#include "CFCUtil.h"
+#include "CFCClass.h"
+#include "CFCFunction.h"
+#include "CFCMethod.h"
+#include "CFCSymbol.h"
+#include "CFCType.h"
+#include "CFCParcel.h"
+#include "CFCParamList.h"
+#include "CFCGoTypeMap.h"
+#include "CFCVariable.h"
+
+struct CFCGoMethod {
+    CFCBase     base;
+    CFCMethod  *method;
+};
+
+/* 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);
+
+static const CFCMeta CFCPERLMETHOD_META = {
+    "Clownfish::CFC::Binding::Perl::Method",
+    sizeof(CFCGoMethod),
+    (CFCBase_destroy_t)CFCGoMethod_destroy
+};
+
+CFCGoMethod*
+CFCGoMethod_new(CFCMethod *method) {
+    CFCGoMethod *self
+        = (CFCGoMethod*)CFCBase_allocate(&CFCPERLMETHOD_META);
+    return CFCGoMethod_init(self, method);
+}
+
+CFCGoMethod*
+CFCGoMethod_init(CFCGoMethod *self, CFCMethod *method) {
+    self->method = (CFCMethod*)CFCBase_incref((CFCBase*)method);
+    return self;
+}
+
+void
+CFCGoMethod_destroy(CFCGoMethod *self) {
+    CFCBase_decref((CFCBase*)self->method);
+    CFCBase_destroy((CFCBase*)self);
+}
+
+char*
+CFCGoMethod_func_def(CFCGoMethod *self) {
+    CFCMethod    *method     = self->method;
+    CFCParcel    *parcel     = CFCMethod_get_parcel(method);
+    CFCParamList *param_list = CFCMethod_get_param_list(method);
+    CFCType      *ret_type   = CFCMethod_get_return_type(method);
+    char *name = CFCGoFunc_go_meth_name(CFCMethod_get_macro_sym(method));
+    char *start = CFCGoFunc_func_start(name, param_list, parcel, ret_type, 1);
+    char pattern[] =
+        "%s\n"
+        "}\n";
+    char *content = CFCUtil_sprintf(pattern, start);
+
+    FREEMEM(name);
+    FREEMEM(start);
+    return content;
+}
+
+int
+CFCGoMethod_can_be_bound(CFCMethod *method) {
+    // Check for
+    // - private methods
+    // - methods with types which cannot be mapped automatically
+    return !CFCSymbol_private((CFCSymbol*)method)
+           && CFCGoFunc_can_be_bound((CFCFunction*)method);
+}
+
+char*
+CFCGoMethod_callback_def(CFCMethod *method) {
+    CFCParamList *param_list   = CFCMethod_get_param_list(method);
+    CFCType      *return_type  = CFCMethod_get_return_type(method);
+    char         *meth_typedef = CFCMethod_full_typedef(method, NULL);
+    char         *meth_sym     = CFCMethod_full_method_sym(method, NULL);
+    const char   *ret_type_str = CFCType_to_c(return_type);
+    const char   *override_sym = CFCMethod_full_override_sym(method);
+    const char   *params       = CFCParamList_to_c(param_list);
+    const char   *name_list    = CFCParamList_name_list(param_list);
+    const char   *maybe_return = CFCType_is_void(return_type)
+                                 ? "" : "return ";
+    char *content;
+
+    if (CFCGoMethod_can_be_bound(method)) {
+        const char pattern[] =
+            "%s %s_wrapper;\n"
+            "%s\n"
+            "%s(%s) {\n"
+            "    %s%s_wrapper(%s);\n"
+            "}\n";
+        content = CFCUtil_sprintf(pattern, meth_typedef, override_sym,
+                                  ret_type_str, override_sym, params,
+                                  maybe_return, override_sym, name_list);
+    }
+    else {
+        CFCVariable **vars = CFCParamList_get_variables(param_list);
+        char *unused = S_build_unused_vars(vars);
+        char *unreachable = S_maybe_unreachable(return_type);
+        const char pattern[] =
+            "%s\n"
+            "%s(%s) {%s\n"
+            "    CFISH_THROW(CFISH_ERR, \"Can't override %s via 
binding\");%s\n"
+            "}\n";
+        content = CFCUtil_sprintf(pattern, ret_type_str, override_sym,
+                                  params, unused, meth_sym, unreachable);
+        FREEMEM(unused);
+        FREEMEM(unreachable);
+    }
+
+    FREEMEM(meth_sym);
+    FREEMEM(meth_typedef);
+    return content;
+}
+
+char*
+CFCGoMethod_callback_cgo(CFCMethod *method) {
+    const char   *override_sym = CFCMethod_full_override_sym(method);
+    CFCParcel    *parcel       = CFCMethod_get_parcel(method);
+    CFCParamList *param_list   = CFCMethod_get_param_list(method);
+    CFCVariable **param_vars   = CFCParamList_get_variables(param_list);
+    CFCType      *self_type    = CFCMethod_self_type(method);
+    CFCType      *return_type  = CFCMethod_get_return_type(method);
+    char *meth_body = "";
+
+    char *self_type_str = CFCGoTypeMap_cgo_type_name(self_type);
+    if (self_type_str == NULL) {
+        CFCUtil_die("Can't convert invalid type in method %s",
+                    CFCMethod_full_method_sym(method, NULL));
+    }
+
+    char *ret_type_str;
+    if (CFCType_is_void(return_type)) {
+        ret_type_str = CFCUtil_strdup("");
+    }
+    else {
+        ret_type_str = CFCGoTypeMap_cgo_type_name(return_type);
+        if (ret_type_str == NULL) {
+            CFCUtil_die("Can't convert invalid type in method %s",
+                        CFCMethod_full_method_sym(method, NULL));
+        }
+        ret_type_str = CFCUtil_cat(ret_type_str, " ", NULL);
+    }
+
+    char *params = CFCUtil_strdup("");
+    for (int i = 1; param_vars[i] != NULL; i++) {
+        CFCVariable *var = param_vars[i];
+        CFCType *type = CFCVariable_get_type(var);
+        char *cgo_type_name = CFCGoTypeMap_cgo_type_name(type);
+        if (cgo_type_name == NULL) {
+            CFCUtil_die("Can't convert invalid type in method %s",
+                        CFCMethod_full_method_sym(method, NULL));
+        }
+        if (i > 1) {
+            params = CFCUtil_cat(params, ", ", NULL);
+        }
+        params = CFCUtil_cat(params, CFCVariable_micro_sym(var), " ",
+                             cgo_type_name, NULL);
+        FREEMEM(cgo_type_name);
+    }
+
+    char pattern[] =
+        "//export %s_internal\n"
+        "func (self %s) %s_internal(%s) %s{\n"
+        "%s"
+        "}\n";
+    char *content = CFCUtil_sprintf(pattern, override_sym, self_type_str,
+                                    override_sym, params, ret_type_str,
+                                    meth_body);
+
+    FREEMEM(self_type_str);
+    FREEMEM(ret_type_str);
+    FREEMEM(params);
+    return content;
+}
+
+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    CFISH_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);
+        char pattern[] = "\n    CFISH_UNREACHABLE_RETURN(%s);";
+        return_statement = CFCUtil_sprintf(pattern, ret_type_str);
+    }
+    return return_statement;
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/src/CFCGoMethod.h
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoMethod.h b/compiler/src/CFCGoMethod.h
new file mode 100644
index 0000000..b1f079b
--- /dev/null
+++ b/compiler/src/CFCGoMethod.h
@@ -0,0 +1,77 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef H_CFCGOMETHOD
+#define H_CFCGOMETHOD
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Clownfish::CFC::Binding::Go::Method - Binding for an object method.
+ *
+ * 
+ * This class isa Clownfish::CFC::Binding::Go::Subroutine -- see its
+ * documentation for various code-generating routines.
+ * 
+ * Method bindings use labeled parameters if the C function takes more than one
+ * argument (other than "self").  If there is only one argument, the binding
+ * will be set up to accept a single positional argument.
+ */
+typedef struct CFCGoMethod CFCGoMethod;
+struct CFCMethod;
+
+CFCGoMethod*
+CFCGoMethod_new(struct CFCMethod *method);
+
+/**
+ * @param method A Clownfish::CFC::Model::Method.
+ */
+CFCGoMethod*
+CFCGoMethod_init(CFCGoMethod *self, struct CFCMethod *method);
+
+void
+CFCGoMethod_destroy(CFCGoMethod *self);
+
+/** Return code for invoking the method from Go-space.
+ */
+char*
+CFCGoMethod_func_def(CFCGoMethod *self);
+
+/** Test whether bindings should be generated for a method.
+  */
+int
+CFCGoMethod_can_be_bound(struct CFCMethod *method);
+
+/** Return CGO code which uses the go `reflect` package to dispatch to a
+ * method overridden in Go.
+ */
+char*
+CFCGoMethod_callback_cgo(struct CFCMethod *method);
+
+/** Return C code which knows how to call back into Go for this method.  This
+ * code is run when a Go subclass has overridden a method in a Clownfish base
+ * class.
+ */
+char*
+CFCGoMethod_callback_def(struct CFCMethod *method);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_CFCGOMETHOD */
+

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/runtime/go/build.go
----------------------------------------------------------------------
diff --git a/runtime/go/build.go b/runtime/go/build.go
index a82cc36..26d7bb4 100644
--- a/runtime/go/build.go
+++ b/runtime/go/build.go
@@ -121,13 +121,23 @@ func runCFC() {
        coreBinding := cfc.NewBindCore(hierarchy, autogenHeader, "")
        modified := coreBinding.WriteAllModified(false)
        if modified {
+               bindClasses()
                cBinding := cfc.NewBindC(hierarchy, autogenHeader, "")
                cBinding.WriteCallbacks()
-               cBinding.WriteHostDefs()
+               goBinding := cfc.NewBindGo(hierarchy)
+               goBinding.SetHeader(autogenHeader)
+               packageDir := path.Join(buildDir, "clownfish")
+               goBinding.WriteBindings("Clownfish", packageDir)
                hierarchy.WriteLog()
        }
 }
 
+func bindClasses() {
+       parcel := cfc.FetchParcel("Clownfish")
+       hashBinding := cfc.NewGoClass(parcel, "Clownfish::Hash")
+       cfc.RegisterGoClass(hashBinding)
+}
+
 func prep() {
        configure()
        runCFC()

Reply via email to